From 253c141f7320b02eb5169c09a16ae1f7931f239e Mon Sep 17 00:00:00 2001 From: James Telfer <792299+jamestelfer@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:32:46 +1000 Subject: [PATCH] test: add for Buildkite API interaction (#3) --- internal/buildkite/pipeline.go | 9 +- internal/buildkite/pipeline_test.go | 141 ++++++++++++++++++++++++++++ internal/config/config.go | 3 +- 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 internal/buildkite/pipeline_test.go diff --git a/internal/buildkite/pipeline.go b/internal/buildkite/pipeline.go index 5103a02..c735390 100644 --- a/internal/buildkite/pipeline.go +++ b/internal/buildkite/pipeline.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "net/url" "github.com/buildkite/go-buildkite/v3/buildkite" "github.com/jamestelfer/ghauth/internal/config" @@ -21,6 +22,12 @@ func New(cfg config.BuildkiteConfig) PipelineLookup { client := buildkite.NewClient(transport.Client()) + if cfg.ApiURL != "" { + url, _ := url.Parse(cfg.ApiURL) + transport.APIHost = url.Host + client.BaseURL, _ = url.Parse(cfg.ApiURL) + } + return PipelineLookup{ client, } @@ -35,7 +42,7 @@ func (p PipelineLookup) RepositoryLookup(ctx context.Context, organizationSlug, repo := pipeline.Repository if repo == nil { - return "", fmt.Errorf("no configured repository for pipeline %s/%s", organizationSlug, pipelineSlug) + return "", fmt.Errorf("no configured repository for pipeline %s/%s", organizationSlug, pipelineSlug) } return *repo, nil diff --git a/internal/buildkite/pipeline_test.go b/internal/buildkite/pipeline_test.go new file mode 100644 index 0000000..2cea178 --- /dev/null +++ b/internal/buildkite/pipeline_test.go @@ -0,0 +1,141 @@ +package buildkite_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + api "github.com/buildkite/go-buildkite/v3/buildkite" + "github.com/jamestelfer/ghauth/internal/buildkite" + "github.com/jamestelfer/ghauth/internal/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepositoryLookup_Succeeds(t *testing.T) { + router := http.NewServeMux() + + router.HandleFunc("/v2/organizations/{organization}/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) { + org := r.PathValue("organization") + pipeline := r.PathValue("pipeline") + + w.Header().Set("Content-Type", "application/json") + pl := &api.Pipeline{ + Name: &pipeline, + Slug: &pipeline, + Repository: api.String("urn:expected-repository-url"), + Description: &org, + Tags: []string{ + "token:" + r.Header.Get("Authorization"), + }, + } + res, _ := json.Marshal(&pl) + _, _ = w.Write(res) + }) + + svr := httptest.NewServer(router) + defer svr.Close() + + bk := buildkite.New(config.BuildkiteConfig{ + Token: "expected-token", + ApiURL: svr.URL, + }) + + repo, err := bk.RepositoryLookup(context.Background(), "expected-organization", "expected-pipeline") + + require.NoError(t, err) + assert.Equal(t, "urn:expected-repository-url", repo) +} + +func TestRepositoryLookup_SendsAuthToken(t *testing.T) { + router := http.NewServeMux() + + var actualToken string + + router.HandleFunc("/v2/organizations/{organization}/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) { + org := r.PathValue("organization") + pipeline := r.PathValue("pipeline") + + // capture the token to assert against + actualToken = r.Header.Get("Authorization") + + w.Header().Set("Content-Type", "application/json") + pl := &api.Pipeline{ + Name: &pipeline, + Slug: &pipeline, + Repository: api.String("urn:expected-repository-url"), + Description: &org, + Tags: []string{ + "token:" + r.Header.Get("Authorization"), + }, + } + res, _ := json.Marshal(&pl) + _, _ = w.Write(res) + }) + + svr := httptest.NewServer(router) + defer svr.Close() + + bk := buildkite.New(config.BuildkiteConfig{ + Token: "expected-token", + ApiURL: svr.URL, + }) + + _, err := bk.RepositoryLookup(context.Background(), "expected-organization", "expected-pipeline") + + require.NoError(t, err) + assert.Equal(t, "Bearer expected-token", actualToken) +} + +func TestRepositoryLookup_FailsWhenRepoNotConfigured(t *testing.T) { + router := http.NewServeMux() + router.HandleFunc("/v2/organizations/{organization}/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) { + org := r.PathValue("organization") + pipeline := r.PathValue("pipeline") + w.Header().Set("Content-Type", "application/json") + pl := &api.Pipeline{ + Name: &pipeline, + Slug: &pipeline, + //Repository: // repository purposefully blank + Description: &org, + } + res, _ := json.Marshal(&pl) + _, _ = w.Write(res) + }) + + svr := httptest.NewServer(router) + defer svr.Close() + + bk := buildkite.New(config.BuildkiteConfig{ + Token: "expected-token", + ApiURL: svr.URL, + }) + + _, err := bk.RepositoryLookup(context.Background(), "expected-organization", "expected-pipeline") + + require.Error(t, err) + assert.ErrorContains(t, err, "no configured repository for pipeline expected-organization/expected-pipeline") +} + +func TestRepositoryLookup_Fails(t *testing.T) { + router := http.NewServeMux() + router.HandleFunc("/v2/organizations/{organization}/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) { + // teapot is useful for test + w.WriteHeader(http.StatusTeapot) + }) + + svr := httptest.NewServer(router) + defer svr.Close() + + bk := buildkite.New(config.BuildkiteConfig{ + Token: "expected-token", + ApiURL: svr.URL, + }) + + _, err := bk.RepositoryLookup(context.Background(), "expected-organization", "expected-pipeline") + + require.Error(t, err) + assert.ErrorContains(t, err, ": 418") +} diff --git a/internal/config/config.go b/internal/config/config.go index eb4233c..bae2e44 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -26,7 +26,8 @@ type AuthorizationConfig struct { } type BuildkiteConfig struct { - Token string `env:"BUILDKITE_API_TOKEN, required"` + Token string `env:"BUILDKITE_API_TOKEN, required"` + ApiURL string // internal only } type GithubConfig struct {