| index 75510154b..bf8bc6ca0 100644 |
| --- a/internal/release/demand_test.go |
| +++ b/internal/release/demand_test.go |
| @@ -4,7 +4,9 @@ import ( |
| "bytes" |
| "context" |
| "encoding/json" |
| + "errors" |
| "fmt" |
| + "os" |
| "strings" |
| "testing" |
| ) |
| @@ -100,3 +102,129 @@ func TestNormalizeDemand(t *testing.T) { |
| } |
| } |
| } |
| + |
| +// unreadableStore answers every Get with an error that is not "not found": a |
| +// bucket this box cannot read right now. |
| +type unreadableStore struct{ Store } |
| + |
| +func (unreadableStore) Get(context.Context, string) ([]byte, string, error) { |
| + return nil, "", errors.New("bucket unreachable") |
| +} |
| + |
| +// Spec §12 "Swap mode source": a staged copy without swap, from an older |
| +// root, cannot produce an overlap for a hold build. |
| +func TestCanonicalDemandReadsTheCanonicalManifestNotTheSibling(t *testing.T) { |
| + s := NewDirStore(t.TempDir()) |
| + ctx := context.Background() |
| + opt := t.TempDir() |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), `"swap":"hold",`) |
| + if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil { |
| + t.Fatal(err) |
| + } |
| + canon, err := GetManifest(ctx, s, "bab", "km", "v1") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + lossy := *canon |
| + lossy.Swap = "" // what an older root's re-marshal wrote |
| + b, err := json.MarshalIndent(lossy, "", " ") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := StageManifest(opt, "bab", "v1", "km", b); err != nil { |
| + t.Fatal(err) |
| + } |
| + demand, digest, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km")) |
| + if err != nil || demand != DemandHold || digest != etagOf([]byte("bin")) { |
| + t.Fatalf("CanonicalDemand = %q, %q, %v; want hold from the canonical manifest", demand, digest, err) |
| + } |
| +} |
| + |
| +func TestCanonicalDemandWithNoDemand(t *testing.T) { |
| + s := NewDirStore(t.TempDir()) |
| + opt := t.TempDir() |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "") |
| + if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil { |
| + t.Fatal(err) |
| + } |
| + demand, digest, err := CanonicalDemand(context.Background(), s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km")) |
| + if err != nil || demand != "" || digest != etagOf([]byte("bin")) { |
| + t.Fatalf("CanonicalDemand = %q, %q, %v; want no demand", demand, digest, err) |
| + } |
| +} |
| + |
| +// Spec §12 "Artifact binding": a re-shipped name with different bytes, plus |
| +// an old staged copy, is a staging error. |
| +func TestCanonicalDemandRefusesBytesTheReleaseDoesNotName(t *testing.T) { |
| + s := NewDirStore(t.TempDir()) |
| + opt := t.TempDir() |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("new bytes"), "") |
| + if err := StageArtifact(opt, "bab", "v1", "km", []byte("old bytes")); err != nil { |
| + t.Fatal(err) |
| + } |
| + demand, digest, err := CanonicalDemand(context.Background(), s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km")) |
| + if !errors.Is(err, ErrStagedDigestMismatch) { |
| + t.Fatalf("err = %v, want ErrStagedDigestMismatch", err) |
| + } |
| + if demand != "" || digest != etagOf([]byte("old bytes")) { |
| + t.Fatalf("CanonicalDemand = %q, %q; want no demand and the staged file's own digest", demand, digest) |
| + } |
| +} |
| + |
| +// Spec §4.2: if the canonical manifest cannot be read, or the digests cannot |
| +// be compared, the attempt runs in hold. Review Focus 2 is the last case. |
| +func TestCanonicalDemandIsHoldWhenItCannotCompare(t *testing.T) { |
| + ctx := context.Background() |
| + staged := func(t *testing.T, opt string) string { |
| + if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil { |
| + t.Fatal(err) |
| + } |
| + return VersionPath(opt, "bab", "v1", "km") |
| + } |
| + t.Run("manifest missing", func(t *testing.T) { |
| + opt := t.TempDir() |
| + d, sum, err := CanonicalDemand(ctx, NewDirStore(t.TempDir()), "bab", "km", "v1", adoptPlatform, staged(t, opt)) |
| + if err != nil || d != DemandHold || sum != etagOf([]byte("bin")) { |
| + t.Fatalf("= %q, %q, %v; want hold", d, sum, err) |
| + } |
| + }) |
| + t.Run("store unreadable", func(t *testing.T) { |
| + opt := t.TempDir() |
| + s := NewDirStore(t.TempDir()) |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "") |
| + d, _, err := CanonicalDemand(ctx, unreadableStore{s}, "bab", "km", "v1", adoptPlatform, staged(t, opt)) |
| + if err != nil || d != DemandHold { |
| + t.Fatalf("= %q, %v; want hold", d, err) |
| + } |
| + }) |
| + t.Run("no build for this platform", func(t *testing.T) { |
| + opt := t.TempDir() |
| + s := NewDirStore(t.TempDir()) |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "") |
| + d, _, err := CanonicalDemand(ctx, s, "bab", "km", "v1", "plan9-mips", staged(t, opt)) |
| + if err != nil || d != DemandHold { |
| + t.Fatalf("= %q, %v; want hold", d, err) |
| + } |
| + }) |
| + t.Run("staged file missing", func(t *testing.T) { |
| + s := NewDirStore(t.TempDir()) |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "") |
| + missing := VersionPath(t.TempDir(), "bab", "v1", "km") |
| + if _, err := os.Stat(missing); err == nil { |
| + t.Fatal("setup: the path exists") |
| + } |
| + d, sum, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, missing) |
| + if err != nil || d != DemandHold || sum != "" { |
| + t.Fatalf("= %q, %q, %v; want hold and no digest", d, sum, err) |
| + } |
| + }) |
| + t.Run("unknown demand", func(t *testing.T) { |
| + opt := t.TempDir() |
| + s := NewDirStore(t.TempDir()) |
| + putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), `"swap":"drain",`) |
| + d, _, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, staged(t, opt)) |
| + if err != nil || d != DemandHold { |
| + t.Fatalf("= %q, %v; want hold", d, err) |
| + } |
| + }) |
| +} |