carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "errors"
5 "os"
6 "path/filepath"
7 "testing"
8)
9
10func TestResolveLiveReadsTheLinkOnce(t *testing.T) {
11 opt := t.TempDir()
12 if err := StageArtifact(opt, "bab", "v1", "km", []byte("build one")); err != nil {
13 t.Fatal(err)
14 }
15 if err := PointLive(opt, "h.example.com", "bab", "v1", "km"); err != nil {
16 t.Fatal(err)
17 }
18 id, err := ResolveLive(opt, "h.example.com")
19 if err != nil {
20 t.Fatalf("ResolveLive: %v", err)
21 }
22 want := StagedIdentity{Version: "v1", StagedPath: VersionPath(opt, "bab", "v1", "km"), Digest: etagOf([]byte("build one"))}
23 if id != want {
24 t.Fatalf("ResolveLive = %+v, want %+v", id, want)
25 }
26 if id.StagedPath == LiveLink(opt, "h.example.com") {
27 t.Fatal("the identity names the live link, which moves; it must name the staged file")
28 }
29}
30
31// Review Focus 1: a link an operator made by hand, outside the version store.
32func TestResolveLiveOutsideTheStoreStillIdentifiesTheBytes(t *testing.T) {
33 opt := t.TempDir()
34 elsewhere := filepath.Join(t.TempDir(), "hand-built")
35 if err := os.WriteFile(elsewhere, []byte("operator's build"), 0o755); err != nil {
36 t.Fatal(err)
37 }
38 if err := os.MkdirAll(filepath.Join(opt, "live"), 0o755); err != nil {
39 t.Fatal(err)
40 }
41 if err := os.Symlink(elsewhere, LiveLink(opt, "h.example.com")); err != nil {
42 t.Fatal(err)
43 }
44 id, err := ResolveLive(opt, "h.example.com")
45 if err != nil {
46 t.Fatalf("ResolveLive: %v", err)
47 }
48 want := StagedIdentity{Version: "", StagedPath: elsewhere, Digest: etagOf([]byte("operator's build"))}
49 if id != want {
50 t.Fatalf("ResolveLive = %+v, want %+v (no version, but the bytes still identified)", id, want)
51 }
52}
53
54// Review Focus 1: a relative link resolves against live/, and its version
55// reads exactly as LiveVersion reads it.
56func TestResolveLiveFollowsARelativeLink(t *testing.T) {
57 opt := t.TempDir()
58 if err := StageArtifact(opt, "bab", "v1", "km", []byte("b")); err != nil {
59 t.Fatal(err)
60 }
61 if err := os.MkdirAll(filepath.Join(opt, "live"), 0o755); err != nil {
62 t.Fatal(err)
63 }
64 if err := os.Symlink(filepath.Join("..", "versions", "v1", "km"), LiveLink(opt, "h.example.com")); err != nil {
65 t.Fatal(err)
66 }
67 id, err := ResolveLive(opt, "h.example.com")
68 if err != nil {
69 t.Fatalf("ResolveLive: %v", err)
70 }
71 lv, _ := LiveVersion(opt, "h.example.com")
72 if id.StagedPath != VersionPath(opt, "bab", "v1", "km") || id.Digest != etagOf([]byte("b")) || id.Version != lv {
73 t.Fatalf("ResolveLive = %+v; want path %s, the file's digest, and version %q", id, VersionPath(opt, "bab", "v1", "km"), lv)
74 }
75}
76
77// Task 9 fix round 1: the path-only resolution names the same file and
78// version as ResolveLive, and hashes nothing.
79func TestResolveLivePathIsResolveLiveWithoutTheDigest(t *testing.T) {
80 opt := t.TempDir()
81 if err := StageArtifact(opt, "bab", "v1", "km", []byte("b")); err != nil {
82 t.Fatal(err)
83 }
84 if err := PointLive(opt, "h.example.com", "bab", "v1", "km"); err != nil {
85 t.Fatal(err)
86 }
87 full, err := ResolveLive(opt, "h.example.com")
88 if err != nil {
89 t.Fatal(err)
90 }
91 got, err := ResolveLivePath(opt, "h.example.com")
92 if err != nil {
93 t.Fatalf("ResolveLivePath: %v", err)
94 }
95 if want := (StagedIdentity{Version: full.Version, StagedPath: full.StagedPath}); got != want || got.Version != "v1" {
96 t.Fatalf("ResolveLivePath = %+v, want %+v", got, want)
97 }
98 if _, err := ResolveLivePath(t.TempDir(), "h.example.com"); err == nil {
99 t.Fatal("ResolveLivePath with no live link returned no error")
100 }
101}
102
103func TestResolveLiveWithNoLinkIsAnError(t *testing.T) {
104 if _, err := ResolveLive(t.TempDir(), "h.example.com"); err == nil {
105 t.Fatal("ResolveLive with no live link returned no error")
106 }
107}
108
109func TestVerifyStaged(t *testing.T) {
110 opt := t.TempDir()
111 if err := StageArtifact(opt, "bab", "v1", "km", []byte("build one")); err != nil {
112 t.Fatal(err)
113 }
114 if err := PointLive(opt, "h.example.com", "bab", "v1", "km"); err != nil {
115 t.Fatal(err)
116 }
117 id, err := ResolveLive(opt, "h.example.com")
118 if err != nil {
119 t.Fatal(err)
120 }
121 if err := VerifyStaged(id); err != nil {
122 t.Fatalf("VerifyStaged on untouched bytes: %v", err)
123 }
124 // Damaged in place, to a different length.
125 if err := os.WriteFile(id.StagedPath, []byte("build one, damaged"), 0o755); err != nil {
126 t.Fatal(err)
127 }
128 if err := VerifyStaged(id); !errors.Is(err, ErrStagedCopyChanged) {
129 t.Fatalf("VerifyStaged on changed bytes = %v, want ErrStagedCopyChanged", err)
130 }
131 if err := os.Remove(id.StagedPath); err != nil {
132 t.Fatal(err)
133 }
134 if err := VerifyStaged(id); !errors.Is(err, ErrStagedCopyChanged) {
135 t.Fatalf("VerifyStaged on a missing file = %v, want ErrStagedCopyChanged", err)
136 }
137 if err := VerifyStaged(StagedIdentity{Version: "v1"}); !errors.Is(err, ErrStagedCopyChanged) {
138 t.Fatalf("VerifyStaged with no retained path or digest = %v, want ErrStagedCopyChanged", err)
139 }
140}
141
142// aliasedChain builds Astra's r2 N3 layout: live/h -> <tmp>/alias/current,
143// where <tmp>/alias is a symlink to the staged version's "links" directory
144// and current -> ../km. The OS resolves current's relative target against
145// its REAL parent, the links directory, so the chain ends at the staged
146// file. A lexical join would end at <tmp>/km instead. It returns the staged
147// file's path and the intermediate link as written.
148func aliasedChain(t *testing.T, opt string) (staged, current string) {
149 t.Helper()
150 if err := StageArtifact(opt, "bab", "v1", "km", []byte("build one")); err != nil {
151 t.Fatal(err)
152 }
153 staged = VersionPath(opt, "bab", "v1", "km")
154 links := filepath.Join(filepath.Dir(staged), "links")
155 if err := os.MkdirAll(links, 0o755); err != nil {
156 t.Fatal(err)
157 }
158 if err := os.Symlink(filepath.Join("..", "km"), filepath.Join(links, "current")); err != nil {
159 t.Fatal(err)
160 }
161 alias := filepath.Join(t.TempDir(), "alias")
162 if err := os.Symlink(links, alias); err != nil {
163 t.Fatal(err)
164 }
165 current = filepath.Join(alias, "current")
166 if err := os.MkdirAll(filepath.Join(opt, "live"), 0o755); err != nil {
167 t.Fatal(err)
168 }
169 if err := os.Symlink(current, LiveLink(opt, "h.example.com")); err != nil {
170 t.Fatal(err)
171 }
172 return staged, current
173}
174
175// A relative hop resolves against the real directory of the link that holds
176// it, as the OS resolves it (Astra r2 N3, ruling R17).
177func TestResolveLiveResolvesARelativeHopAgainstItsRealParent(t *testing.T) {
178 opt := t.TempDir()
179 staged, _ := aliasedChain(t, opt)
180 osAnswer, err := filepath.EvalSymlinks(LiveLink(opt, "h.example.com"))
181 if err != nil {
182 t.Fatalf("the OS cannot resolve the chain: %v", err)
183 }
184 if osAnswer != staged {
185 t.Fatalf("setup: the OS resolves the chain to %s, want %s", osAnswer, staged)
186 }
187 id, err := ResolveLive(opt, "h.example.com")
188 if err != nil {
189 t.Fatalf("ResolveLive: %v", err)
190 }
191 if id.StagedPath != osAnswer || id.Digest != etagOf([]byte("build one")) {
192 t.Fatalf("ResolveLive = %+v; want the OS's answer %s and the file's digest", id, osAnswer)
193 }
194}
195
196// A live link that names another link: the identity is the file at the end
197// of the chain, not the intermediate link, which can move.
198func TestResolveLiveFollowsAChainedLinkToTheFile(t *testing.T) {
199 opt := t.TempDir()
200 if err := StageArtifact(opt, "bab", "v1", "km", []byte("build one")); err != nil {
201 t.Fatal(err)
202 }
203 current := filepath.Join(t.TempDir(), "operator-current")
204 if err := os.Symlink(VersionPath(opt, "bab", "v1", "km"), current); err != nil {
205 t.Fatal(err)
206 }
207 if err := os.MkdirAll(filepath.Join(opt, "live"), 0o755); err != nil {
208 t.Fatal(err)
209 }
210 if err := os.Symlink(current, LiveLink(opt, "h.example.com")); err != nil {
211 t.Fatal(err)
212 }
213 id, err := ResolveLive(opt, "h.example.com")
214 if err != nil {
215 t.Fatalf("ResolveLive: %v", err)
216 }
217 if id.StagedPath != VersionPath(opt, "bab", "v1", "km") || id.Digest != etagOf([]byte("build one")) {
218 t.Fatalf("ResolveLive = %+v; want path %s and the file's digest", id, VersionPath(opt, "bab", "v1", "km"))
219 }
220}
221