All checks were successful
default / ensure tests work (push) Successful in 29s
114 lines
3.2 KiB
Go
114 lines
3.2 KiB
Go
package artifact
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestE2E_ArtifactRoundTrip exercises the real results service. It only runs
|
|
// inside a CI pipeline (GITHUB_ACTIONS=true) and is skipped locally so that
|
|
// `go test ./...` stays hermetic. Its purpose is to detect drift between the
|
|
// results service the runner talks to and the API this client expects.
|
|
func TestE2E_ArtifactRoundTrip(t *testing.T) {
|
|
if os.Getenv("GITHUB_ACTIONS") != "true" {
|
|
t.Skip("e2e test only runs in CI")
|
|
}
|
|
|
|
resultsURL := os.Getenv("ACTIONS_RESULTS_URL")
|
|
runtimeToken := os.Getenv("ACTIONS_RUNTIME_TOKEN")
|
|
if resultsURL == "" || runtimeToken == "" {
|
|
t.Skip("ACTIONS_RESULTS_URL and ACTIONS_RUNTIME_TOKEN are required")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
defer cancel()
|
|
|
|
run, job, err := JobInfo()
|
|
if err != nil {
|
|
t.Fatalf("JobInfo: %v", err)
|
|
}
|
|
|
|
client := NewClient(resultsURL, runtimeToken)
|
|
name := fmt.Sprintf("e2e-%d-%d", os.Getpid(), time.Now().UnixNano())
|
|
|
|
expire := time.Now().Add(1 * time.Hour)
|
|
create, err := client.CreateArtifact(ctx, CreateArtifactRequest{
|
|
RunID: run,
|
|
JobRunID: job,
|
|
Name: name,
|
|
Version: 4,
|
|
ExpiresAt: &expire,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateArtifact: %v", err)
|
|
}
|
|
if !create.Ok {
|
|
t.Fatalf("CreateArtifact not ok: %+v", create)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
if _, err := client.DeleteArtifact(context.Background(), DeleteArtifactRequest{RunID: run, JobRunID: job, Name: name}); err != nil {
|
|
t.Logf("cleanup DeleteArtifact: %v", err)
|
|
}
|
|
})
|
|
|
|
content := []byte("artifact e2e " + name)
|
|
pushed, err := PushBlob(ctx, nil, bytes.NewReader(content), create.SignedUploadUrl, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("PushBlob: %v", err)
|
|
}
|
|
|
|
finalize, err := client.FinalizeArtifact(ctx, FinalizeArtifactRequest{
|
|
RunID: run,
|
|
JobRunID: job,
|
|
Name: name,
|
|
Size: pushed.Size,
|
|
Hash: pushed.SHA256Sum,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("FinalizeArtifact: %v", err)
|
|
}
|
|
if !finalize.Ok {
|
|
t.Fatalf("FinalizeArtifact not ok: %+v", finalize)
|
|
}
|
|
|
|
signed := getSignedArtifactURL(t, ctx, client, run, job, name)
|
|
|
|
var buf bytes.Buffer
|
|
pulled, err := PullBlob(ctx, nil, &buf, signed.SignedUrl)
|
|
if err != nil {
|
|
t.Fatalf("PullBlob: %v", err)
|
|
}
|
|
if !bytes.Equal(buf.Bytes(), content) {
|
|
t.Errorf("round trip mismatch: got %q want %q", buf.String(), content)
|
|
}
|
|
if pulled.SHA256Sum != pushed.SHA256Sum {
|
|
t.Errorf("hash mismatch: got %q want %q", pulled.SHA256Sum, pushed.SHA256Sum)
|
|
}
|
|
}
|
|
|
|
// getSignedArtifactURL retries briefly because a freshly finalized artifact may
|
|
// not be signed yet.
|
|
func getSignedArtifactURL(t *testing.T, ctx context.Context, client *Client, run, job, name string) *GetSignedArtifactURLResponse {
|
|
t.Helper()
|
|
|
|
var lastErr error
|
|
for attempt := 0; attempt < 5; attempt++ {
|
|
signed, err := client.GetSignedArtifactURL(ctx, GetSignedArtifactURLRequest{RunID: run, JobRunID: job, Name: name})
|
|
if err == nil && signed.SignedUrl != "" {
|
|
return signed
|
|
}
|
|
lastErr = err
|
|
select {
|
|
case <-ctx.Done():
|
|
t.Fatalf("GetSignedArtifactURL: %v", ctx.Err())
|
|
case <-time.After(time.Duration(attempt+1) * time.Second):
|
|
}
|
|
}
|
|
t.Fatalf("GetSignedArtifactURL: %v", lastErr)
|
|
return nil
|
|
}
|