Synchronize delivery test fixtures and record the concurrency audit
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
releasekit "gitea.maximumdirect.net/eric/ReleaseKit"
|
||||
@@ -641,7 +642,7 @@ func TestDeliverPageOnlyCreatesAndReusesRelease(t *testing.T) {
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
}
|
||||
writeDeliveryProfile(t, repository, settings)
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3") // Historical lightweight tags are valid delivery inputs.
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
@@ -654,11 +655,11 @@ func TestDeliverPageOnlyCreatesAndReusesRelease(t *testing.T) {
|
||||
t.Fatalf("delivery output = %q", stdout.String())
|
||||
}
|
||||
}
|
||||
if state.releaseCreates != 1 || state.assetUploads != 0 || state.assetListReads != 0 {
|
||||
t.Fatalf("page-only calls = creates:%d asset uploads:%d asset reads:%d", state.releaseCreates, state.assetUploads, state.assetListReads)
|
||||
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 0 || state.snapshot().assetListReads != 0 {
|
||||
t.Fatalf("page-only calls = creates:%d asset uploads:%d asset reads:%d", state.snapshot().releaseCreates, state.snapshot().assetUploads, state.snapshot().assetListReads)
|
||||
}
|
||||
if state.noteBody != "# Example Product v1.2.3\n\nRelease note.\n" {
|
||||
t.Fatalf("hosted note body = %q", state.noteBody)
|
||||
if state.snapshot().noteBody != "# Example Product v1.2.3\n\nRelease note.\n" {
|
||||
t.Fatalf("hosted note body = %q", state.snapshot().noteBody)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,7 +676,7 @@ func TestDeliverBuildsTaggedArtifactsAndReusesAssets(t *testing.T) {
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
}
|
||||
writeDeliveryProfile(t, repository, settings)
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
cliGitRun(t, repository, "checkout", "--detach", "v1.2.3")
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
@@ -689,14 +690,15 @@ func TestDeliverBuildsTaggedArtifactsAndReusesAssets(t *testing.T) {
|
||||
t.Fatalf("second delivery did not report matching assets: %q", stdout.String())
|
||||
}
|
||||
}
|
||||
if state.releaseCreates != 1 || state.assetUploads != 2 {
|
||||
t.Fatalf("delivery calls = creates:%d asset uploads:%d", state.releaseCreates, state.assetUploads)
|
||||
observed := state.snapshot()
|
||||
if observed.releaseCreates != 1 || observed.assetUploads != 2 {
|
||||
t.Fatalf("delivery calls = creates:%d asset uploads:%d", observed.releaseCreates, observed.assetUploads)
|
||||
}
|
||||
if _, ok := state.assets["helper-v1.2.3-linux-amd64"]; !ok {
|
||||
t.Fatalf("binary was not uploaded: %#v", state.assets)
|
||||
if _, ok := observed.assets["helper-v1.2.3-linux-amd64"]; !ok {
|
||||
t.Fatalf("binary was not uploaded: %#v", observed.assets)
|
||||
}
|
||||
if _, ok := state.assets["SHA256SUMS"]; !ok {
|
||||
t.Fatalf("checksum manifest was not uploaded: %#v", state.assets)
|
||||
if _, ok := observed.assets["SHA256SUMS"]; !ok {
|
||||
t.Fatalf("checksum manifest was not uploaded: %#v", observed.assets)
|
||||
}
|
||||
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
||||
t.Fatalf("facade-owned build output remains after delivery: entries=%v error=%v", entries, err)
|
||||
@@ -717,7 +719,7 @@ func TestCIDeliverLoadsTaggedProfileAndSkipsUnavailablePageOnlyDependencies(t *t
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||
writeDeliveryProfileAt(t, repository, "profiles/ci-delivery.json", cli.Settings{Product: "Later Branch Profile"})
|
||||
cliGitRun(t, repository, "checkout", "--detach", expectedCommit)
|
||||
cliGitRun(t, repository, "remote", "remove", "origin")
|
||||
@@ -730,8 +732,8 @@ func TestCIDeliverLoadsTaggedProfileAndSkipsUnavailablePageOnlyDependencies(t *t
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||
}
|
||||
if state.releaseCreates != 1 || state.apiRequests == 0 {
|
||||
t.Fatalf("page delivery did not reach the server: creates=%d requests=%d", state.releaseCreates, state.apiRequests)
|
||||
if state.snapshot().releaseCreates != 1 || state.snapshot().apiRequests == 0 {
|
||||
t.Fatalf("page delivery did not reach the server: creates=%d requests=%d", state.snapshot().releaseCreates, state.snapshot().apiRequests)
|
||||
}
|
||||
if got := cliGitOutput(t, repository, "rev-parse", "HEAD"); got != expectedCommit {
|
||||
t.Fatalf("prepared HEAD = %s, want %s", got, expectedCommit)
|
||||
@@ -752,7 +754,7 @@ func TestCIDeliverRejectsWrongCommitBeforeGoOrHostedRequests(t *testing.T) {
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||
|
||||
shimDir := t.TempDir()
|
||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||
@@ -775,8 +777,8 @@ func TestCIDeliverRejectsWrongCommitBeforeGoOrHostedRequests(t *testing.T) {
|
||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("Go was invoked before commit agreement: %v", err)
|
||||
}
|
||||
if state.apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
||||
if state.snapshot().apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.snapshot().apiRequests)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,7 +805,7 @@ func TestCIDeliverRejectsExpectedModuleMismatchBeforeDependencyPreparation(t *te
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||
shimDir := t.TempDir()
|
||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||
goShim := "#!/bin/sh\n: > \"$RELEASEKIT_GO_MARKER\"\nexit 99\n"
|
||||
@@ -824,8 +826,8 @@ func TestCIDeliverRejectsExpectedModuleMismatchBeforeDependencyPreparation(t *te
|
||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("Go was invoked before profile agreement: %v", err)
|
||||
}
|
||||
if state.apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
||||
if state.snapshot().apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.snapshot().apiRequests)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,18 +842,18 @@ func TestCIDeliverPreparesDependenciesAndBuildsConfiguredTargets(t *testing.T) {
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
args := []string{"ci-deliver", "--repo", repository, "--commit", expectedCommit, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||
}
|
||||
if state.releaseCreates != 1 || state.assetUploads != 2 {
|
||||
t.Fatalf("delivery counts = creates:%d uploads:%d", state.releaseCreates, state.assetUploads)
|
||||
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 2 {
|
||||
t.Fatalf("delivery counts = creates:%d uploads:%d", state.snapshot().releaseCreates, state.snapshot().assetUploads)
|
||||
}
|
||||
if _, ok := state.assets["helper-v1.2.3-"+runtime.GOOS+"-"+runtime.GOARCH]; !ok {
|
||||
t.Fatalf("binary not uploaded: %#v", state.assets)
|
||||
if _, ok := state.snapshot().assets["helper-v1.2.3-"+runtime.GOOS+"-"+runtime.GOARCH]; !ok {
|
||||
t.Fatalf("binary not uploaded: %#v", state.snapshot().assets)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,7 +868,7 @@ func TestCIDeliverValidatesCredentialsBeforePreparingDependencies(t *testing.T)
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||
|
||||
shimDir := t.TempDir()
|
||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||
@@ -888,8 +890,8 @@ func TestCIDeliverValidatesCredentialsBeforePreparingDependencies(t *testing.T)
|
||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("Go was invoked before credential validation: %v", err)
|
||||
}
|
||||
if state.apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
||||
if state.snapshot().apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.snapshot().apiRequests)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,7 +906,7 @@ func TestDeliverRejectsInvalidTaggedSourceBeforeManagedBuild(t *testing.T) {
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
}
|
||||
writeDeliveryProfile(t, repository, settings)
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
if err := os.WriteFile(filepath.Join(repository, "uncommitted.txt"), []byte("dirty"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -912,8 +914,8 @@ func TestDeliverRejectsInvalidTaggedSourceBeforeManagedBuild(t *testing.T) {
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 1 || state.apiRequests != 0 || strings.Contains(stderr.String(), "building ") {
|
||||
t.Fatalf("invalid-source delivery status=%d requests=%d stdout=%q stderr=%q", status, state.apiRequests, stdout.String(), stderr.String())
|
||||
if status != 1 || state.snapshot().apiRequests != 0 || strings.Contains(stderr.String(), "building ") {
|
||||
t.Fatalf("invalid-source delivery status=%d requests=%d stdout=%q stderr=%q", status, state.snapshot().apiRequests, stdout.String(), stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,16 +960,18 @@ func TestDeliverFailureReportsRecoveryForPartialConflictAndUnknownStates(t *test
|
||||
Build: &build,
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
assetsDir, _ := writeDeliveryReceipt(t, repository, build)
|
||||
switch test.name {
|
||||
case "partial":
|
||||
state.failAssetName = "helper-v1.2.3-linux-amd64"
|
||||
state.update(func(state *deliveryServerData) { state.failAssetName = "helper-v1.2.3-linux-amd64" })
|
||||
case "conflict":
|
||||
state.assets["helper-v1.2.3-linux-amd64"] = []byte("different hosted bytes")
|
||||
state.update(func(state *deliveryServerData) {
|
||||
state.assets["helper-v1.2.3-linux-amd64"] = []byte("different hosted bytes")
|
||||
})
|
||||
case "unknown":
|
||||
state.ambiguousAssetName = "helper-v1.2.3-linux-amd64"
|
||||
state.update(func(state *deliveryServerData) { state.ambiguousAssetName = "helper-v1.2.3-linux-amd64" })
|
||||
}
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -995,17 +999,17 @@ func TestDeliverFailureReportsRecoveryForPartialConflictAndUnknownStates(t *test
|
||||
|
||||
func TestDeliverRejectsWrongHostedTagBeforeReleaseWrite(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
state := &deliveryServerState{remoteCommit: strings.Repeat("0", 40)}
|
||||
state := &deliveryServerState{data: deliveryServerData{remoteCommit: strings.Repeat("0", 40)}}
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 1 || state.releaseCreates != 0 || state.assetUploads != 0 {
|
||||
t.Fatalf("wrong hosted tag status=%d creates=%d uploads=%d stdout=%q stderr=%q", status, state.releaseCreates, state.assetUploads, stdout.String(), stderr.String())
|
||||
if status != 1 || state.snapshot().releaseCreates != 0 || state.snapshot().assetUploads != 0 {
|
||||
t.Fatalf("wrong hosted tag status=%d creates=%d uploads=%d stdout=%q stderr=%q", status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stdout.String(), stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,13 +1019,13 @@ func TestDeliverRequiresCredentialBeforeRemoteRequests(t *testing.T) {
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example", TokenEnv: "RELEASEKIT_TEST_DELIVERY_TOKEN"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
t.Setenv("RELEASEKIT_TEST_DELIVERY_TOKEN", "")
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 2 || state.apiRequests != 0 || !strings.Contains(stderr.String(), "RELEASEKIT_TEST_DELIVERY_TOKEN") {
|
||||
t.Fatalf("missing credential status=%d requests=%d stdout=%q stderr=%q", status, state.apiRequests, stdout.String(), stderr.String())
|
||||
if status != 2 || state.snapshot().apiRequests != 0 || !strings.Contains(stderr.String(), "RELEASEKIT_TEST_DELIVERY_TOKEN") {
|
||||
t.Fatalf("missing credential status=%d requests=%d stdout=%q stderr=%q", status, state.snapshot().apiRequests, stdout.String(), stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1035,7 @@ func TestDeliverRejectsCheckoutThatDiffersFromLocalTag(t *testing.T) {
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
if err := os.WriteFile(filepath.Join(repository, "later.txt"), []byte("later commit"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1041,11 +1045,11 @@ func TestDeliverRejectsCheckoutThatDiffersFromLocalTag(t *testing.T) {
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 1 || state.apiRequests != 0 || state.releaseCreates != 0 {
|
||||
t.Fatalf("mismatched checkout status=%d requests=%d creates=%d stdout=%q stderr=%q", status, state.apiRequests, state.releaseCreates, stdout.String(), stderr.String())
|
||||
if status != 1 || state.snapshot().apiRequests != 0 || state.snapshot().releaseCreates != 0 {
|
||||
t.Fatalf("mismatched checkout status=%d requests=%d creates=%d stdout=%q stderr=%q", status, state.snapshot().apiRequests, state.snapshot().releaseCreates, stdout.String(), stderr.String())
|
||||
}
|
||||
if got := cliGitOutput(t, repository, "rev-parse", "refs/tags/v1.2.3"); got != state.commit {
|
||||
t.Fatalf("delivery moved tag from %s to %s", state.commit, got)
|
||||
if got := cliGitOutput(t, repository, "rev-parse", "refs/tags/v1.2.3"); got != state.snapshot().commit {
|
||||
t.Fatalf("delivery moved tag from %s to %s", state.snapshot().commit, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1059,7 +1063,7 @@ func TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites(t *testin
|
||||
current := cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}
|
||||
settings := cli.Settings{Product: "Example Product", Build: ¤t, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}}
|
||||
writeDeliveryProfile(t, repository, settings)
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
receiptSettings := current
|
||||
if mode == "stale settings" {
|
||||
@@ -1085,16 +1089,16 @@ func TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites(t *testin
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assetsDir, "v1.2.3"}, &stdout, &stderr)
|
||||
if mode == "valid receipt" {
|
||||
if status != 0 || state.releaseCreates != 1 || state.assetUploads != 2 {
|
||||
t.Fatalf("valid receipt status=%d creates=%d uploads=%d stdout=%q stderr=%q", status, state.releaseCreates, state.assetUploads, stdout.String(), stderr.String())
|
||||
if status != 0 || state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 2 {
|
||||
t.Fatalf("valid receipt status=%d creates=%d uploads=%d stdout=%q stderr=%q", status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stdout.String(), stderr.String())
|
||||
}
|
||||
if _, err := os.Stat(artifactPath); err != nil {
|
||||
t.Fatalf("prebuilt artifact was not retained: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if status != 1 || state.apiRequests != 0 || state.releaseCreates != 0 || state.assetUploads != 0 {
|
||||
t.Fatalf("%s receipt status=%d requests=%d stdout=%q stderr=%q", mode, status, state.apiRequests, stdout.String(), stderr.String())
|
||||
if status != 1 || state.snapshot().apiRequests != 0 || state.snapshot().releaseCreates != 0 || state.snapshot().assetUploads != 0 {
|
||||
t.Fatalf("%s receipt status=%d requests=%d stdout=%q stderr=%q", mode, status, state.snapshot().apiRequests, stdout.String(), stderr.String())
|
||||
}
|
||||
if mode == "tampered" || mode == "missing artifact" {
|
||||
if strings.Contains(stderr.String(), "load verified build receipt") {
|
||||
@@ -1136,6 +1140,30 @@ func writeDeliveryReceipt(t *testing.T, repository string, settings cli.BuildSet
|
||||
}
|
||||
|
||||
type deliveryServerState struct {
|
||||
mu sync.Mutex
|
||||
data deliveryServerData
|
||||
}
|
||||
|
||||
func (state *deliveryServerState) update(change func(*deliveryServerData)) {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
change(&state.data)
|
||||
}
|
||||
|
||||
func (state *deliveryServerState) snapshot() deliveryServerData {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
result := state.data
|
||||
|
||||
result.assets = make(map[string][]byte, len(state.data.assets))
|
||||
for key, value := range state.data.assets {
|
||||
result.assets[key] = append([]byte(nil), value...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type deliveryServerData struct {
|
||||
tag string
|
||||
malformedCreate bool
|
||||
failAssetListing bool
|
||||
@@ -1156,36 +1184,38 @@ type deliveryServerState struct {
|
||||
|
||||
func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Server {
|
||||
t.Helper()
|
||||
if state.tag == "" {
|
||||
state.tag = "v1.2.3"
|
||||
data := &state.data
|
||||
if data.tag == "" {
|
||||
data.tag = "v1.2.3"
|
||||
}
|
||||
if state.assets == nil {
|
||||
state.assets = make(map[string][]byte)
|
||||
if data.assets == nil {
|
||||
data.assets = make(map[string][]byte)
|
||||
}
|
||||
serverURL := ""
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
if strings.HasPrefix(r.URL.Path, "/api/") {
|
||||
state.apiRequests++
|
||||
data.apiRequests++
|
||||
if r.Header.Get("Authorization") != "token fixture-token" {
|
||||
t.Errorf("authorization = %q", r.Header.Get("Authorization"))
|
||||
}
|
||||
}
|
||||
switch r.URL.Path {
|
||||
case "/api/v1/repos/owner/example/tags/" + state.tag:
|
||||
if state.commit == "" {
|
||||
case "/api/v1/repos/owner/example/tags/" + data.tag:
|
||||
if data.commit == "" {
|
||||
t.Error("tag source commit was not initialized")
|
||||
}
|
||||
commit := state.remoteCommit
|
||||
commit := data.remoteCommit
|
||||
if commit == "" {
|
||||
commit = state.commit
|
||||
commit = data.commit
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, state.tag, commit)
|
||||
case "/api/v1/repos/owner/example/releases/tags/" + state.tag:
|
||||
if !state.pageExists {
|
||||
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, data.tag, commit)
|
||||
case "/api/v1/repos/owner/example/releases/tags/" + data.tag:
|
||||
if !data.pageExists {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
writeDeliveryRelease(w, serverURL, state)
|
||||
writeDeliveryRelease(w, serverURL(r), data)
|
||||
case "/api/v1/repos/owner/example/releases":
|
||||
if r.Method != http.MethodPost {
|
||||
t.Errorf("release request method = %s", r.Method)
|
||||
@@ -1201,26 +1231,26 @@ func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Serve
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
t.Errorf("decode create payload: %v", err)
|
||||
}
|
||||
if payload.Tag != state.tag || payload.Target != state.commit {
|
||||
if payload.Tag != data.tag || payload.Target != data.commit {
|
||||
t.Errorf("release source payload = %#v", payload)
|
||||
}
|
||||
state.pageExists, state.releaseCreates, state.noteBody = true, state.releaseCreates+1, payload.Body
|
||||
if state.malformedCreate {
|
||||
data.pageExists, data.releaseCreates, data.noteBody = true, data.releaseCreates+1, payload.Body
|
||||
if data.malformedCreate {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "tag_name": state.tag, "name": "inconsistent response", "body": payload.Body})
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "tag_name": data.tag, "name": "inconsistent response", "body": payload.Body})
|
||||
return
|
||||
}
|
||||
writeDeliveryRelease(w, serverURL, state)
|
||||
writeDeliveryRelease(w, serverURL(r), data)
|
||||
case "/api/v1/repos/owner/example/releases/10/assets":
|
||||
if r.Method == http.MethodGet {
|
||||
state.assetListReads++
|
||||
if state.failAssetListing {
|
||||
data.assetListReads++
|
||||
if data.failAssetListing {
|
||||
http.Error(w, "unavailable", 503)
|
||||
return
|
||||
}
|
||||
list := make([]map[string]any, 0, len(state.assets))
|
||||
for name, content := range state.assets {
|
||||
list = append(list, map[string]any{"id": len(list) + 1, "name": name, "size": len(content), "browser_download_url": serverURL + "/files/" + name})
|
||||
list := make([]map[string]any, 0, len(data.assets))
|
||||
for name, content := range data.assets {
|
||||
list = append(list, map[string]any{"id": len(list) + 1, "name": name, "size": len(content), "browser_download_url": serverURL(r) + "/files/" + name})
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(list)
|
||||
return
|
||||
@@ -1242,28 +1272,28 @@ func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Serve
|
||||
if err != nil {
|
||||
t.Errorf("read asset bytes: %v", err)
|
||||
}
|
||||
if _, exists := state.assets[name]; exists {
|
||||
if _, exists := data.assets[name]; exists {
|
||||
t.Errorf("asset %q uploaded more than once", name)
|
||||
}
|
||||
if name == state.failAssetName {
|
||||
if name == data.failAssetName {
|
||||
http.Error(w, "upload failed", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if name == state.ambiguousAssetName {
|
||||
if name == data.ambiguousAssetName {
|
||||
http.Error(w, "upload outcome ambiguous", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
state.assets[name] = content
|
||||
state.assetUploads++
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": state.assetUploads, "name": name, "size": len(content)})
|
||||
data.assets[name] = content
|
||||
data.assetUploads++
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": data.assetUploads, "name": name, "size": len(content)})
|
||||
default:
|
||||
if strings.HasPrefix(r.URL.Path, "/files/") {
|
||||
if state.failAssetDownloads {
|
||||
if data.failAssetDownloads {
|
||||
http.Error(w, "unavailable", 503)
|
||||
return
|
||||
}
|
||||
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
||||
content, ok := state.assets[name]
|
||||
content, ok := data.assets[name]
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
@@ -1276,17 +1306,17 @@ func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Serve
|
||||
}
|
||||
})
|
||||
server := httptest.NewServer(handler)
|
||||
serverURL = server.URL
|
||||
return server
|
||||
}
|
||||
|
||||
func writeDeliveryRelease(w http.ResponseWriter, serverURL string, state *deliveryServerState) {
|
||||
// writeDeliveryRelease reads data while the request handler holds state.mu.
|
||||
func writeDeliveryRelease(w http.ResponseWriter, baseURL string, data *deliveryServerData) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
product := state.product
|
||||
product := data.product
|
||||
if product == "" {
|
||||
product = "Example Product"
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": serverURL + "/releases/10", "tag_name": state.tag, "target_commitish": state.commit, "name": product + " v1.2.3", "body": state.noteBody, "draft": false, "prerelease": false})
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": baseURL + "/releases/10", "tag_name": data.tag, "target_commitish": data.commit, "name": product + " v1.2.3", "body": data.noteBody, "draft": false, "prerelease": false})
|
||||
}
|
||||
|
||||
func writeDeliveryProfile(t *testing.T, repository string, settings cli.Settings) {
|
||||
@@ -1417,7 +1447,7 @@ func TestDeliverRejectsReceiptBuiltFromDirtySourceAfterCleanup(t *testing.T) {
|
||||
defer server.Close()
|
||||
settings := cli.Settings{Product: "Example Product", Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}}
|
||||
writeDeliveryProfile(t, repository, settings)
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
mainPath := filepath.Join(repository, "main.go")
|
||||
original, err := os.ReadFile(mainPath)
|
||||
@@ -1444,26 +1474,26 @@ func TestDeliverRejectsReceiptBuiltFromDirtySourceAfterCleanup(t *testing.T) {
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assets, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 1 || state.apiRequests != 0 || !strings.Contains(stderr.String(), "build source") {
|
||||
t.Fatalf("status=%d requests=%d %s", status, state.apiRequests, stderr.String())
|
||||
if status != 1 || state.snapshot().apiRequests != 0 || !strings.Contains(stderr.String(), "build source") {
|
||||
t.Fatalf("status=%d requests=%d %s", status, state.snapshot().apiRequests, stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoveredPageCreatePreservesPartialDeliveryOnInspectionFailure(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
state := &deliveryServerState{malformedCreate: true, failAssetListing: true}
|
||||
state := &deliveryServerState{data: deliveryServerData{malformedCreate: true, failAssetListing: true}}
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
build := cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Build: &build, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
assets, _ := writeDeliveryReceipt(t, repository, build)
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assets, "v1.2.3"}, &stdout, &stderr)
|
||||
if status != 1 || !strings.Contains(stderr.String(), "state=partial") || state.releaseCreates != 1 || state.assetListReads != 1 || state.assetUploads != 0 {
|
||||
t.Fatalf("status=%d state=%#v %s", status, state, stderr.String())
|
||||
if status != 1 || !strings.Contains(stderr.String(), "state=partial") || state.snapshot().releaseCreates != 1 || state.snapshot().assetListReads != 1 || state.snapshot().assetUploads != 0 {
|
||||
t.Fatalf("status=%d state=%#v %s", status, state.snapshot(), stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,12 +1501,12 @@ func TestUnreadableAssetsDoNotClaimAnUnknownWrite(t *testing.T) {
|
||||
for _, existingPage := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("existing-page-%t", existingPage), func(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
state := &deliveryServerState{pageExists: existingPage, noteBody: "# Example Product v1.2.3\n\nRelease note.\n", failAssetDownloads: true, assets: map[string][]byte{"helper-v1.2.3-linux-amd64": []byte("fixture artifact")}}
|
||||
state := &deliveryServerState{data: deliveryServerData{pageExists: existingPage, noteBody: "# Example Product v1.2.3\n\nRelease note.\n", failAssetDownloads: true, assets: map[string][]byte{"helper-v1.2.3-linux-amd64": []byte("fixture artifact")}}}
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
build := cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Build: &build, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
assets, _ := writeDeliveryReceipt(t, repository, build)
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
@@ -1488,8 +1518,8 @@ func TestUnreadableAssetsDoNotClaimAnUnknownWrite(t *testing.T) {
|
||||
expectedState = "not_attempted"
|
||||
expectedCreates = 0
|
||||
}
|
||||
if status != 1 || !strings.Contains(stderr.String(), "state="+expectedState) || state.assetUploads != 0 || state.releaseCreates != expectedCreates {
|
||||
t.Fatalf("status=%d creates=%d uploads=%d %s", status, state.releaseCreates, state.assetUploads, stderr.String())
|
||||
if status != 1 || !strings.Contains(stderr.String(), "state="+expectedState) || state.snapshot().assetUploads != 0 || state.snapshot().releaseCreates != expectedCreates {
|
||||
t.Fatalf("status=%d creates=%d uploads=%d %s", status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stderr.String())
|
||||
}
|
||||
if existingPage && !strings.Contains(stderr.String(), "recovery: fix the local or preflight failure") {
|
||||
t.Fatalf("not-attempted recovery guidance missing: %s", stderr.String())
|
||||
@@ -1532,7 +1562,7 @@ func TestTemporaryDeliveryBuildDoesNotRequireWritableRepositoryParent(t *testing
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
||||
state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
state.update(func(state *deliveryServerData) { state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") })
|
||||
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||
parent := t.TempDir()
|
||||
moved := filepath.Join(parent, "repo")
|
||||
|
||||
Reference in New Issue
Block a user