| 1 | package release |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "errors" |
| 7 | "strings" |
| 8 | "testing" |
| 9 | ) |
| 10 | |
| 11 | func TestManifestDigestIsTheStoredBytes(t *testing.T) { |
| 12 | s := NewDirStore(t.TempDir()) |
| 13 | ctx := context.Background() |
| 14 | shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("bin one")) |
| 15 | raw, _, err := s.Get(ctx, ManifestKey("bab", "km", "v1")) |
| 16 | if err != nil { |
| 17 | t.Fatal(err) |
| 18 | } |
| 19 | m, got, err := GetManifestRaw(ctx, s, "bab", "km", "v1") |
| 20 | if err != nil || m.Version != "v1" || !bytes.Equal(got, raw) { |
| 21 | t.Fatalf("GetManifestRaw = %+v, %q, %v; want the stored bytes", m, got, err) |
| 22 | } |
| 23 | if d := ManifestDigest(raw); d != etagOf(raw) || !validDigest(d) { |
| 24 | t.Fatalf("ManifestDigest = %q, want the hex sha256 of the stored bytes", d) |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | func TestCheckManifestDigest(t *testing.T) { |
| 29 | s := NewDirStore(t.TempDir()) |
| 30 | ctx := context.Background() |
| 31 | shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("bin one")) |
| 32 | raw, _, _ := s.Get(ctx, ManifestKey("bab", "km", "v1")) |
| 33 | want := ManifestDigest(raw) |
| 34 | |
| 35 | if m, _, err := CheckManifestDigest(ctx, s, "bab", "km", "v1", want); err != nil || m == nil { |
| 36 | t.Fatalf("matching digest: %v", err) |
| 37 | } |
| 38 | if _, _, err := CheckManifestDigest(ctx, s, "bab", "km", "v1", ""); err != nil { |
| 39 | t.Fatalf("no digest asked for: %v", err) |
| 40 | } |
| 41 | other := ManifestDigest([]byte("some other manifest")) |
| 42 | if _, _, err := CheckManifestDigest(ctx, s, "bab", "km", "v1", other); !errors.Is(err, ErrManifestDigest) { |
| 43 | t.Fatalf("mismatched digest: %v, want ErrManifestDigest", err) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | func TestValidDigest(t *testing.T) { |
| 48 | for d, ok := range map[string]bool{ |
| 49 | ManifestDigest([]byte("x")): true, |
| 50 | "": false, |
| 51 | "ABCDEF" + ManifestDigest([]byte("x"))[6:]: false, // upper case is not canonical |
| 52 | ManifestDigest([]byte("x"))[:63]: false, |
| 53 | } { |
| 54 | if validDigest(d) != ok { |
| 55 | t.Errorf("validDigest(%q) = %v, want %v", d, !ok, ok) |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // failingGetStore fails every Get of one key, as a transient bucket error |
| 61 | // would, while listing still names it. |
| 62 | type failingGetStore struct { |
| 63 | Store |
| 64 | key string |
| 65 | } |
| 66 | |
| 67 | var errTransient = errors.New("transient read failure") |
| 68 | |
| 69 | func (f failingGetStore) Get(ctx context.Context, key string) ([]byte, string, error) { |
| 70 | if key == f.key { |
| 71 | return nil, "", errTransient |
| 72 | } |
| 73 | return f.Store.Get(ctx, key) |
| 74 | } |
| 75 | |
| 76 | // Astra r1 #4: a channel whose pointer cannot be read is not skipped. It |
| 77 | // comes back with Err set, so no consumer can count it as carrying digests. |
| 78 | func TestDigestReportMarksAChannelItCannotRead(t *testing.T) { |
| 79 | base := NewDirStore(t.TempDir()) |
| 80 | ctx := context.Background() |
| 81 | priv, pub := testKeys(t) |
| 82 | shipRelease(t, base, "bab", "km", "v1", adoptPlatform, []byte("one")) |
| 83 | if err := Promote(ctx, base, priv, "bab", "km", "beta", "v1"); err != nil { // no digests |
| 84 | t.Fatal(err) |
| 85 | } |
| 86 | markerOn(t, base, priv, "bab") |
| 87 | if err := Promote(ctx, base, priv, "bab", "km", "canary/x", "v1"); err != nil { // digests |
| 88 | t.Fatal(err) |
| 89 | } |
| 90 | s := failingGetStore{Store: base, key: ChannelKey("bab", "km", "beta")} |
| 91 | rows, _, err := DigestReport(ctx, s, pub, "bab", "km") |
| 92 | if err != nil || len(rows) != 2 { |
| 93 | t.Fatalf("report = %+v, %v; want both channels", rows, err) |
| 94 | } |
| 95 | got := map[string]ChannelDigestState{} |
| 96 | for _, r := range rows { |
| 97 | got[r.Channel] = r |
| 98 | } |
| 99 | if b := got["beta"]; b.Err == "" || !strings.Contains(b.Err, errTransient.Error()) || b.HeadDigest { |
| 100 | t.Fatalf("beta = %+v; want its read error, never a digest state", b) |
| 101 | } |
| 102 | if c := got["canary/x"]; c.Err != "" || !c.HeadDigest { |
| 103 | t.Fatalf("canary/x = %+v; want it read as carrying digests", c) |
| 104 | } |
| 105 | } |
| 106 | |