package artifact import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) type capturedRequest struct { path string auth string body []byte } func newTestServer(t *testing.T, status int, contentType, response string) (*httptest.Server, *capturedRequest) { t.Helper() got := &capturedRequest{} server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got.path = r.URL.Path got.auth = r.Header.Get("Authorization") got.body, _ = io.ReadAll(r.Body) w.Header().Set("content-type", contentType) w.WriteHeader(status) _, _ = io.WriteString(w, response) })) t.Cleanup(server.Close) return server, got } func TestClient_CreateArtifact(t *testing.T) { t.Parallel() server, got := newTestServer(t, http.StatusOK, "application/json", `{"ok":true,"signedUploadUrl":"https://example.test/upload"}`) c := NewClient(server.URL, "secret") res, err := c.CreateArtifact(context.Background(), CreateArtifactRequest{RunID: "run", JobRunID: "job", Name: "foo", Version: 4}) if err != nil { t.Fatal(err) } if !res.Ok || res.SignedUploadUrl != "https://example.test/upload" { t.Errorf("unexpected response: %+v", res) } if got, want := got.path, "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact"; got != want { t.Errorf("expected path %q to be %q", got, want) } if got, want := got.auth, "Bearer secret"; got != want { t.Errorf("expected Authorization %q to be %q", got, want) } var req CreateArtifactRequest if err := json.Unmarshal(got.body, &req); err != nil { t.Fatal(err) } if req != (CreateArtifactRequest{RunID: "run", JobRunID: "job", Name: "foo", Version: 4}) { t.Errorf("unexpected request: %+v", req) } } func TestClient_FinalizeArtifact(t *testing.T) { t.Parallel() server, got := newTestServer(t, http.StatusOK, "application/json", `{"ok":true,"artifactId":"42"}`) c := NewClient(server.URL, "secret") res, err := c.FinalizeArtifact(context.Background(), FinalizeArtifactRequest{RunID: "run", JobRunID: "job", Name: "foo", Size: 10, Hash: "abc"}) if err != nil { t.Fatal(err) } if !res.Ok || res.ArtifactId != "42" { t.Errorf("unexpected response: %+v", res) } if got, want := got.path, "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact"; got != want { t.Errorf("expected path %q to be %q", got, want) } var req FinalizeArtifactRequest if err := json.Unmarshal(got.body, &req); err != nil { t.Fatal(err) } if req.Size != 10 || req.Hash != "abc" { t.Errorf("unexpected request: %+v", req) } } func TestClient_ListArtifacts(t *testing.T) { t.Parallel() server, got := newTestServer(t, http.StatusOK, "application/json", `{"artifacts":[{"databaseId":"7","name":"foo"}]}`) c := NewClient(server.URL, "secret") res, err := c.ListArtifacts(context.Background(), ListArtifactsRequest{NameFilter: "foo"}) if err != nil { t.Fatal(err) } if len(res.Artifacts) != 1 || res.Artifacts[0].Id != "7" || res.Artifacts[0].Name != "foo" { t.Errorf("unexpected response: %+v", res) } if got, want := got.path, "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts"; got != want { t.Errorf("expected path %q to be %q", got, want) } } func TestClient_GetSignedArtifactURL(t *testing.T) { t.Parallel() server, got := newTestServer(t, http.StatusOK, "application/json", `{"signedUrl":"https://example.test/download"}`) c := NewClient(server.URL, "secret") res, err := c.GetSignedArtifactURL(context.Background(), GetSignedArtifactURLRequest{RunID: "run", JobRunID: "job", Name: "foo"}) if err != nil { t.Fatal(err) } if res.SignedUrl != "https://example.test/download" { t.Errorf("unexpected response: %+v", res) } if got, want := got.path, "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL"; got != want { t.Errorf("expected path %q to be %q", got, want) } } func TestClient_DeleteArtifact(t *testing.T) { t.Parallel() server, got := newTestServer(t, http.StatusOK, "application/json", `{"ok":true,"artifactId":"7"}`) c := NewClient(server.URL, "secret") res, err := c.DeleteArtifact(context.Background(), DeleteArtifactRequest{RunID: "run", JobRunID: "job", Name: "foo"}) if err != nil { t.Fatal(err) } if !res.Ok || res.ArtifactId != "7" { t.Errorf("unexpected response: %+v", res) } if got, want := got.path, "/twirp/github.actions.results.api.v1.ArtifactService/DeleteArtifact"; got != want { t.Errorf("expected path %q to be %q", got, want) } } func TestClient_twirp(t *testing.T) { t.Parallel() cases := []struct { name string status int contentType string body string wantErr string }{ {"bad content type", http.StatusOK, "text/plain", "nope", "content-type"}, {"server error", http.StatusInternalServerError, "application/json", `{"message":"boom"}`, "twirp call"}, {"bad json", http.StatusOK, "application/json", "{", "unexpected EOF"}, } for _, tc := range cases { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() server, _ := newTestServer(t, tc.status, tc.contentType, tc.body) c := NewClient(server.URL, "secret") _, err := c.CreateArtifact(context.Background(), CreateArtifactRequest{Name: "foo"}) if err == nil || !strings.Contains(err.Error(), tc.wantErr) { t.Errorf("expected error containing %q, got %v", tc.wantErr, err) } }) } }