Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d5a8110c6 | |||
| d42727b038 | |||
| 059a79525a | |||
| 6176971307 | |||
| 8f6e72b0b8 |
@@ -37,7 +37,7 @@ func TestBuildAndPrebuiltDeliveryUseSameDirectoryBase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeRepositoryFile(t, repository, ".gitignore", "dist/\n")
|
writeRepositoryFile(t, repository, ".gitignore", "dist/\n")
|
||||||
state := &deliveryServerState{tag: tag}
|
state := &deliveryServerState{data: deliveryServerData{tag: tag}}
|
||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
settings := cli.Settings{Product: "Example Product", ModuleDir: tt.module,
|
settings := cli.Settings{Product: "Example Product", ModuleDir: tt.module,
|
||||||
@@ -47,7 +47,7 @@ func TestBuildAndPrebuiltDeliveryUseSameDirectoryBase(t *testing.T) {
|
|||||||
writeDeliveryProfile(t, repository, settings)
|
writeDeliveryProfile(t, repository, settings)
|
||||||
cliGitRun(t, repository, "add", ".")
|
cliGitRun(t, repository, "add", ".")
|
||||||
cliGitRun(t, repository, "commit", "-m", "Prepare module source and output exclusion")
|
cliGitRun(t, repository, "commit", "-m", "Prepare module source and output exclusion")
|
||||||
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", tag)
|
cliGitRun(t, repository, "tag", tag)
|
||||||
output, resolved := "dist", filepath.Join(moduleDirectory, "dist")
|
output, resolved := "dist", filepath.Join(moduleDirectory, "dist")
|
||||||
if tt.absolute {
|
if tt.absolute {
|
||||||
@@ -73,12 +73,12 @@ func TestBuildAndPrebuiltDeliveryUseSameDirectoryBase(t *testing.T) {
|
|||||||
args := []string{"deliver", "--repo", invocationRepository, "--assets", output, "v1.2.3"}
|
args := []string{"deliver", "--repo", invocationRepository, "--assets", output, "v1.2.3"}
|
||||||
stdout.Reset()
|
stdout.Reset()
|
||||||
stderr.Reset()
|
stderr.Reset()
|
||||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 || state.releaseCreates != 1 || state.assetUploads != 2 {
|
if status := run(context.Background(), args, &stdout, &stderr); status != 0 || state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 2 {
|
||||||
t.Fatalf("delivery status=%d, creates=%d, uploads=%d, stderr=%s", status, state.releaseCreates, state.assetUploads, stderr.String())
|
t.Fatalf("delivery status=%d, creates=%d, uploads=%d, stderr=%s", status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stderr.String())
|
||||||
}
|
}
|
||||||
if tt.alias {
|
if tt.alias {
|
||||||
if status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", output, "v1.2.3"}, &stdout, &stderr); status != 0 || state.assetUploads != 2 {
|
if status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", output, "v1.2.3"}, &stdout, &stderr); status != 0 || state.snapshot().assetUploads != 2 {
|
||||||
t.Fatalf("physical-path retry status=%d, uploads=%d, stderr=%s", status, state.assetUploads, stderr.String())
|
t.Fatalf("physical-path retry status=%d, uploads=%d, stderr=%s", status, state.snapshot().assetUploads, stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tt.module != "" {
|
if tt.module != "" {
|
||||||
@@ -86,17 +86,17 @@ func TestBuildAndPrebuiltDeliveryUseSameDirectoryBase(t *testing.T) {
|
|||||||
wrongModule := settings
|
wrongModule := settings
|
||||||
wrongModule.ModuleDir = ""
|
wrongModule.ModuleDir = ""
|
||||||
invocation := cli.Invocation{Repository: repository, AssetsDir: resolved, Version: "v1.2.3"}
|
invocation := cli.Invocation{Repository: repository, AssetsDir: resolved, Version: "v1.2.3"}
|
||||||
requestsBefore := state.apiRequests
|
requestsBefore := state.snapshot().apiRequests
|
||||||
if status := deliverWithSettings(context.Background(), invocation, wrongModule, &stdout, &stderr); status != 1 || state.apiRequests != requestsBefore {
|
if status := deliverWithSettings(context.Background(), invocation, wrongModule, &stdout, &stderr); status != 1 || state.snapshot().apiRequests != requestsBefore {
|
||||||
t.Fatalf("wrong module status=%d, requests=%d->%d, stderr=%s", status, requestsBefore, state.apiRequests, stderr.String())
|
t.Fatalf("wrong module status=%d, requests=%d->%d, stderr=%s", status, requestsBefore, state.snapshot().apiRequests, stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(receipt.Artifacts[0].Path, []byte("changed"), 0o600); err != nil {
|
if err := os.WriteFile(receipt.Artifacts[0].Path, []byte("changed"), 0o600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
requestsBefore := state.apiRequests
|
requestsBefore := state.snapshot().apiRequests
|
||||||
if status := run(context.Background(), args, &stdout, &stderr); status != 1 || state.apiRequests != requestsBefore {
|
if status := run(context.Background(), args, &stdout, &stderr); status != 1 || state.snapshot().apiRequests != requestsBefore {
|
||||||
t.Fatalf("changed content status=%d, requests=%d->%d, stderr=%s", status, requestsBefore, state.apiRequests, stderr.String())
|
t.Fatalf("changed content status=%d, requests=%d->%d, stderr=%s", status, requestsBefore, state.snapshot().apiRequests, stderr.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ func TestPageOnlyDeliveryUsesCommittedModuleInSparseCheckout(t *testing.T) {
|
|||||||
writeRepositoryFile(t, repository, module+"/docs/releases/v1.2.3.md", "# Example Product v1.2.3\n\nNested release note.\n")
|
writeRepositoryFile(t, repository, module+"/docs/releases/v1.2.3.md", "# Example Product v1.2.3\n\nNested release note.\n")
|
||||||
cliGitRun(t, repository, "add", ".")
|
cliGitRun(t, repository, "add", ".")
|
||||||
cliGitRun(t, repository, "commit", "-m", "Add sparse release module")
|
cliGitRun(t, repository, "commit", "-m", "Add sparse release module")
|
||||||
state := &deliveryServerState{tag: module + "/v1.2.3"}
|
state := &deliveryServerState{data: deliveryServerData{tag: module + "/v1.2.3"}}
|
||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", ModuleDir: module,
|
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", ModuleDir: module,
|
||||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
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", state.tag)
|
cliGitRun(t, repository, "tag", state.snapshot().tag)
|
||||||
cliGitRun(t, repository, "checkout", "--detach", state.tag)
|
cliGitRun(t, repository, "checkout", "--detach", state.snapshot().tag)
|
||||||
cliGitRun(t, repository, "sparse-checkout", "set", "--cone", "docs")
|
cliGitRun(t, repository, "sparse-checkout", "set", "--cone", "docs")
|
||||||
if _, err := os.Stat(filepath.Join(repository, filepath.FromSlash(module))); !errors.Is(err, os.ErrNotExist) {
|
if _, err := os.Stat(filepath.Join(repository, filepath.FromSlash(module))); !errors.Is(err, os.ErrNotExist) {
|
||||||
t.Fatalf("module fixture is materialized: %v", err)
|
t.Fatalf("module fixture is materialized: %v", err)
|
||||||
@@ -61,11 +61,11 @@ func TestPageOnlyDeliveryUsesCommittedModuleInSparseCheckout(t *testing.T) {
|
|||||||
t.Setenv("PATH", gitOnly)
|
t.Setenv("PATH", gitOnly)
|
||||||
for _, args := range [][]string{
|
for _, args := range [][]string{
|
||||||
{"deliver", "--repo", repository, "v1.2.3"},
|
{"deliver", "--repo", repository, "v1.2.3"},
|
||||||
{"ci-deliver", "--repo", repository, "--prepared", "--expected-module", module, "--commit", state.commit, state.tag},
|
{"ci-deliver", "--repo", repository, "--prepared", "--expected-module", module, "--commit", state.snapshot().commit, state.snapshot().tag},
|
||||||
} {
|
} {
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 || state.releaseCreates != 1 || state.assetUploads != 0 {
|
if status := run(context.Background(), args, &stdout, &stderr); status != 0 || state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 0 {
|
||||||
t.Fatalf("%s sparse delivery status=%d creates=%d uploads=%d stderr=%s", args[0], status, state.releaseCreates, state.assetUploads, stderr.String())
|
t.Fatalf("%s sparse delivery status=%d creates=%d uploads=%d stderr=%s", args[0], status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ func TestMainModuleWithoutDomainSupportsCheckBuildAndPageDelivery(t *testing.T)
|
|||||||
}
|
}
|
||||||
settings.Build = nil
|
settings.Build = nil
|
||||||
writeDeliveryProfile(t, repository, settings)
|
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, "tag", "v1.2.3")
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
if status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr); status != 0 || state.releaseCreates != 1 || state.assetUploads != 0 {
|
if status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr); status != 0 || state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 0 {
|
||||||
t.Fatalf("page delivery status=%d creates=%d uploads=%d stderr=%s", status, state.releaseCreates, state.assetUploads, stderr.String())
|
t.Fatalf("page delivery status=%d creates=%d uploads=%d stderr=%s", status, state.snapshot().releaseCreates, state.snapshot().assetUploads, stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
releasekit "gitea.maximumdirect.net/eric/ReleaseKit"
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
}
|
}
|
||||||
writeDeliveryProfile(t, repository, settings)
|
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.
|
cliGitRun(t, repository, "tag", "v1.2.3") // Historical lightweight tags are valid delivery inputs.
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
for attempt := 0; attempt < 2; attempt++ {
|
for attempt := 0; attempt < 2; attempt++ {
|
||||||
@@ -654,11 +655,11 @@ func TestDeliverPageOnlyCreatesAndReusesRelease(t *testing.T) {
|
|||||||
t.Fatalf("delivery output = %q", stdout.String())
|
t.Fatalf("delivery output = %q", stdout.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 0 || state.assetListReads != 0 {
|
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.releaseCreates, state.assetUploads, state.assetListReads)
|
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" {
|
if state.snapshot().noteBody != "# Example Product v1.2.3\n\nRelease note.\n" {
|
||||||
t.Fatalf("hosted note body = %q", state.noteBody)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
}
|
}
|
||||||
writeDeliveryProfile(t, repository, settings)
|
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, "tag", "v1.2.3")
|
||||||
cliGitRun(t, repository, "checkout", "--detach", "v1.2.3")
|
cliGitRun(t, repository, "checkout", "--detach", "v1.2.3")
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
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())
|
t.Fatalf("second delivery did not report matching assets: %q", stdout.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 2 {
|
observed := state.snapshot()
|
||||||
t.Fatalf("delivery calls = creates:%d asset uploads:%d", state.releaseCreates, state.assetUploads)
|
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 {
|
if _, ok := observed.assets["helper-v1.2.3-linux-amd64"]; !ok {
|
||||||
t.Fatalf("binary was not uploaded: %#v", state.assets)
|
t.Fatalf("binary was not uploaded: %#v", observed.assets)
|
||||||
}
|
}
|
||||||
if _, ok := state.assets["SHA256SUMS"]; !ok {
|
if _, ok := observed.assets["SHA256SUMS"]; !ok {
|
||||||
t.Fatalf("checksum manifest was not uploaded: %#v", state.assets)
|
t.Fatalf("checksum manifest was not uploaded: %#v", observed.assets)
|
||||||
}
|
}
|
||||||
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
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)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
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"})
|
writeDeliveryProfileAt(t, repository, "profiles/ci-delivery.json", cli.Settings{Product: "Later Branch Profile"})
|
||||||
cliGitRun(t, repository, "checkout", "--detach", expectedCommit)
|
cliGitRun(t, repository, "checkout", "--detach", expectedCommit)
|
||||||
cliGitRun(t, repository, "remote", "remove", "origin")
|
cliGitRun(t, repository, "remote", "remove", "origin")
|
||||||
@@ -730,8 +732,8 @@ func TestCIDeliverLoadsTaggedProfileAndSkipsUnavailablePageOnlyDependencies(t *t
|
|||||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||||
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.apiRequests == 0 {
|
if state.snapshot().releaseCreates != 1 || state.snapshot().apiRequests == 0 {
|
||||||
t.Fatalf("page delivery did not reach the server: creates=%d requests=%d", state.releaseCreates, state.apiRequests)
|
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 {
|
if got := cliGitOutput(t, repository, "rev-parse", "HEAD"); got != expectedCommit {
|
||||||
t.Fatalf("prepared HEAD = %s, want %s", 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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = expectedCommit
|
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||||
|
|
||||||
shimDir := t.TempDir()
|
shimDir := t.TempDir()
|
||||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||||
@@ -775,8 +777,8 @@ func TestCIDeliverRejectsWrongCommitBeforeGoOrHostedRequests(t *testing.T) {
|
|||||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||||
t.Fatalf("Go was invoked before commit agreement: %v", err)
|
t.Fatalf("Go was invoked before commit agreement: %v", err)
|
||||||
}
|
}
|
||||||
if state.apiRequests != 0 {
|
if state.snapshot().apiRequests != 0 {
|
||||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = expectedCommit
|
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||||
shimDir := t.TempDir()
|
shimDir := t.TempDir()
|
||||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||||
goShim := "#!/bin/sh\n: > \"$RELEASEKIT_GO_MARKER\"\nexit 99\n"
|
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) {
|
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||||
t.Fatalf("Go was invoked before profile agreement: %v", err)
|
t.Fatalf("Go was invoked before profile agreement: %v", err)
|
||||||
}
|
}
|
||||||
if state.apiRequests != 0 {
|
if state.snapshot().apiRequests != 0 {
|
||||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = expectedCommit
|
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
args := []string{"ci-deliver", "--repo", repository, "--commit", expectedCommit, "v1.2.3"}
|
args := []string{"ci-deliver", "--repo", repository, "--commit", expectedCommit, "v1.2.3"}
|
||||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||||
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 2 {
|
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 2 {
|
||||||
t.Fatalf("delivery counts = creates:%d uploads:%d", state.releaseCreates, state.assetUploads)
|
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 {
|
if _, ok := state.snapshot().assets["helper-v1.2.3-"+runtime.GOOS+"-"+runtime.GOARCH]; !ok {
|
||||||
t.Fatalf("binary not uploaded: %#v", state.assets)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = expectedCommit
|
state.update(func(state *deliveryServerData) { state.commit = expectedCommit })
|
||||||
|
|
||||||
shimDir := t.TempDir()
|
shimDir := t.TempDir()
|
||||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||||
@@ -888,8 +890,8 @@ func TestCIDeliverValidatesCredentialsBeforePreparingDependencies(t *testing.T)
|
|||||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||||
t.Fatalf("Go was invoked before credential validation: %v", err)
|
t.Fatalf("Go was invoked before credential validation: %v", err)
|
||||||
}
|
}
|
||||||
if state.apiRequests != 0 {
|
if state.snapshot().apiRequests != 0 {
|
||||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
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"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
}
|
}
|
||||||
writeDeliveryProfile(t, repository, settings)
|
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, "tag", "v1.2.3")
|
||||||
if err := os.WriteFile(filepath.Join(repository, "uncommitted.txt"), []byte("dirty"), 0600); err != nil {
|
if err := os.WriteFile(filepath.Join(repository, "uncommitted.txt"), []byte("dirty"), 0600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -912,8 +914,8 @@ func TestDeliverRejectsInvalidTaggedSourceBeforeManagedBuild(t *testing.T) {
|
|||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||||
if status != 1 || state.apiRequests != 0 || strings.Contains(stderr.String(), "building ") {
|
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.apiRequests, stdout.String(), stderr.String())
|
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,
|
Build: &build,
|
||||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
assetsDir, _ := writeDeliveryReceipt(t, repository, build)
|
assetsDir, _ := writeDeliveryReceipt(t, repository, build)
|
||||||
switch test.name {
|
switch test.name {
|
||||||
case "partial":
|
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":
|
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":
|
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")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
@@ -995,17 +999,17 @@ func TestDeliverFailureReportsRecoveryForPartialConflictAndUnknownStates(t *test
|
|||||||
|
|
||||||
func TestDeliverRejectsWrongHostedTagBeforeReleaseWrite(t *testing.T) {
|
func TestDeliverRejectsWrongHostedTagBeforeReleaseWrite(t *testing.T) {
|
||||||
repository := newCLIRepository(t)
|
repository := newCLIRepository(t)
|
||||||
state := &deliveryServerState{remoteCommit: strings.Repeat("0", 40)}
|
state := &deliveryServerState{data: deliveryServerData{remoteCommit: strings.Repeat("0", 40)}}
|
||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||||
if status != 1 || state.releaseCreates != 0 || state.assetUploads != 0 {
|
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.releaseCreates, state.assetUploads, stdout.String(), stderr.String())
|
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)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
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"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
t.Setenv("RELEASEKIT_TEST_DELIVERY_TOKEN", "")
|
t.Setenv("RELEASEKIT_TEST_DELIVERY_TOKEN", "")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
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") {
|
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.apiRequests, stdout.String(), stderr.String())
|
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)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
writeDeliveryProfile(t, repository, cli.Settings{Product: "Example Product", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
if err := os.WriteFile(filepath.Join(repository, "later.txt"), []byte("later commit"), 0o600); err != nil {
|
if err := os.WriteFile(filepath.Join(repository, "later.txt"), []byte("later commit"), 0o600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -1041,11 +1045,11 @@ func TestDeliverRejectsCheckoutThatDiffersFromLocalTag(t *testing.T) {
|
|||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr)
|
||||||
if status != 1 || state.apiRequests != 0 || state.releaseCreates != 0 {
|
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.apiRequests, state.releaseCreates, stdout.String(), stderr.String())
|
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 {
|
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.commit, got)
|
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"}}
|
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"}}
|
settings := cli.Settings{Product: "Example Product", Build: ¤t, Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}}
|
||||||
writeDeliveryProfile(t, repository, settings)
|
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, "tag", "v1.2.3")
|
||||||
receiptSettings := current
|
receiptSettings := current
|
||||||
if mode == "stale settings" {
|
if mode == "stale settings" {
|
||||||
@@ -1085,16 +1089,16 @@ func TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites(t *testin
|
|||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assetsDir, "v1.2.3"}, &stdout, &stderr)
|
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assetsDir, "v1.2.3"}, &stdout, &stderr)
|
||||||
if mode == "valid receipt" {
|
if mode == "valid receipt" {
|
||||||
if status != 0 || state.releaseCreates != 1 || state.assetUploads != 2 {
|
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.releaseCreates, state.assetUploads, stdout.String(), stderr.String())
|
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 {
|
if _, err := os.Stat(artifactPath); err != nil {
|
||||||
t.Fatalf("prebuilt artifact was not retained: %v", err)
|
t.Fatalf("prebuilt artifact was not retained: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if status != 1 || state.apiRequests != 0 || state.releaseCreates != 0 || state.assetUploads != 0 {
|
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.apiRequests, stdout.String(), stderr.String())
|
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 mode == "tampered" || mode == "missing artifact" {
|
||||||
if strings.Contains(stderr.String(), "load verified build receipt") {
|
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 {
|
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
|
tag string
|
||||||
malformedCreate bool
|
malformedCreate bool
|
||||||
failAssetListing bool
|
failAssetListing bool
|
||||||
@@ -1156,36 +1184,38 @@ type deliveryServerState struct {
|
|||||||
|
|
||||||
func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Server {
|
func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Server {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if state.tag == "" {
|
data := &state.data
|
||||||
state.tag = "v1.2.3"
|
if data.tag == "" {
|
||||||
|
data.tag = "v1.2.3"
|
||||||
}
|
}
|
||||||
if state.assets == nil {
|
if data.assets == nil {
|
||||||
state.assets = make(map[string][]byte)
|
data.assets = make(map[string][]byte)
|
||||||
}
|
}
|
||||||
serverURL := ""
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
state.mu.Lock()
|
||||||
|
defer state.mu.Unlock()
|
||||||
if strings.HasPrefix(r.URL.Path, "/api/") {
|
if strings.HasPrefix(r.URL.Path, "/api/") {
|
||||||
state.apiRequests++
|
data.apiRequests++
|
||||||
if r.Header.Get("Authorization") != "token fixture-token" {
|
if r.Header.Get("Authorization") != "token fixture-token" {
|
||||||
t.Errorf("authorization = %q", r.Header.Get("Authorization"))
|
t.Errorf("authorization = %q", r.Header.Get("Authorization"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
case "/api/v1/repos/owner/example/tags/" + state.tag:
|
case "/api/v1/repos/owner/example/tags/" + data.tag:
|
||||||
if state.commit == "" {
|
if data.commit == "" {
|
||||||
t.Error("tag source commit was not initialized")
|
t.Error("tag source commit was not initialized")
|
||||||
}
|
}
|
||||||
commit := state.remoteCommit
|
commit := data.remoteCommit
|
||||||
if commit == "" {
|
if commit == "" {
|
||||||
commit = state.commit
|
commit = data.commit
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, state.tag, commit)
|
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, data.tag, commit)
|
||||||
case "/api/v1/repos/owner/example/releases/tags/" + state.tag:
|
case "/api/v1/repos/owner/example/releases/tags/" + data.tag:
|
||||||
if !state.pageExists {
|
if !data.pageExists {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeDeliveryRelease(w, serverURL, state)
|
writeDeliveryRelease(w, serverURL(r), data)
|
||||||
case "/api/v1/repos/owner/example/releases":
|
case "/api/v1/repos/owner/example/releases":
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
t.Errorf("release request method = %s", r.Method)
|
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 {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
t.Errorf("decode create payload: %v", err)
|
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)
|
t.Errorf("release source payload = %#v", payload)
|
||||||
}
|
}
|
||||||
state.pageExists, state.releaseCreates, state.noteBody = true, state.releaseCreates+1, payload.Body
|
data.pageExists, data.releaseCreates, data.noteBody = true, data.releaseCreates+1, payload.Body
|
||||||
if state.malformedCreate {
|
if data.malformedCreate {
|
||||||
w.WriteHeader(http.StatusCreated)
|
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
|
return
|
||||||
}
|
}
|
||||||
writeDeliveryRelease(w, serverURL, state)
|
writeDeliveryRelease(w, serverURL(r), data)
|
||||||
case "/api/v1/repos/owner/example/releases/10/assets":
|
case "/api/v1/repos/owner/example/releases/10/assets":
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
state.assetListReads++
|
data.assetListReads++
|
||||||
if state.failAssetListing {
|
if data.failAssetListing {
|
||||||
http.Error(w, "unavailable", 503)
|
http.Error(w, "unavailable", 503)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
list := make([]map[string]any, 0, len(state.assets))
|
list := make([]map[string]any, 0, len(data.assets))
|
||||||
for name, content := range state.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 + "/files/" + name})
|
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)
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
return
|
return
|
||||||
@@ -1242,28 +1272,28 @@ func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Serve
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("read asset bytes: %v", err)
|
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)
|
t.Errorf("asset %q uploaded more than once", name)
|
||||||
}
|
}
|
||||||
if name == state.failAssetName {
|
if name == data.failAssetName {
|
||||||
http.Error(w, "upload failed", http.StatusBadRequest)
|
http.Error(w, "upload failed", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if name == state.ambiguousAssetName {
|
if name == data.ambiguousAssetName {
|
||||||
http.Error(w, "upload outcome ambiguous", http.StatusInternalServerError)
|
http.Error(w, "upload outcome ambiguous", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.assets[name] = content
|
data.assets[name] = content
|
||||||
state.assetUploads++
|
data.assetUploads++
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": state.assetUploads, "name": name, "size": len(content)})
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": data.assetUploads, "name": name, "size": len(content)})
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(r.URL.Path, "/files/") {
|
if strings.HasPrefix(r.URL.Path, "/files/") {
|
||||||
if state.failAssetDownloads {
|
if data.failAssetDownloads {
|
||||||
http.Error(w, "unavailable", 503)
|
http.Error(w, "unavailable", 503)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
||||||
content, ok := state.assets[name]
|
content, ok := data.assets[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
@@ -1276,17 +1306,17 @@ func newDeliveryServer(t *testing.T, state *deliveryServerState) *httptest.Serve
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
serverURL = server.URL
|
|
||||||
return server
|
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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
product := state.product
|
product := data.product
|
||||||
if product == "" {
|
if product == "" {
|
||||||
product = "Example 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) {
|
func writeDeliveryProfile(t *testing.T, repository string, settings cli.Settings) {
|
||||||
@@ -1417,7 +1447,7 @@ func TestDeliverRejectsReceiptBuiltFromDirtySourceAfterCleanup(t *testing.T) {
|
|||||||
defer server.Close()
|
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"}}
|
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)
|
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, "tag", "v1.2.3")
|
||||||
mainPath := filepath.Join(repository, "main.go")
|
mainPath := filepath.Join(repository, "main.go")
|
||||||
original, err := os.ReadFile(mainPath)
|
original, err := os.ReadFile(mainPath)
|
||||||
@@ -1444,26 +1474,26 @@ func TestDeliverRejectsReceiptBuiltFromDirtySourceAfterCleanup(t *testing.T) {
|
|||||||
stdout.Reset()
|
stdout.Reset()
|
||||||
stderr.Reset()
|
stderr.Reset()
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assets, "v1.2.3"}, &stdout, &stderr)
|
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") {
|
if status != 1 || state.snapshot().apiRequests != 0 || !strings.Contains(stderr.String(), "build source") {
|
||||||
t.Fatalf("status=%d requests=%d %s", status, state.apiRequests, stderr.String())
|
t.Fatalf("status=%d requests=%d %s", status, state.snapshot().apiRequests, stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecoveredPageCreatePreservesPartialDeliveryOnInspectionFailure(t *testing.T) {
|
func TestRecoveredPageCreatePreservesPartialDeliveryOnInspectionFailure(t *testing.T) {
|
||||||
repository := newCLIRepository(t)
|
repository := newCLIRepository(t)
|
||||||
state := &deliveryServerState{malformedCreate: true, failAssetListing: true}
|
state := &deliveryServerState{data: deliveryServerData{malformedCreate: true, failAssetListing: true}}
|
||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
build := cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}
|
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"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
assets, _ := writeDeliveryReceipt(t, repository, build)
|
assets, _ := writeDeliveryReceipt(t, repository, build)
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
status := run(context.Background(), []string{"deliver", "--repo", repository, "--assets", assets, "v1.2.3"}, &stdout, &stderr)
|
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 {
|
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, stderr.String())
|
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} {
|
for _, existingPage := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprintf("existing-page-%t", existingPage), func(t *testing.T) {
|
t.Run(fmt.Sprintf("existing-page-%t", existingPage), func(t *testing.T) {
|
||||||
repository := newCLIRepository(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)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
build := cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}
|
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"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
assets, _ := writeDeliveryReceipt(t, repository, build)
|
assets, _ := writeDeliveryReceipt(t, repository, build)
|
||||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||||
@@ -1488,8 +1518,8 @@ func TestUnreadableAssetsDoNotClaimAnUnknownWrite(t *testing.T) {
|
|||||||
expectedState = "not_attempted"
|
expectedState = "not_attempted"
|
||||||
expectedCreates = 0
|
expectedCreates = 0
|
||||||
}
|
}
|
||||||
if status != 1 || !strings.Contains(stderr.String(), "state="+expectedState) || state.assetUploads != 0 || state.releaseCreates != expectedCreates {
|
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.releaseCreates, state.assetUploads, stderr.String())
|
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") {
|
if existingPage && !strings.Contains(stderr.String(), "recovery: fix the local or preflight failure") {
|
||||||
t.Fatalf("not-attempted recovery guidance missing: %s", stderr.String())
|
t.Fatalf("not-attempted recovery guidance missing: %s", stderr.String())
|
||||||
@@ -1532,7 +1562,7 @@ func TestTemporaryDeliveryBuildDoesNotRequireWritableRepositoryParent(t *testing
|
|||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
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"}})
|
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")
|
cliGitRun(t, repository, "tag", "v1.2.3")
|
||||||
parent := t.TempDir()
|
parent := t.TempDir()
|
||||||
moved := filepath.Join(parent, "repo")
|
moved := filepath.Join(parent, "repo")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit/internal/cli"
|
"gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit/internal/cli"
|
||||||
@@ -43,18 +44,18 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
|||||||
repository := newWorkflowRepository(t, example)
|
repository := newWorkflowRepository(t, example)
|
||||||
writeMappedProfile(t, repository, "releasekit-ckconfig.json", cli.Settings{
|
writeMappedProfile(t, repository, "releasekit-ckconfig.json", cli.Settings{
|
||||||
Product: "Example Consumer Integration", ModuleDir: "integrations/ckconfig",
|
Product: "Example Consumer Integration", ModuleDir: "integrations/ckconfig",
|
||||||
Delivery: workflowDeliverySettings(delivery.URL),
|
Delivery: workflowDeliverySettings(delivery.snapshot().URL),
|
||||||
})
|
})
|
||||||
writeRepositoryFile(t, repository, "integrations/ckconfig/docs/releases/v1.2.3.md", "# Example Consumer Integration v1.2.3\n\nTagged integration note.\n")
|
writeRepositoryFile(t, repository, "integrations/ckconfig/docs/releases/v1.2.3.md", "# Example Consumer Integration v1.2.3\n\nTagged integration note.\n")
|
||||||
writeRepositoryFile(t, repository, "integrations/ckconfig/go.mod", workflowModuleFile("example.com/multi-module-consumer/integrations/ckconfig", "v0.2.197"))
|
writeRepositoryFile(t, repository, "integrations/ckconfig/go.mod", workflowModuleFile("example.com/multi-module-consumer/integrations/ckconfig", "v0.2.197"))
|
||||||
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
||||||
delivery.commit = commit
|
delivery.update(func(delivery *mappedDeliveryData) { delivery.commit = commit })
|
||||||
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
||||||
if output := runWoodpeckerBlock(t, command, repository, env); !strings.Contains(output, "state complete") || !strings.Contains(output, "tool-version=v0.2.197") {
|
if output := runWoodpeckerBlock(t, command, repository, env); !strings.Contains(output, "state complete") || !strings.Contains(output, "tool-version=v0.2.197") {
|
||||||
t.Fatalf("tagged page delivery output = %q", output)
|
t.Fatalf("tagged page delivery output = %q", output)
|
||||||
}
|
}
|
||||||
if delivery.creates != 1 || delivery.requests == 0 || delivery.tag != tag {
|
if delivery.snapshot().creates != 1 || delivery.snapshot().requests == 0 || delivery.snapshot().tag != tag {
|
||||||
t.Fatalf("page delivery fixture = %#v", delivery)
|
t.Fatalf("page delivery fixture = %#v", delivery.snapshot())
|
||||||
}
|
}
|
||||||
|
|
||||||
cliGitRun(t, repository, "checkout", "main")
|
cliGitRun(t, repository, "checkout", "main")
|
||||||
@@ -68,8 +69,8 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
|||||||
if !strings.Contains(output, "tool-version=v0.2.197") || strings.Contains(output, "tool-version=v0.2.198") {
|
if !strings.Contains(output, "tool-version=v0.2.197") || strings.Contains(output, "tool-version=v0.2.198") {
|
||||||
t.Fatalf("tag retry did not use its committed tool version: %q", output)
|
t.Fatalf("tag retry did not use its committed tool version: %q", output)
|
||||||
}
|
}
|
||||||
if delivery.creates != 1 || delivery.tag != tag {
|
if delivery.snapshot().creates != 1 || delivery.snapshot().tag != tag {
|
||||||
t.Fatalf("tag retry did not reuse the tagged mapping: %#v", delivery)
|
t.Fatalf("tag retry did not reuse the tagged mapping: %#v", delivery.snapshot())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -80,25 +81,26 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
|||||||
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
||||||
Product: "Example Consumer Core v2", ModuleDir: "integrations/core/v2",
|
Product: "Example Consumer Core v2", ModuleDir: "integrations/core/v2",
|
||||||
Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "core", Targets: []string{"linux/amd64"}},
|
Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "core", Targets: []string{"linux/amd64"}},
|
||||||
Delivery: workflowDeliverySettings(delivery.URL),
|
Delivery: workflowDeliverySettings(delivery.snapshot().URL),
|
||||||
})
|
})
|
||||||
writeRepositoryFile(t, repository, "integrations/core/v2/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nTagged core note.\n")
|
writeRepositoryFile(t, repository, "integrations/core/v2/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nTagged core note.\n")
|
||||||
prepareWorkflowModuleSum(t, repository, "integrations/core/v2", proxy)
|
prepareWorkflowModuleSum(t, repository, "integrations/core/v2", proxy)
|
||||||
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
||||||
delivery.commit = commit
|
delivery.update(func(delivery *mappedDeliveryData) { delivery.commit = commit })
|
||||||
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
||||||
output := runWoodpeckerBlock(t, command, repository, env)
|
output := runWoodpeckerBlock(t, command, repository, env)
|
||||||
if !strings.Contains(output, "tool-version=v0.2.197") || !strings.Contains(output, "state complete") {
|
if !strings.Contains(output, "tool-version=v0.2.197") || !strings.Contains(output, "state complete") {
|
||||||
t.Fatalf("binary delivery output = %q", output)
|
t.Fatalf("binary delivery output = %q", output)
|
||||||
}
|
}
|
||||||
if delivery.creates != 1 || delivery.uploads != 2 || delivery.tag != tag {
|
observed := delivery.snapshot()
|
||||||
t.Fatalf("binary delivery fixture = %#v", delivery)
|
if observed.creates != 1 || observed.uploads != 2 || observed.tag != tag {
|
||||||
|
t.Fatalf("binary delivery fixture = %#v", observed)
|
||||||
}
|
}
|
||||||
if _, ok := delivery.assets["core-v2.1.0-linux-amd64"]; !ok {
|
if _, ok := observed.assets["core-v2.1.0-linux-amd64"]; !ok {
|
||||||
t.Fatalf("binary asset missing: %#v", delivery.assets)
|
t.Fatalf("binary asset missing: %#v", observed.assets)
|
||||||
}
|
}
|
||||||
if _, ok := delivery.assets["SHA256SUMS"]; !ok {
|
if _, ok := observed.assets["SHA256SUMS"]; !ok {
|
||||||
t.Fatalf("checksum asset missing: %#v", delivery.assets)
|
t.Fatalf("checksum asset missing: %#v", observed.assets)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -109,21 +111,21 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
|||||||
writeRepositoryFile(t, repository, "release-modules.tsv", strings.ReplaceAll(readWorkflowFile(t, repository, "release-modules.tsv"), "integrations/core/v2\treleasekit-core-v2.json", "integrations/core\treleasekit-core-v2.json"))
|
writeRepositoryFile(t, repository, "release-modules.tsv", strings.ReplaceAll(readWorkflowFile(t, repository, "release-modules.tsv"), "integrations/core/v2\treleasekit-core-v2.json", "integrations/core\treleasekit-core-v2.json"))
|
||||||
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
||||||
Product: "Example Consumer Core v2", ModuleDir: "integrations/core",
|
Product: "Example Consumer Core v2", ModuleDir: "integrations/core",
|
||||||
Delivery: workflowDeliverySettings(delivery.URL),
|
Delivery: workflowDeliverySettings(delivery.snapshot().URL),
|
||||||
})
|
})
|
||||||
writeRepositoryFile(t, repository, "integrations/core/go.mod", workflowModuleFile("example.com/multi-module-consumer/integrations/core/v2", "v0.2.197"))
|
writeRepositoryFile(t, repository, "integrations/core/go.mod", workflowModuleFile("example.com/multi-module-consumer/integrations/core/v2", "v0.2.197"))
|
||||||
writeRepositoryFile(t, repository, "integrations/core/main.go", readWorkflowFile(t, example, "integrations/core/v2/main.go"))
|
writeRepositoryFile(t, repository, "integrations/core/main.go", readWorkflowFile(t, example, "integrations/core/v2/main.go"))
|
||||||
writeRepositoryFile(t, repository, "integrations/core/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nTagged core note.\n")
|
writeRepositoryFile(t, repository, "integrations/core/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nTagged core note.\n")
|
||||||
prepareWorkflowModuleSum(t, repository, "integrations/core", proxy)
|
prepareWorkflowModuleSum(t, repository, "integrations/core", proxy)
|
||||||
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
||||||
delivery.commit = commit
|
delivery.update(func(delivery *mappedDeliveryData) { delivery.commit = commit })
|
||||||
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
||||||
output := runWoodpeckerBlock(t, command, repository, env)
|
output := runWoodpeckerBlock(t, command, repository, env)
|
||||||
if !strings.Contains(output, "tool-version=v0.2.197") || !strings.Contains(output, "state complete") {
|
if !strings.Contains(output, "tool-version=v0.2.197") || !strings.Contains(output, "state complete") {
|
||||||
t.Fatalf("virtual-major delivery output = %q", output)
|
t.Fatalf("virtual-major delivery output = %q", output)
|
||||||
}
|
}
|
||||||
if delivery.creates != 1 || delivery.tag != tag {
|
if delivery.snapshot().creates != 1 || delivery.snapshot().tag != tag {
|
||||||
t.Fatalf("virtual-major delivery fixture = %#v", delivery)
|
t.Fatalf("virtual-major delivery fixture = %#v", delivery.snapshot())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -138,24 +140,48 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
|||||||
writeRepositoryFile(t, repository, "release-modules.tsv", mapping)
|
writeRepositoryFile(t, repository, "release-modules.tsv", mapping)
|
||||||
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
writeMappedProfile(t, repository, "releasekit-core-v2.json", cli.Settings{
|
||||||
Product: "Example Consumer Core v2", ModuleDir: "integrations/core/v2",
|
Product: "Example Consumer Core v2", ModuleDir: "integrations/core/v2",
|
||||||
Delivery: workflowDeliverySettings(delivery.URL),
|
Delivery: workflowDeliverySettings(delivery.snapshot().URL),
|
||||||
})
|
})
|
||||||
writeRepositoryFile(t, repository, "integrations/core/v2/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nMismatched mapped note.\n")
|
writeRepositoryFile(t, repository, "integrations/core/v2/docs/releases/v2.1.0.md", "# Example Consumer Core v2 v2.1.0\n\nMismatched mapped note.\n")
|
||||||
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
||||||
delivery.commit = commit
|
delivery.update(func(delivery *mappedDeliveryData) { delivery.commit = commit })
|
||||||
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
||||||
if output, err := executeWoodpeckerBlock(command, repository, env); err == nil {
|
if output, err := executeWoodpeckerBlock(command, repository, env); err == nil {
|
||||||
t.Fatalf("inconsistent mapping succeeded: %s", output)
|
t.Fatalf("inconsistent mapping succeeded: %s", output)
|
||||||
} else if !strings.Contains(output, tag) || !strings.Contains(output, "integrations/core/v2.1.0") {
|
} else if !strings.Contains(output, tag) || !strings.Contains(output, "integrations/core/v2.1.0") {
|
||||||
t.Fatalf("inconsistent mapping did not fail the exact tag comparison: %s", output)
|
t.Fatalf("inconsistent mapping did not fail the exact tag comparison: %s", output)
|
||||||
}
|
}
|
||||||
if delivery.requests != 0 {
|
if delivery.snapshot().requests != 0 {
|
||||||
t.Fatalf("exact event tag mismatch reached hosted delivery: %#v", delivery)
|
t.Fatalf("exact event tag mismatch reached hosted delivery: %#v", delivery.snapshot())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type mappedDeliveryFixture struct {
|
type mappedDeliveryFixture struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
data mappedDeliveryData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *mappedDeliveryFixture) update(change func(*mappedDeliveryData)) {
|
||||||
|
state.mu.Lock()
|
||||||
|
defer state.mu.Unlock()
|
||||||
|
change(&state.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *mappedDeliveryFixture) snapshot() mappedDeliveryData {
|
||||||
|
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 mappedDeliveryData struct {
|
||||||
URL string
|
URL string
|
||||||
tag string
|
tag string
|
||||||
commit string
|
commit string
|
||||||
@@ -170,9 +196,12 @@ type mappedDeliveryFixture struct {
|
|||||||
|
|
||||||
func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
state := &mappedDeliveryFixture{tag: tag, assets: make(map[string][]byte)}
|
state := &mappedDeliveryFixture{data: mappedDeliveryData{tag: tag, assets: make(map[string][]byte)}}
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
data := &state.data
|
||||||
state.requests++
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
state.mu.Lock()
|
||||||
|
defer state.mu.Unlock()
|
||||||
|
data.requests++
|
||||||
if r.Header.Get("Authorization") != "token fixture-token" {
|
if r.Header.Get("Authorization") != "token fixture-token" {
|
||||||
t.Errorf("delivery authorization = %q", r.Header.Get("Authorization"))
|
t.Errorf("delivery authorization = %q", r.Header.Get("Authorization"))
|
||||||
}
|
}
|
||||||
@@ -180,13 +209,13 @@ func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
|||||||
escapedTag := url.PathEscape(tag)
|
escapedTag := url.PathEscape(tag)
|
||||||
switch r.URL.EscapedPath() {
|
switch r.URL.EscapedPath() {
|
||||||
case apiRoot + "tags/" + escapedTag:
|
case apiRoot + "tags/" + escapedTag:
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"name": tag, "commit": map[string]string{"sha": state.commit}})
|
_ = json.NewEncoder(w).Encode(map[string]any{"name": tag, "commit": map[string]string{"sha": data.commit}})
|
||||||
case apiRoot + "releases/tags/" + escapedTag:
|
case apiRoot + "releases/tags/" + escapedTag:
|
||||||
if !state.page {
|
if !data.page {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": serverURL(r) + "/releases/10", "tag_name": tag, "target_commitish": state.commit, "name": state.name, "body": state.body, "draft": false, "prerelease": false})
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": serverURL(r) + "/releases/10", "tag_name": tag, "target_commitish": data.commit, "name": data.name, "body": data.body, "draft": false, "prerelease": false})
|
||||||
case apiRoot + "releases":
|
case apiRoot + "releases":
|
||||||
var payload struct {
|
var payload struct {
|
||||||
Tag string `json:"tag_name"`
|
Tag string `json:"tag_name"`
|
||||||
@@ -197,18 +226,18 @@ func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
|||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
t.Errorf("decode release create: %v", err)
|
t.Errorf("decode release create: %v", err)
|
||||||
}
|
}
|
||||||
if payload.Tag != tag || payload.Commit != state.commit {
|
if payload.Tag != tag || payload.Commit != data.commit {
|
||||||
t.Errorf("release payload = %#v", payload)
|
t.Errorf("release payload = %#v", payload)
|
||||||
}
|
}
|
||||||
state.page = true
|
data.page = true
|
||||||
state.name = payload.Name
|
data.name = payload.Name
|
||||||
state.body = payload.Body
|
data.body = payload.Body
|
||||||
state.creates++
|
data.creates++
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": serverURL(r) + "/releases/10", "tag_name": tag, "target_commitish": state.commit, "name": payload.Name, "body": payload.Body, "draft": false, "prerelease": false})
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 10, "html_url": serverURL(r) + "/releases/10", "tag_name": tag, "target_commitish": data.commit, "name": payload.Name, "body": payload.Body, "draft": false, "prerelease": false})
|
||||||
case apiRoot + "releases/10/assets":
|
case apiRoot + "releases/10/assets":
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
listing := make([]map[string]any, 0, len(state.assets))
|
listing := make([]map[string]any, 0, len(data.assets))
|
||||||
for name, content := range state.assets {
|
for name, content := range data.assets {
|
||||||
listing = append(listing, map[string]any{"id": len(listing) + 1, "name": name, "size": len(content), "browser_download_url": serverURL(r) + "/files/" + name})
|
listing = append(listing, map[string]any{"id": len(listing) + 1, "name": name, "size": len(content), "browser_download_url": serverURL(r) + "/files/" + name})
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(listing)
|
_ = json.NewEncoder(w).Encode(listing)
|
||||||
@@ -231,13 +260,13 @@ func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("read asset bytes: %v", err)
|
t.Errorf("read asset bytes: %v", err)
|
||||||
}
|
}
|
||||||
state.assets[name] = content
|
data.assets[name] = content
|
||||||
state.uploads++
|
data.uploads++
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": state.uploads, "name": name, "size": len(content)})
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": data.uploads, "name": name, "size": len(content)})
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(r.URL.Path, "/files/") {
|
if strings.HasPrefix(r.URL.Path, "/files/") {
|
||||||
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
||||||
if content, ok := state.assets[name]; ok {
|
if content, ok := data.assets[name]; ok {
|
||||||
_, _ = w.Write(content)
|
_, _ = w.Write(content)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -248,8 +277,9 @@ func newMappedDeliveryFixture(t *testing.T, tag string) *mappedDeliveryFixture {
|
|||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
data.URL = "http://" + server.Listener.Addr().String()
|
||||||
|
server.Start()
|
||||||
t.Cleanup(server.Close)
|
t.Cleanup(server.Close)
|
||||||
state.URL = server.URL
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) {
|
|||||||
cliGitRun(t, repository, "add", "docs/releases/"+version+".md")
|
cliGitRun(t, repository, "add", "docs/releases/"+version+".md")
|
||||||
cliGitRun(t, repository, "commit", "-m", "prepare source-only delivery")
|
cliGitRun(t, repository, "commit", "-m", "prepare source-only delivery")
|
||||||
|
|
||||||
state := &deliveryServerState{product: "ReleaseKit"}
|
state := &deliveryServerState{data: deliveryServerData{product: "ReleaseKit"}}
|
||||||
server := newDeliveryServer(t, state)
|
server := newDeliveryServer(t, state)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
writeDeliveryProfile(t, repository, cli.Settings{
|
writeDeliveryProfile(t, repository, cli.Settings{
|
||||||
Product: "ReleaseKit",
|
Product: "ReleaseKit",
|
||||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
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", "-a", version, "-m", "ReleaseKit v1.2.3")
|
cliGitRun(t, repository, "tag", "-a", version, "-m", "ReleaseKit v1.2.3")
|
||||||
|
|
||||||
remote := filepath.Join(t.TempDir(), "origin.git")
|
remote := filepath.Join(t.TempDir(), "origin.git")
|
||||||
@@ -78,7 +78,7 @@ func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
env := map[string]string{
|
env := map[string]string{
|
||||||
"CI_WORKSPACE": repository,
|
"CI_WORKSPACE": repository,
|
||||||
"CI_COMMIT_SHA": state.commit,
|
"CI_COMMIT_SHA": state.snapshot().commit,
|
||||||
"CI_COMMIT_TAG": version,
|
"CI_COMMIT_TAG": version,
|
||||||
"RELEASEKIT_GITEA_TOKEN": "fixture-token",
|
"RELEASEKIT_GITEA_TOKEN": "fixture-token",
|
||||||
"GOTOOLCHAIN": "local",
|
"GOTOOLCHAIN": "local",
|
||||||
@@ -87,14 +87,14 @@ func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command := strings.ReplaceAll(block, "$${", "${")
|
command := strings.ReplaceAll(block, "$${", "${")
|
||||||
|
|
||||||
state.remoteCommit = strings.Repeat("f", 40)
|
state.update(func(state *deliveryServerData) { state.remoteCommit = strings.Repeat("f", 40) })
|
||||||
if output, err := executeWoodpeckerBlock(command, repository, env); err == nil {
|
if output, err := executeWoodpeckerBlock(command, repository, env); err == nil {
|
||||||
t.Fatalf("delivery with a mismatched hosted tag succeeded: %s", output)
|
t.Fatalf("delivery with a mismatched hosted tag succeeded: %s", output)
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 0 || cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}") != state.commit {
|
if state.snapshot().releaseCreates != 0 || cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}") != state.snapshot().commit {
|
||||||
t.Fatal("delivery failure changed hosted release state or source tag")
|
t.Fatal("delivery failure changed hosted release state or source tag")
|
||||||
}
|
}
|
||||||
state.remoteCommit = ""
|
state.update(func(state *deliveryServerData) { state.remoteCommit = "" })
|
||||||
|
|
||||||
for attempt := 0; attempt < 2; attempt++ {
|
for attempt := 0; attempt < 2; attempt++ {
|
||||||
output := runWoodpeckerBlock(t, command, repository, env)
|
output := runWoodpeckerBlock(t, command, repository, env)
|
||||||
@@ -102,14 +102,14 @@ func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) {
|
|||||||
t.Fatalf("delivery attempt %d output = %q", attempt+1, output)
|
t.Fatalf("delivery attempt %d output = %q", attempt+1, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 0 || state.assetListReads != 0 {
|
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 0 || state.snapshot().assetListReads != 0 {
|
||||||
t.Fatalf("page-only workflow calls = creates:%d uploads:%d asset reads:%d", state.releaseCreates, state.assetUploads, state.assetListReads)
|
t.Fatalf("page-only workflow calls = creates:%d uploads:%d asset reads:%d", state.snapshot().releaseCreates, state.snapshot().assetUploads, state.snapshot().assetListReads)
|
||||||
}
|
}
|
||||||
if state.noteBody != note {
|
if state.snapshot().noteBody != note {
|
||||||
t.Fatalf("hosted note body = %q, want complete note %q", state.noteBody, note)
|
t.Fatalf("hosted note body = %q, want complete note %q", state.snapshot().noteBody, note)
|
||||||
}
|
}
|
||||||
if got := cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}"); got != state.commit {
|
if got := cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}"); got != state.snapshot().commit {
|
||||||
t.Fatalf("source tag moved after delivery: %s, want %s", got, state.commit)
|
t.Fatalf("source tag moved after delivery: %s, want %s", got, state.snapshot().commit)
|
||||||
}
|
}
|
||||||
if status := cliGitOutput(t, repository, "status", "--porcelain"); status != "" {
|
if status := cliGitOutput(t, repository, "status", "--porcelain"); status != "" {
|
||||||
t.Fatalf("tagged checkout became dirty after delivery: %s", status)
|
t.Fatalf("tagged checkout became dirty after delivery: %s", status)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
|||||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
commit := pushTaggedCLIRepository(t, repository)
|
commit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = commit
|
state.update(func(state *deliveryServerData) { state.commit = commit })
|
||||||
|
|
||||||
goDir, goLog := installGoBootstrapShim(t)
|
goDir, goLog := installGoBootstrapShim(t)
|
||||||
env := templateEnvironment(repository, commit, goDir, goLog)
|
env := templateEnvironment(repository, commit, goDir, goLog)
|
||||||
@@ -51,8 +51,8 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
|||||||
if !strings.Contains(output, "state complete") {
|
if !strings.Contains(output, "state complete") {
|
||||||
t.Fatalf("delivery output=%q", output)
|
t.Fatalf("delivery output=%q", output)
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 0 {
|
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 0 {
|
||||||
t.Fatalf("source-only calls = creates:%d uploads:%d", state.releaseCreates, state.assetUploads)
|
t.Fatalf("source-only calls = creates:%d uploads:%d", state.snapshot().releaseCreates, state.snapshot().assetUploads)
|
||||||
}
|
}
|
||||||
assertModuleBootstrap(t, repository, goLog, moduleBefore)
|
assertModuleBootstrap(t, repository, goLog, moduleBefore)
|
||||||
})
|
})
|
||||||
@@ -73,7 +73,7 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
|||||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||||
})
|
})
|
||||||
commit := pushTaggedCLIRepository(t, repository)
|
commit := pushTaggedCLIRepository(t, repository)
|
||||||
state.commit = commit
|
state.update(func(state *deliveryServerData) { state.commit = commit })
|
||||||
|
|
||||||
goDir, goLog := installGoBootstrapShim(t)
|
goDir, goLog := installGoBootstrapShim(t)
|
||||||
env := templateEnvironment(repository, commit, goDir, goLog)
|
env := templateEnvironment(repository, commit, goDir, goLog)
|
||||||
@@ -88,14 +88,14 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
|||||||
t.Fatalf("second delivery did not reuse matching assets: %q", output)
|
t.Fatalf("second delivery did not reuse matching assets: %q", output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state.releaseCreates != 1 || state.assetUploads != 2 {
|
if state.snapshot().releaseCreates != 1 || state.snapshot().assetUploads != 2 {
|
||||||
t.Fatalf("binary calls = creates:%d uploads:%d", state.releaseCreates, state.assetUploads)
|
t.Fatalf("binary calls = creates:%d uploads:%d", state.snapshot().releaseCreates, state.snapshot().assetUploads)
|
||||||
}
|
}
|
||||||
if _, ok := state.assets["helper-v1.2.3-"+runtime.GOOS+"-"+runtime.GOARCH]; !ok {
|
if _, ok := state.snapshot().assets["helper-v1.2.3-"+runtime.GOOS+"-"+runtime.GOARCH]; !ok {
|
||||||
t.Fatalf("binary asset was not delivered: %#v", state.assets)
|
t.Fatalf("binary asset was not delivered: %#v", state.snapshot().assets)
|
||||||
}
|
}
|
||||||
if _, ok := state.assets["SHA256SUMS"]; !ok {
|
if _, ok := state.snapshot().assets["SHA256SUMS"]; !ok {
|
||||||
t.Fatalf("checksum manifest was not delivered: %#v", state.assets)
|
t.Fatalf("checksum manifest was not delivered: %#v", state.snapshot().assets)
|
||||||
}
|
}
|
||||||
assertModuleBootstrap(t, repository, goLog, moduleBefore, sumBefore)
|
assertModuleBootstrap(t, repository, goLog, moduleBefore, sumBefore)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ func TestDeliverReadsLocalBinaryOnceForVerificationAndOnceForUpload(t *testing.T
|
|||||||
}
|
}
|
||||||
state := &deliveryBenchmarkServer{commit: build.CandidateCommit, assets: expected}
|
state := &deliveryBenchmarkServer{commit: build.CandidateCommit, assets: expected}
|
||||||
state.reset(false)
|
state.reset(false)
|
||||||
server := httptest.NewServer(http.HandlerFunc(state.serveHTTP))
|
server := httptest.NewUnstartedServer(http.HandlerFunc(state.serveHTTP))
|
||||||
|
state.baseURL = "http://" + server.Listener.Addr().String()
|
||||||
|
server.Start()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
state.baseURL = server.URL
|
|
||||||
result, err := releasekit.Deliver(t.Context(), releasekit.DeliveryRequest{
|
result, err := releasekit.Deliver(t.Context(), releasekit.DeliveryRequest{
|
||||||
RepositoryDir: repository, Product: "Sample App", Version: build.Version,
|
RepositoryDir: repository, Product: "Sample App", Version: build.Version,
|
||||||
Destination: facadeDeliveryDestination(server.URL), Prebuilt: &build,
|
Destination: facadeDeliveryDestination(server.URL), Prebuilt: &build,
|
||||||
|
|||||||
170
delivery_test.go
170
delivery_test.go
@@ -13,6 +13,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
releasekit "gitea.maximumdirect.net/eric/ReleaseKit"
|
releasekit "gitea.maximumdirect.net/eric/ReleaseKit"
|
||||||
@@ -41,11 +42,12 @@ func TestDeliverManagedBuildUploadsArtifactsAndCleansTemporaryOutput(t *testing.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Deliver() error = %v; result = %#v", err, result)
|
t.Fatalf("Deliver() error = %v; result = %#v", err, result)
|
||||||
}
|
}
|
||||||
if result.State != releasekit.DeliveryComplete || result.CandidateCommit != remote.commit {
|
observed := remote.snapshot()
|
||||||
|
if result.State != releasekit.DeliveryComplete || result.CandidateCommit != observed.commit {
|
||||||
t.Fatalf("delivery result = %#v", result)
|
t.Fatalf("delivery result = %#v", result)
|
||||||
}
|
}
|
||||||
if len(result.Uploaded) != 2 || len(remote.assets) != 2 {
|
if len(result.Uploaded) != 2 || len(observed.assets) != 2 {
|
||||||
t.Fatalf("uploaded = %#v; hosted assets = %v", result.Uploaded, mapKeys(remote.assets))
|
t.Fatalf("uploaded = %#v; hosted assets = %v", result.Uploaded, mapKeys(observed.assets))
|
||||||
}
|
}
|
||||||
if !strings.Contains(progress.String(), "checking fixture version output") {
|
if !strings.Contains(progress.String(), "checking fixture version output") {
|
||||||
t.Fatalf("build progress = %q", progress.String())
|
t.Fatalf("build progress = %q", progress.String())
|
||||||
@@ -79,7 +81,8 @@ func TestDeliverBinaryConsumerWithBuildinfoAndSelectedTargets(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Deliver() error = %v; result = %#v", err, result)
|
t.Fatalf("Deliver() error = %v; result = %#v", err, result)
|
||||||
}
|
}
|
||||||
if result.State != releasekit.DeliveryComplete || result.CandidateCommit != remote.commit {
|
observed := remote.snapshot()
|
||||||
|
if result.State != releasekit.DeliveryComplete || result.CandidateCommit != observed.commit {
|
||||||
t.Fatalf("delivery result = %#v", result)
|
t.Fatalf("delivery result = %#v", result)
|
||||||
}
|
}
|
||||||
wantAssets := []string{
|
wantAssets := []string{
|
||||||
@@ -87,12 +90,12 @@ func TestDeliverBinaryConsumerWithBuildinfoAndSelectedTargets(t *testing.T) {
|
|||||||
"fixture-v1.2.3-linux-arm64",
|
"fixture-v1.2.3-linux-arm64",
|
||||||
"SHA256SUMS",
|
"SHA256SUMS",
|
||||||
}
|
}
|
||||||
if len(result.Uploaded) != len(wantAssets) || len(remote.assets) != len(wantAssets) {
|
if len(result.Uploaded) != len(wantAssets) || len(observed.assets) != len(wantAssets) {
|
||||||
t.Fatalf("uploaded = %#v; hosted assets = %v", result.Uploaded, mapKeys(remote.assets))
|
t.Fatalf("uploaded = %#v; hosted assets = %v", result.Uploaded, mapKeys(observed.assets))
|
||||||
}
|
}
|
||||||
for _, name := range wantAssets {
|
for _, name := range wantAssets {
|
||||||
if _, ok := remote.assets[name]; !ok {
|
if _, ok := observed.assets[name]; !ok {
|
||||||
t.Errorf("hosted assets omit %q: %v", name, mapKeys(remote.assets))
|
t.Errorf("hosted assets omit %q: %v", name, mapKeys(observed.assets))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !strings.Contains(progress.String(), "checking fixture version output") {
|
if !strings.Contains(progress.String(), "checking fixture version output") {
|
||||||
@@ -174,8 +177,8 @@ func TestDeliverRejectsSameSizePrebuiltTamperingBeforeHostedWrites(t *testing.T)
|
|||||||
if err == nil || !strings.Contains(err.Error(), "verify build assets") || result.State != releasekit.DeliveryNotAttempted {
|
if err == nil || !strings.Contains(err.Error(), "verify build assets") || result.State != releasekit.DeliveryNotAttempted {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want local asset verification failure", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want local asset verification failure", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none before local verification", remote.requests)
|
t.Fatalf("hosted requests = %d, want none before local verification", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,8 +220,8 @@ func TestDeliverPageOnlySkipsBuildAndHostedAssets(t *testing.T) {
|
|||||||
if err != nil || result.State != releasekit.DeliveryComplete {
|
if err != nil || result.State != releasekit.DeliveryComplete {
|
||||||
t.Fatalf("Deliver() = %#v, %v", result, err)
|
t.Fatalf("Deliver() = %#v, %v", result, err)
|
||||||
}
|
}
|
||||||
if len(remote.assets) != 0 || len(result.Uploaded) != 0 || len(result.Pending) != 0 {
|
if len(remote.snapshot().assets) != 0 || len(result.Uploaded) != 0 || len(result.Pending) != 0 {
|
||||||
t.Fatalf("page-only delivery included assets: result=%#v assets=%v", result, mapKeys(remote.assets))
|
t.Fatalf("page-only delivery included assets: result=%#v assets=%v", result, mapKeys(remote.snapshot().assets))
|
||||||
}
|
}
|
||||||
if progress.Len() != 0 {
|
if progress.Len() != 0 {
|
||||||
t.Fatalf("page-only delivery emitted build progress: %q", progress.String())
|
t.Fatalf("page-only delivery emitted build progress: %q", progress.String())
|
||||||
@@ -242,8 +245,8 @@ func TestDeliverUsesCommittedModuleIdentityForSeparatePages(t *testing.T) {
|
|||||||
ExpectedTag: "integrations/wrong/v1.2.3",
|
ExpectedTag: "integrations/wrong/v1.2.3",
|
||||||
Destination: facadeDeliveryDestination(server.URL),
|
Destination: facadeDeliveryDestination(server.URL),
|
||||||
})
|
})
|
||||||
if err == nil || wrongTag.Tag != "v1.2.3" || remote.requests != 0 {
|
if err == nil || wrongTag.Tag != "v1.2.3" || remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("delivery with inconsistent expected tag = %#v, %v; hosted requests %d", wrongTag, err, remote.requests)
|
t.Fatalf("delivery with inconsistent expected tag = %#v, %v; hosted requests %d", wrongTag, err, remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootResult, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
rootResult, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
||||||
@@ -258,8 +261,10 @@ func TestDeliverUsesCommittedModuleIdentityForSeparatePages(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nestedTag := "integrations/ckconfig/v1.2.3"
|
nestedTag := "integrations/ckconfig/v1.2.3"
|
||||||
remote.tag = nestedTag
|
remote.update(func(remote *facadeDeliveryData) {
|
||||||
remote.noteBody = "# Fixture v1.2.3\n\nNested delivery fixture.\n"
|
remote.tag = nestedTag
|
||||||
|
remote.noteBody = "# Fixture v1.2.3\n\nNested delivery fixture.\n"
|
||||||
|
})
|
||||||
nestedResult, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
nestedResult, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
||||||
RepositoryDir: repository,
|
RepositoryDir: repository,
|
||||||
ModuleDir: "integrations/ckconfig",
|
ModuleDir: "integrations/ckconfig",
|
||||||
@@ -271,8 +276,8 @@ func TestDeliverUsesCommittedModuleIdentityForSeparatePages(t *testing.T) {
|
|||||||
if err != nil || nestedResult.State != releasekit.DeliveryComplete || nestedResult.Tag != nestedTag || nestedResult.ModulePath != "example.com/ckconfig" {
|
if err != nil || nestedResult.State != releasekit.DeliveryComplete || nestedResult.Tag != nestedTag || nestedResult.ModulePath != "example.com/ckconfig" {
|
||||||
t.Fatalf("nested delivery = %#v, %v", nestedResult, err)
|
t.Fatalf("nested delivery = %#v, %v", nestedResult, err)
|
||||||
}
|
}
|
||||||
if len(remote.pages) != 2 || !remote.pages["v1.2.3"] || !remote.pages[nestedTag] {
|
if len(remote.snapshot().pages) != 2 || !remote.snapshot().pages["v1.2.3"] || !remote.snapshot().pages[nestedTag] {
|
||||||
t.Fatalf("created hosted pages = %#v", remote.pages)
|
t.Fatalf("created hosted pages = %#v", remote.snapshot().pages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,21 +298,21 @@ func TestDeliverRejectsPrebuiltArtifactsForAnotherModuleBeforeHostedRequests(t *
|
|||||||
}
|
}
|
||||||
server, remote := newFacadeDeliveryServer(t, repository)
|
server, remote := newFacadeDeliveryServer(t, repository)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
remote.tag = "integrations/ckconfig/v1.2.3"
|
remote.update(func(remote *facadeDeliveryData) { remote.tag = "integrations/ckconfig/v1.2.3" })
|
||||||
result, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
result, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
||||||
RepositoryDir: repository,
|
RepositoryDir: repository,
|
||||||
ModuleDir: "integrations/ckconfig",
|
ModuleDir: "integrations/ckconfig",
|
||||||
Product: "Fixture",
|
Product: "Fixture",
|
||||||
Version: "v1.2.3",
|
Version: "v1.2.3",
|
||||||
ExpectedTag: remote.tag,
|
ExpectedTag: remote.snapshot().tag,
|
||||||
Destination: facadeDeliveryDestination(server.URL),
|
Destination: facadeDeliveryDestination(server.URL),
|
||||||
Prebuilt: &prebuilt,
|
Prebuilt: &prebuilt,
|
||||||
})
|
})
|
||||||
if err == nil || !strings.Contains(err.Error(), "selected tagged module") || result.Tag != remote.tag {
|
if err == nil || !strings.Contains(err.Error(), "selected tagged module") || result.Tag != remote.snapshot().tag {
|
||||||
t.Fatalf("nested delivery with root artifacts = %#v, %v", result, err)
|
t.Fatalf("nested delivery with root artifacts = %#v, %v", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests before prebuilt identity rejection = %d", remote.requests)
|
t.Fatalf("hosted requests before prebuilt identity rejection = %d", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,14 +356,14 @@ func TestDeliverRejectsDirtyTaggedSourceBeforeBuildOrHostedWrites(t *testing.T)
|
|||||||
},
|
},
|
||||||
Output: &progress,
|
Output: &progress,
|
||||||
})
|
})
|
||||||
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.commit {
|
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.snapshot().commit {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want preflight failure for known commit", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want preflight failure for known commit", result, err)
|
||||||
}
|
}
|
||||||
if progress.Len() != 0 {
|
if progress.Len() != 0 {
|
||||||
t.Fatalf("build started before source eligibility passed: %q", progress.String())
|
t.Fatalf("build started before source eligibility passed: %q", progress.String())
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,8 +397,8 @@ func TestDeliverRejectsCheckoutAwayFromTagBeforeBuildOrHostedWrites(t *testing.T
|
|||||||
if progress.Len() != 0 {
|
if progress.Len() != 0 {
|
||||||
t.Fatalf("build started before tag/HEAD agreement: %q", progress.String())
|
t.Fatalf("build started before tag/HEAD agreement: %q", progress.String())
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,14 +422,14 @@ func TestDeliverRejectsInvalidCommittedNoteBeforeBuildOrHostedWrites(t *testing.
|
|||||||
},
|
},
|
||||||
Output: &progress,
|
Output: &progress,
|
||||||
})
|
})
|
||||||
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.commit {
|
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.snapshot().commit {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want committed-note preflight failure", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want committed-note preflight failure", result, err)
|
||||||
}
|
}
|
||||||
if progress.Len() != 0 {
|
if progress.Len() != 0 {
|
||||||
t.Fatalf("build started before committed-note validation: %q", progress.String())
|
t.Fatalf("build started before committed-note validation: %q", progress.String())
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +463,8 @@ func TestDeliverManagedBuildRejectsCheckoutMutationBeforeHostedWrites(t *testing
|
|||||||
if err == nil || !strings.Contains(err.Error(), "build source") || result.State != releasekit.DeliveryNotAttempted {
|
if err == nil || !strings.Contains(err.Error(), "build source") || result.State != releasekit.DeliveryNotAttempted {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want build-source eligibility failure", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want build-source eligibility failure", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
||||||
t.Fatalf("ineligible managed build output remains: entries=%v error=%v", entries, err)
|
t.Fatalf("ineligible managed build output remains: entries=%v error=%v", entries, err)
|
||||||
@@ -484,11 +489,11 @@ func TestDeliverManagedBuildFailureCleansTemporaryOutputBeforeHostedWrites(t *te
|
|||||||
Targets: []releasekit.Target{{OS: runtime.GOOS, Arch: runtime.GOARCH}},
|
Targets: []releasekit.Target{{OS: runtime.GOOS, Arch: runtime.GOARCH}},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.commit {
|
if err == nil || result.State != releasekit.DeliveryNotAttempted || result.CandidateCommit != remote.snapshot().commit {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want managed build failure before hosted writes", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want managed build failure before hosted writes", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
||||||
t.Fatalf("failed managed build output remains: entries=%v error=%v", entries, err)
|
t.Fatalf("failed managed build output remains: entries=%v error=%v", entries, err)
|
||||||
@@ -520,8 +525,8 @@ func TestDeliverManagedBuildCancellationCleansTemporaryOutput(t *testing.T) {
|
|||||||
if !errors.Is(err, context.Canceled) || result.State != releasekit.DeliveryNotAttempted {
|
if !errors.Is(err, context.Canceled) || result.State != releasekit.DeliveryNotAttempted {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want cancellation before hosted writes", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want cancellation before hosted writes", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
if entries, err := os.ReadDir(temporaryRoot); err != nil || len(entries) != 0 {
|
||||||
t.Fatalf("canceled managed build output remains: entries=%v error=%v", entries, err)
|
t.Fatalf("canceled managed build output remains: entries=%v error=%v", entries, err)
|
||||||
@@ -544,8 +549,8 @@ func TestDeliverRejectsBothBuildSourcesBeforeHostedWrites(t *testing.T) {
|
|||||||
if err == nil || result.State != releasekit.DeliveryNotAttempted || !strings.Contains(err.Error(), "both") {
|
if err == nil || result.State != releasekit.DeliveryNotAttempted || !strings.Contains(err.Error(), "both") {
|
||||||
t.Fatalf("Deliver() = %#v, %v; want invalid build-source combination", result, err)
|
t.Fatalf("Deliver() = %#v, %v; want invalid build-source combination", result, err)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("hosted requests = %d, want none", remote.requests)
|
t.Fatalf("hosted requests = %d, want none", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,6 +587,35 @@ func (writer deliveryCancelWriter) Write(content []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type facadeDeliveryServer struct {
|
type facadeDeliveryServer struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
data facadeDeliveryData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *facadeDeliveryServer) update(change func(*facadeDeliveryData)) {
|
||||||
|
state.mu.Lock()
|
||||||
|
defer state.mu.Unlock()
|
||||||
|
change(&state.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *facadeDeliveryServer) snapshot() facadeDeliveryData {
|
||||||
|
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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.pages = make(map[string]bool, len(state.data.pages))
|
||||||
|
for key, value := range state.data.pages {
|
||||||
|
result.pages[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type facadeDeliveryData struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
commit string
|
commit string
|
||||||
tag string
|
tag string
|
||||||
@@ -595,24 +629,28 @@ type facadeDeliveryServer struct {
|
|||||||
|
|
||||||
func newFacadeDeliveryServer(t *testing.T, repository string) (*httptest.Server, *facadeDeliveryServer) {
|
func newFacadeDeliveryServer(t *testing.T, repository string) (*httptest.Server, *facadeDeliveryServer) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
state := &facadeDeliveryServer{t: t, commit: buildGit(t, repository, "rev-parse", "HEAD"), tag: "v1.2.3", version: "v1.2.3", noteBody: "# Fixture v1.2.3\n\nDelivery fixture.\n", pages: make(map[string]bool), assets: make(map[string][]byte)}
|
state := &facadeDeliveryServer{data: facadeDeliveryData{t: t, commit: buildGit(t, repository, "rev-parse", "HEAD"), tag: "v1.2.3", version: "v1.2.3", noteBody: "# Fixture v1.2.3\n\nDelivery fixture.\n", pages: make(map[string]bool), assets: make(map[string][]byte)}}
|
||||||
server := httptest.NewServer(http.HandlerFunc(state.serveHTTP))
|
server := httptest.NewUnstartedServer(http.HandlerFunc(state.serveHTTP))
|
||||||
state.url = server.URL
|
state.data.url = "http://" + server.Listener.Addr().String()
|
||||||
|
server.Start()
|
||||||
return server, state
|
return server, state
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
state.mu.Lock()
|
||||||
|
defer state.mu.Unlock()
|
||||||
|
data := &state.data
|
||||||
if strings.HasPrefix(r.URL.Path, "/api/") {
|
if strings.HasPrefix(r.URL.Path, "/api/") {
|
||||||
state.requests++
|
data.requests++
|
||||||
if r.Header.Get("Authorization") != "token fixture-token" {
|
if r.Header.Get("Authorization") != "token fixture-token" {
|
||||||
state.t.Errorf("authorization = %q", r.Header.Get("Authorization"))
|
data.t.Errorf("authorization = %q", r.Header.Get("Authorization"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch r.URL.EscapedPath() {
|
switch r.URL.EscapedPath() {
|
||||||
case "/api/v1/repos/owner/example/tags/" + url.PathEscape(state.tag):
|
case "/api/v1/repos/owner/example/tags/" + url.PathEscape(data.tag):
|
||||||
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, state.tag, state.commit)
|
_, _ = fmt.Fprintf(w, `{"name":%q,"commit":{"sha":%q}}`, data.tag, data.commit)
|
||||||
case "/api/v1/repos/owner/example/releases/tags/" + url.PathEscape(state.tag):
|
case "/api/v1/repos/owner/example/releases/tags/" + url.PathEscape(data.tag):
|
||||||
if !state.pages[state.tag] {
|
if !data.pages[data.tag] {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -625,20 +663,20 @@ func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Requ
|
|||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
state.t.Errorf("decode release payload: %v", err)
|
data.t.Errorf("decode release payload: %v", err)
|
||||||
http.Error(w, "invalid payload", http.StatusBadRequest)
|
http.Error(w, "invalid payload", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if payload.Tag != state.tag || payload.Target != state.commit || payload.Name != "Fixture "+state.version {
|
if payload.Tag != data.tag || payload.Target != data.commit || payload.Name != "Fixture "+data.version {
|
||||||
state.t.Errorf("release payload = %#v", payload)
|
data.t.Errorf("release payload = %#v", payload)
|
||||||
}
|
}
|
||||||
state.pages[state.tag] = true
|
data.pages[data.tag] = true
|
||||||
state.writeRelease(w)
|
state.writeRelease(w)
|
||||||
case "/api/v1/repos/owner/example/releases/10/assets":
|
case "/api/v1/repos/owner/example/releases/10/assets":
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
list := make([]map[string]any, 0, len(state.assets))
|
list := make([]map[string]any, 0, len(data.assets))
|
||||||
for name, content := range state.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": state.url + "/files/" + name})
|
list = append(list, map[string]any{"id": len(list) + 1, "name": name, "size": len(content), "browser_download_url": data.url + "/files/" + name})
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(list)
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
return
|
return
|
||||||
@@ -646,22 +684,22 @@ func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Requ
|
|||||||
name := r.URL.Query().Get("name")
|
name := r.URL.Query().Get("name")
|
||||||
file, _, err := r.FormFile("attachment")
|
file, _, err := r.FormFile("attachment")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.t.Errorf("read upload %q: %v", name, err)
|
data.t.Errorf("read upload %q: %v", name, err)
|
||||||
http.Error(w, "invalid multipart upload", http.StatusBadRequest)
|
http.Error(w, "invalid multipart upload", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
content, err := io.ReadAll(file)
|
content, err := io.ReadAll(file)
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.t.Errorf("read upload %q: %v", name, err)
|
data.t.Errorf("read upload %q: %v", name, err)
|
||||||
}
|
}
|
||||||
state.assets[name] = content
|
data.assets[name] = content
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": len(state.assets), "name": name, "size": len(content)})
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": len(data.assets), "name": name, "size": len(content)})
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(r.URL.Path, "/files/") {
|
if strings.HasPrefix(r.URL.Path, "/files/") {
|
||||||
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
name := strings.TrimPrefix(r.URL.Path, "/files/")
|
||||||
content, ok := state.assets[name]
|
content, ok := data.assets[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
@@ -669,17 +707,19 @@ func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Requ
|
|||||||
_, _ = w.Write(content)
|
_, _ = w.Write(content)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.t.Errorf("unexpected delivery request %s %s", r.Method, r.URL)
|
data.t.Errorf("unexpected delivery request %s %s", r.Method, r.URL)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *facadeDeliveryServer) writeRelease(w http.ResponseWriter) {
|
func (state *facadeDeliveryServer) writeRelease(w http.ResponseWriter) {
|
||||||
|
// The request handler holds state.mu.
|
||||||
|
data := &state.data
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
"id": 10, "html_url": state.url + "/releases/10", "tag_name": state.tag,
|
"id": 10, "html_url": data.url + "/releases/10", "tag_name": data.tag,
|
||||||
"target_commitish": state.commit, "name": "Fixture " + state.version,
|
"target_commitish": data.commit, "name": "Fixture " + data.version,
|
||||||
"body": state.noteBody, "draft": false, "prerelease": false,
|
"body": data.noteBody, "draft": false, "prerelease": false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
docs/releases/v0.4.1.md
Normal file
29
docs/releases/v0.4.1.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# ReleaseKit v0.4.1
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This patch synchronizes shared state in the delivery and Gitea HTTP test
|
||||||
|
fixtures. It makes the test suite reliable under concurrent request handling
|
||||||
|
and the Go race detector. Production code and public behavior are unchanged.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Protect delivery fixture state with mutexes and independent snapshots; use
|
||||||
|
atomic observations for independent Gitea fixture counters and flags.
|
||||||
|
- Keep fatal test assertions in the test goroutine and ensure release test
|
||||||
|
servers close with their owning tests.
|
||||||
|
- Preserve existing delivery, conflict, retry, and source-tag assertions while
|
||||||
|
exercising the synchronized fixtures.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
There are no API, configuration, or runtime behavior changes, and no consumer
|
||||||
|
migration is required. The Go minimum remains 1.26.0. The published v0.4.0 tag
|
||||||
|
and its contents are unchanged.
|
||||||
|
|
||||||
|
Consumers may upgrade to v0.4.1 with their existing API, profile, and workflow
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/COPYRIGHT) |
|
||||||
|
[LICENSE](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/LICENSE) |
|
||||||
|
[ATTRIBUTION](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/oss/attribution.md)
|
||||||
@@ -47,7 +47,8 @@ func TestInspectAssetsAcceptsMatchingContentAndPreservesUnrelatedFiles(t *testin
|
|||||||
case "/files/notes":
|
case "/files/notes":
|
||||||
_, _ = w.Write([]byte("unrelated"))
|
_, _ = w.Write([]byte("unrelated"))
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected path %s", r.URL.Path)
|
t.Errorf("unexpected path %s", r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -107,7 +108,8 @@ func TestInspectAssetsAcceptsFullManifestWhenBinariesAreMissing(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.URL.Path != "/files/manifest" {
|
if r.URL.Path != "/files/manifest" {
|
||||||
t.Fatalf("unexpected path %s", r.URL.Path)
|
t.Errorf("unexpected path %s", r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
_, _ = w.Write(manifest.Content)
|
_, _ = w.Write(manifest.Content)
|
||||||
}))
|
}))
|
||||||
@@ -237,7 +239,8 @@ func TestInspectAssetsFollowsSameOriginRedirectWithAuthorization(t *testing.T) {
|
|||||||
_, _ = w.Write(content)
|
_, _ = w.Write(content)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Fatalf("unexpected path %s", r.URL.Path)
|
t.Errorf("unexpected path %s", r.URL.Path)
|
||||||
|
return
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
inspection, err := testClient(server).InspectAssets(context.Background(), testDestination(server.URL), 10, []ExpectedAsset{expected})
|
inspection, err := testClient(server).InspectAssets(context.Background(), testDestination(server.URL), 10, []ExpectedAsset{expected})
|
||||||
@@ -253,9 +256,9 @@ func TestInspectAssetsVerifiesExternalContentWithoutGiteaToken(t *testing.T) {
|
|||||||
for _, mode := range []string{"direct", "redirect", "conflicting"} {
|
for _, mode := range []string{"direct", "redirect", "conflicting"} {
|
||||||
t.Run(mode, func(t *testing.T) {
|
t.Run(mode, func(t *testing.T) {
|
||||||
expected := assetFor("app.bin", []byte("matching bytes"))
|
expected := assetFor("app.bin", []byte("matching bytes"))
|
||||||
externalGets := 0
|
var externalGets atomic.Int64
|
||||||
external := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
external := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
externalGets++
|
externalGets.Add(1)
|
||||||
if r.Header.Get("Authorization") != "" {
|
if r.Header.Get("Authorization") != "" {
|
||||||
t.Error("Gitea token reached external host")
|
t.Error("Gitea token reached external host")
|
||||||
}
|
}
|
||||||
@@ -287,8 +290,8 @@ func TestInspectAssetsVerifiesExternalContentWithoutGiteaToken(t *testing.T) {
|
|||||||
if mode == "conflicting" {
|
if mode == "conflicting" {
|
||||||
want = AssetConflicting
|
want = AssetConflicting
|
||||||
}
|
}
|
||||||
if inspection.Assets[0].State != want || externalGets != 1 {
|
if inspection.Assets[0].State != want || externalGets.Load() != 1 {
|
||||||
t.Fatalf("inspection=%#v downloads=%d", inspection, externalGets)
|
t.Fatalf("inspection=%#v downloads=%d", inspection, externalGets.Load())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,19 +41,23 @@ func TestEnsureReleaseEscapesSlashContainingTag(t *testing.T) {
|
|||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
case "/api/v1/repos/owner/project/releases":
|
case "/api/v1/repos/owner/project/releases":
|
||||||
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
||||||
state.posts++
|
state.posts.Add(1)
|
||||||
var payload createReleaseRequest
|
var payload createReleaseRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if payload.TagName != tag || payload.Name != "Example v1.2.3" || payload.Body != testNote {
|
if payload.TagName != tag || payload.Name != "Example v1.2.3" || payload.Body != testNote {
|
||||||
t.Fatalf("create payload = %#v", payload)
|
t.Errorf("create payload = %#v", payload)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := json.NewEncoder(w).Encode(releaseResponse{ID: 7, HTMLURL: "https://example.test/release", TagName: tag, TargetCommitish: testCommit, Name: payload.Name, Body: payload.Body}); err != nil {
|
if err := json.NewEncoder(w).Encode(releaseResponse{ID: 7, HTMLURL: "https://example.test/release", TagName: tag, TargetCommitish: testCommit, Name: payload.Name, Body: payload.Body}); err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected escaped request %s %s", r.Method, r.URL.EscapedPath())
|
t.Errorf("unexpected escaped request %s %s", r.Method, r.URL.EscapedPath())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -60,8 +65,8 @@ func TestEnsureReleaseEscapesSlashContainingTag(t *testing.T) {
|
|||||||
request := testRequest(server.URL)
|
request := testRequest(server.URL)
|
||||||
request.Tag = tag
|
request.Tag = tag
|
||||||
result, err := testClient(server).EnsureRelease(context.Background(), request)
|
result, err := testClient(server).EnsureRelease(context.Background(), request)
|
||||||
if err != nil || !result.Created || state.posts != 1 {
|
if err != nil || !result.Created || state.posts.Load() != 1 {
|
||||||
t.Fatalf("nested tag release = %#v, posts %d, error %v", result, state.posts, err)
|
t.Fatalf("nested tag release = %#v, posts %d, error %v", result, state.posts.Load(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +87,7 @@ func TestEnsureReleaseConflictDoesNotEditExistingPage(t *testing.T) {
|
|||||||
if failureKind(t, err) != FailureConflict {
|
if failureKind(t, err) != FailureConflict {
|
||||||
t.Fatalf("error = %v", err)
|
t.Fatalf("error = %v", err)
|
||||||
}
|
}
|
||||||
if got := state.posts; got != 0 {
|
if got := state.posts.Load(); got != 0 {
|
||||||
t.Fatalf("release creates = %d, want 0", got)
|
t.Fatalf("release creates = %d, want 0", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,68 +108,68 @@ func TestEnsureReleaseUnauthorizedIsStructuredAndHidesToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseMalformedTagResponseStopsBeforeCreate(t *testing.T) {
|
func TestEnsureReleaseMalformedTagResponseStopsBeforeCreate(t *testing.T) {
|
||||||
posts := 0
|
var posts atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
posts++
|
posts.Add(1)
|
||||||
}
|
}
|
||||||
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
||||||
_, _ = w.Write([]byte("{"))
|
_, _ = w.Write([]byte("{"))
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
||||||
if failureKind(t, err) != FailureMalformed || posts != 0 {
|
if failureKind(t, err) != FailureMalformed || posts.Load() != 0 {
|
||||||
t.Fatalf("error = %v, posts = %d", err, posts)
|
t.Fatalf("error = %v, posts = %d", err, posts.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseWrongHostedTagStopsBeforeCreate(t *testing.T) {
|
func TestEnsureReleaseWrongHostedTagStopsBeforeCreate(t *testing.T) {
|
||||||
posts := 0
|
var posts atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
posts++
|
posts.Add(1)
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(w, `{"commit":{"sha":"%040d"}}`, 7)
|
_, _ = fmt.Fprintf(w, `{"commit":{"sha":"%040d"}}`, 7)
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
||||||
if failureKind(t, err) != FailureConflict || posts != 0 {
|
if failureKind(t, err) != FailureConflict || posts.Load() != 0 {
|
||||||
t.Fatalf("error = %v, posts = %d", err, posts)
|
t.Fatalf("error = %v, posts = %d", err, posts.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseMissingHostedTagStopsBeforeCreate(t *testing.T) {
|
func TestEnsureReleaseMissingHostedTagStopsBeforeCreate(t *testing.T) {
|
||||||
posts := 0
|
var posts atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
assertRequest(t, r, http.MethodGet, "/api/v1/repos/owner/project/tags/v1.2.3")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
posts++
|
posts.Add(1)
|
||||||
}
|
}
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
||||||
if failureKind(t, err) != FailureNotFound || posts != 0 {
|
if failureKind(t, err) != FailureNotFound || posts.Load() != 0 {
|
||||||
t.Fatalf("error = %v, posts = %d", err, posts)
|
t.Fatalf("error = %v, posts = %d", err, posts.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseHonorsContextCancellation(t *testing.T) {
|
func TestEnsureReleaseHonorsContextCancellation(t *testing.T) {
|
||||||
called := false
|
var called atomic.Bool
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { called = true }))
|
server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { called.Store(true) }))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
cancel()
|
cancel()
|
||||||
_, err := testClient(server).EnsureRelease(ctx, testRequest(server.URL))
|
_, err := testClient(server).EnsureRelease(ctx, testRequest(server.URL))
|
||||||
if !errors.Is(err, context.Canceled) || called {
|
if !errors.Is(err, context.Canceled) || called.Load() {
|
||||||
t.Fatalf("error = %v, server called = %t", err, called)
|
t.Fatalf("error = %v, server called = %t", err, called.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseResolvesLostCreateAcknowledgmentWithOneLookup(t *testing.T) {
|
func TestEnsureReleaseResolvesLostCreateAcknowledgmentWithOneLookup(t *testing.T) {
|
||||||
created := false
|
var created atomic.Bool
|
||||||
lookups := 0
|
var lookups atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assertAuth(t, r)
|
assertAuth(t, r)
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
@@ -173,8 +178,8 @@ func TestEnsureReleaseResolvesLostCreateAcknowledgmentWithOneLookup(t *testing.T
|
|||||||
writeTag(t, w, testCommit)
|
writeTag(t, w, testCommit)
|
||||||
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
||||||
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
||||||
lookups++
|
lookups.Add(1)
|
||||||
if !created {
|
if !created.Load() {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -183,23 +188,28 @@ func TestEnsureReleaseResolvesLostCreateAcknowledgmentWithOneLookup(t *testing.T
|
|||||||
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
||||||
var payload createReleaseRequest
|
var payload createReleaseRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if payload.TagName != "v1.2.3" || payload.TargetCommitish != testCommit || payload.Name != "Example v1.2.3" || payload.Body != testNote || payload.Draft || payload.Prerelease {
|
if payload.TagName != "v1.2.3" || payload.TargetCommitish != testCommit || payload.Name != "Example v1.2.3" || payload.Body != testNote || payload.Draft || payload.Prerelease {
|
||||||
t.Fatalf("create payload = %#v", payload)
|
t.Errorf("create payload = %#v", payload)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
created = true
|
created.Store(true)
|
||||||
hijacker, ok := w.(http.Hijacker)
|
hijacker, ok := w.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("HTTP writer does not support hijacking")
|
t.Error("HTTP writer does not support hijacking")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
connection, _, err := hijacker.Hijack()
|
connection, _, err := hijacker.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
_ = connection.Close()
|
_ = connection.Close()
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -207,13 +217,13 @@ func TestEnsureReleaseResolvesLostCreateAcknowledgmentWithOneLookup(t *testing.T
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if result.Created || lookups != 2 {
|
if result.Created || lookups.Load() != 2 {
|
||||||
t.Fatalf("result = %#v, release lookups = %d", result, lookups)
|
t.Fatalf("result = %#v, release lookups = %d", result, lookups.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseResolvesCreateConflictWithOneLookup(t *testing.T) {
|
func TestEnsureReleaseResolvesCreateConflictWithOneLookup(t *testing.T) {
|
||||||
lookups := 0
|
var lookups atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assertAuth(t, r)
|
assertAuth(t, r)
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
@@ -222,8 +232,8 @@ func TestEnsureReleaseResolvesCreateConflictWithOneLookup(t *testing.T) {
|
|||||||
writeTag(t, w, testCommit)
|
writeTag(t, w, testCommit)
|
||||||
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
||||||
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
||||||
lookups++
|
lookups.Add(1)
|
||||||
if lookups == 1 {
|
if lookups.Load() == 1 {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -232,7 +242,8 @@ func TestEnsureReleaseResolvesCreateConflictWithOneLookup(t *testing.T) {
|
|||||||
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
||||||
w.WriteHeader(http.StatusConflict)
|
w.WriteHeader(http.StatusConflict)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -240,13 +251,13 @@ func TestEnsureReleaseResolvesCreateConflictWithOneLookup(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if result.Created || result.Release.Title != "Created by another request" || lookups != 2 {
|
if result.Created || result.Release.Title != "Created by another request" || lookups.Load() != 2 {
|
||||||
t.Fatalf("result = %#v, release lookups = %d", result, lookups)
|
t.Fatalf("result = %#v, release lookups = %d", result, lookups.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureReleaseReportsUnknownWhenAmbiguousCreateCannotBeInspected(t *testing.T) {
|
func TestEnsureReleaseReportsUnknownWhenAmbiguousCreateCannotBeInspected(t *testing.T) {
|
||||||
lookups := 0
|
var lookups atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assertAuth(t, r)
|
assertAuth(t, r)
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
@@ -255,8 +266,8 @@ func TestEnsureReleaseReportsUnknownWhenAmbiguousCreateCannotBeInspected(t *test
|
|||||||
writeTag(t, w, testCommit)
|
writeTag(t, w, testCommit)
|
||||||
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
case "/api/v1/repos/owner/project/releases/tags/v1.2.3":
|
||||||
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
assertRequest(t, r, http.MethodGet, r.URL.Path)
|
||||||
lookups++
|
lookups.Add(1)
|
||||||
if lookups == 1 {
|
if lookups.Load() == 1 {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -265,13 +276,14 @@ func TestEnsureReleaseReportsUnknownWhenAmbiguousCreateCannotBeInspected(t *test
|
|||||||
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
_, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
||||||
if failureKind(t, err) != FailureUnknown || lookups != 2 {
|
if failureKind(t, err) != FailureUnknown || lookups.Load() != 2 {
|
||||||
t.Fatalf("error = %v, release lookups = %d", err, lookups)
|
t.Fatalf("error = %v, release lookups = %d", err, lookups.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +294,7 @@ type releaseFixture struct {
|
|||||||
pre bool
|
pre bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type fixtureState struct{ posts int }
|
type fixtureState struct{ posts atomic.Int64 }
|
||||||
|
|
||||||
func newReleaseServer(t *testing.T, existing releaseFixture, createMissing bool) (*httptest.Server, *fixtureState) {
|
func newReleaseServer(t *testing.T, existing releaseFixture, createMissing bool) (*httptest.Server, *fixtureState) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -302,13 +314,15 @@ func newReleaseServer(t *testing.T, existing releaseFixture, createMissing bool)
|
|||||||
writeRelease(t, w, existing)
|
writeRelease(t, w, existing)
|
||||||
case "/api/v1/repos/owner/project/releases":
|
case "/api/v1/repos/owner/project/releases":
|
||||||
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
assertRequest(t, r, http.MethodPost, r.URL.Path)
|
||||||
state.posts++
|
state.posts.Add(1)
|
||||||
var payload createReleaseRequest
|
var payload createReleaseRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if payload.TagName != "v1.2.3" || payload.TargetCommitish != testCommit || payload.Name != "Example v1.2.3" || payload.Body != testNote || payload.Draft || payload.Prerelease {
|
if payload.TagName != "v1.2.3" || payload.TargetCommitish != testCommit || payload.Name != "Example v1.2.3" || payload.Body != testNote || payload.Draft || payload.Prerelease {
|
||||||
t.Fatalf("create payload = %#v", payload)
|
t.Errorf("create payload = %#v", payload)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !createMissing {
|
if !createMissing {
|
||||||
w.WriteHeader(http.StatusConflict)
|
w.WriteHeader(http.StatusConflict)
|
||||||
@@ -316,10 +330,12 @@ func newReleaseServer(t *testing.T, existing releaseFixture, createMissing bool)
|
|||||||
}
|
}
|
||||||
writeRelease(t, w, releaseFixture{title: payload.Name})
|
writeRelease(t, w, releaseFixture{title: payload.Name})
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
|
t.Cleanup(server.Close)
|
||||||
return server, state
|
return server, state
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +362,8 @@ func (fixture releaseFixture) bodyOrDefault() string {
|
|||||||
func assertRequest(t *testing.T, request *http.Request, method, path string) {
|
func assertRequest(t *testing.T, request *http.Request, method, path string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if request.Method != method || request.URL.Path != path {
|
if request.Method != method || request.URL.Path != path {
|
||||||
t.Fatalf("request = %s %s, want %s %s", request.Method, request.URL.Path, method, path)
|
t.Errorf("request = %s %s, want %s %s", request.Method, request.URL.Path, method, path)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
assertAuth(t, request)
|
assertAuth(t, request)
|
||||||
}
|
}
|
||||||
@@ -354,7 +371,8 @@ func assertRequest(t *testing.T, request *http.Request, method, path string) {
|
|||||||
func assertAuth(t *testing.T, request *http.Request) {
|
func assertAuth(t *testing.T, request *http.Request) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if got := request.Header.Get("Authorization"); got != "token "+testToken {
|
if got := request.Header.Get("Authorization"); got != "token "+testToken {
|
||||||
t.Fatalf("Authorization = %q", got)
|
t.Errorf("Authorization = %q", got)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,14 +400,14 @@ func contains(value, substring string) bool {
|
|||||||
func TestEnsureReleaseReconcilesInconsistentSuccessfulCreate(t *testing.T) {
|
func TestEnsureReleaseReconcilesInconsistentSuccessfulCreate(t *testing.T) {
|
||||||
for _, mode := range []string{"matching", "conflicting", "unavailable"} {
|
for _, mode := range []string{"matching", "conflicting", "unavailable"} {
|
||||||
t.Run(mode, func(t *testing.T) {
|
t.Run(mode, func(t *testing.T) {
|
||||||
posts, lookups := 0, 0
|
var posts, lookups atomic.Int64
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch {
|
switch {
|
||||||
case strings.Contains(r.URL.Path, "/tags/") && !strings.Contains(r.URL.Path, "/releases/"):
|
case strings.Contains(r.URL.Path, "/tags/") && !strings.Contains(r.URL.Path, "/releases/"):
|
||||||
writeTag(t, w, testCommit)
|
writeTag(t, w, testCommit)
|
||||||
case strings.Contains(r.URL.Path, "/releases/tags/"):
|
case strings.Contains(r.URL.Path, "/releases/tags/"):
|
||||||
lookups++
|
lookups.Add(1)
|
||||||
if posts == 0 {
|
if posts.Load() == 0 {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -403,7 +421,7 @@ func TestEnsureReleaseReconcilesInconsistentSuccessfulCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
writeRelease(t, w, releaseFixture{title: "Example v1.2.3", body: body})
|
writeRelease(t, w, releaseFixture{title: "Example v1.2.3", body: body})
|
||||||
case r.Method == http.MethodPost:
|
case r.Method == http.MethodPost:
|
||||||
posts++
|
posts.Add(1)
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
json.NewEncoder(w).Encode(releaseResponse{ID: 10, TagName: "v1.2.3", Name: "inconsistent title", Body: testNote})
|
json.NewEncoder(w).Encode(releaseResponse{ID: 10, TagName: "v1.2.3", Name: "inconsistent title", Body: testNote})
|
||||||
default:
|
default:
|
||||||
@@ -412,8 +430,8 @@ func TestEnsureReleaseReconcilesInconsistentSuccessfulCreate(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
result, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
result, err := testClient(server).EnsureRelease(context.Background(), testRequest(server.URL))
|
||||||
if posts != 1 || lookups != 2 {
|
if posts.Load() != 1 || lookups.Load() != 2 {
|
||||||
t.Fatalf("posts=%d lookups=%d", posts, lookups)
|
t.Fatalf("posts=%d lookups=%d", posts.Load(), lookups.Load())
|
||||||
}
|
}
|
||||||
switch mode {
|
switch mode {
|
||||||
case "matching":
|
case "matching":
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ type deliveryBenchmarkIdentity struct {
|
|||||||
func BenchmarkPerformanceFacadeDeliver(b *testing.B) {
|
func BenchmarkPerformanceFacadeDeliver(b *testing.B) {
|
||||||
repository, build, expected := newDeliveryBenchmarkFixture(b)
|
repository, build, expected := newDeliveryBenchmarkFixture(b)
|
||||||
state := &deliveryBenchmarkServer{commit: gitText(b, repository, "rev-parse", "HEAD"), assets: expected}
|
state := &deliveryBenchmarkServer{commit: gitText(b, repository, "rev-parse", "HEAD"), assets: expected}
|
||||||
server := httptest.NewServer(http.HandlerFunc(state.serveHTTP))
|
server := httptest.NewUnstartedServer(http.HandlerFunc(state.serveHTTP))
|
||||||
state.baseURL = server.URL
|
state.baseURL = "http://" + server.Listener.Addr().String()
|
||||||
|
server.Start()
|
||||||
b.Cleanup(server.Close)
|
b.Cleanup(server.Close)
|
||||||
request := releasekit.DeliveryRequest{
|
request := releasekit.DeliveryRequest{
|
||||||
RepositoryDir: repository,
|
RepositoryDir: repository,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func TestModuleSelectionFollowsSymlinkBeforeParentAcrossOperations(t *testing.T)
|
|||||||
buildGit(t, repository, "tag", wantTag)
|
buildGit(t, repository, "tag", wantTag)
|
||||||
server, remote := newFacadeDeliveryServer(t, repository)
|
server, remote := newFacadeDeliveryServer(t, repository)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
remote.tag, remote.noteBody = wantTag, note
|
remote.update(func(remote *facadeDeliveryData) { remote.tag, remote.noteBody = wantTag, note })
|
||||||
delivered, err := releasekit.Deliver(t.Context(), releasekit.DeliveryRequest{
|
delivered, err := releasekit.Deliver(t.Context(), releasekit.DeliveryRequest{
|
||||||
RepositoryDir: repository, ModuleDir: "link/..", Product: "Fixture", Version: version,
|
RepositoryDir: repository, ModuleDir: "link/..", Product: "Fixture", Version: version,
|
||||||
ExpectedTag: wantTag, Destination: facadeDeliveryDestination(server.URL),
|
ExpectedTag: wantTag, Destination: facadeDeliveryDestination(server.URL),
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ func TestSourceOperationsRejectUnavailableNoteWithoutTransport(t *testing.T) {
|
|||||||
if checked.Commit != commit || checked.Tag != "v1.2.3" || delivered.CandidateCommit != commit || delivered.Tag != checked.Tag || delivered.State != releasekit.DeliveryNotAttempted {
|
if checked.Commit != commit || checked.Tag != "v1.2.3" || delivered.CandidateCommit != commit || delivered.Tag != checked.Tag || delivered.State != releasekit.DeliveryNotAttempted {
|
||||||
t.Fatalf("missing-note evidence: check=%#v, delivery=%#v", checked, delivered)
|
t.Fatalf("missing-note evidence: check=%#v, delivery=%#v", checked, delivered)
|
||||||
}
|
}
|
||||||
if remote.requests != 0 {
|
if remote.snapshot().requests != 0 {
|
||||||
t.Fatalf("missing note caused %d hosted requests", remote.requests)
|
t.Fatalf("missing note caused %d hosted requests", remote.snapshot().requests)
|
||||||
}
|
}
|
||||||
assertInspectionStayedLocal(t, repository, attempts, before)
|
assertInspectionStayedLocal(t, repository, attempts, before)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user