carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build unix
2
3package hostfiles
4
5import (
6 "context"
7 "io"
8 "os"
9 "path/filepath"
10 "reflect"
11 "testing"
12
13 "github.com/carlosframework/platform/internal/release"
14)
15
16func write(t *testing.T, dir, rel, body string) {
17 t.Helper()
18 full := filepath.Join(dir, rel)
19 if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
20 t.Fatal(err)
21 }
22 if err := os.WriteFile(full, []byte(body), 0o640); err != nil {
23 t.Fatal(err)
24 }
25}
26
27func TestSyncDirPublishesNewChangedAndDeleted(t *testing.T) {
28 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
29 p := open(t, s)
30 write(t, src, "instance.key", "k")
31 write(t, src, "uploads/a.txt", "a")
32 st, err := SyncDir(ctx, p, src, nil)
33 if err != nil || st.Published != 2 {
34 t.Fatalf("first sync: %+v, %v", st, err)
35 }
36 commit(t, p)
37
38 write(t, src, "uploads/a.txt", "a2")
39 if err := os.Remove(filepath.Join(src, "instance.key")); err != nil {
40 t.Fatal(err)
41 }
42 write(t, src, "new.txt", "n")
43 st, err = SyncDir(ctx, p, src, nil)
44 if err != nil || st.Published != 2 || st.Deleted != 1 {
45 t.Fatalf("second sync: %+v, %v", st, err)
46 }
47 commit(t, p)
48
49 dst := t.TempDir()
50 if _, _, err := Restore(ctx, s, pfx, dst, nil); err != nil {
51 t.Fatal(err)
52 }
53 if read(t, dst, "uploads/a.txt") != "a2" || read(t, dst, "new.txt") != "n" {
54 t.Fatal("content differs")
55 }
56 if _, err := os.Stat(filepath.Join(dst, "instance.key")); !os.IsNotExist(err) {
57 t.Fatal("deleted file restored")
58 }
59 fi, _ := os.Stat(filepath.Join(dst, "new.txt"))
60 if fi.Mode().Perm() != 0o640 {
61 t.Fatalf("mode = %v, want 0640", fi.Mode().Perm())
62 }
63}
64
65func TestSyncDirOfAnUnchangedDirectoryStagesNothing(t *testing.T) {
66 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
67 p := open(t, s)
68 write(t, src, "a", "a")
69 if _, err := SyncDir(ctx, p, src, nil); err != nil {
70 t.Fatal(err)
71 }
72 commit(t, p)
73 st, err := SyncDir(ctx, p, src, nil)
74 if err != nil || st.Published != 0 || st.Deleted != 0 || p.Dirty() {
75 t.Fatalf("second sync: %+v dirty=%v err=%v", st, p.Dirty(), err)
76 }
77}
78
79// Review Focus 2: never follow or open what is not a regular file.
80func TestSyncDirSkipsWhatIsNotARegularFile(t *testing.T) {
81 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
82 outside := filepath.Join(t.TempDir(), "secret")
83 if err := os.WriteFile(outside, []byte("do not publish"), 0o600); err != nil {
84 t.Fatal(err)
85 }
86 write(t, src, "real.txt", "r")
87 if err := os.Symlink(outside, filepath.Join(src, "link")); err != nil {
88 t.Fatal(err)
89 }
90 if err := os.Symlink(filepath.Dir(outside), filepath.Join(src, "linkdir")); err != nil {
91 t.Fatal(err)
92 }
93 p := open(t, s)
94 st, err := SyncDir(ctx, p, src, nil)
95 if err != nil {
96 t.Fatal(err)
97 }
98 if st.Published != 1 || !reflect.DeepEqual(st.NotRegular, []string{"link", "linkdir"}) {
99 t.Fatalf("stats = %+v", st)
100 }
101 if got := p.Paths(); !reflect.DeepEqual(got, []string{"real.txt"}) {
102 t.Fatalf("Paths = %v", got)
103 }
104}
105
106// Review Focus 3: too large is recorded, never read whole, never dropped.
107func TestSyncDirRecordsAFileOverTheCapWithoutReadingIt(t *testing.T) {
108 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
109 big := filepath.Join(src, "huge.bin")
110 f, err := os.Create(big)
111 if err != nil {
112 t.Fatal(err)
113 }
114 if err := f.Truncate(MaxFileBytes + 1); err != nil { // sparse: costs no disk
115 t.Fatal(err)
116 }
117 f.Close()
118 reads := 0
119 orig := readAll
120 readAll = func(r io.Reader) ([]byte, error) { reads++; return orig(r) }
121 t.Cleanup(func() { readAll = orig })
122 p := open(t, s)
123 st, err := SyncDir(ctx, p, src, nil)
124 if err != nil {
125 t.Fatal(err)
126 }
127 if !reflect.DeepEqual(st.TooLarge, []string{"huge.bin"}) || st.Published != 0 {
128 t.Fatalf("stats = %+v", st)
129 }
130 if reads != 0 {
131 t.Fatalf("the oversized file was read %d times; it must be refused on its size alone", reads)
132 }
133 commit(t, p)
134 _, rs, err := Restore(ctx, s, pfx, t.TempDir(), nil)
135 if err != nil || !reflect.DeepEqual(rs.Skipped, []string{"huge.bin"}) {
136 t.Fatalf("restore skipped %v, err %v", rs.Skipped, err)
137 }
138}
139
140func TestSyncDirHonoursSkip(t *testing.T) {
141 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
142 write(t, src, "keep.txt", "k")
143 write(t, src, ".files-gen", "marker")
144 write(t, src, "cache/x", "x")
145 p := open(t, s)
146 skip := func(rel string, isDir bool) bool { return rel == ".files-gen" || (isDir && rel == "cache") }
147 if _, err := SyncDir(ctx, p, src, skip); err != nil {
148 t.Fatal(err)
149 }
150 if got := p.Paths(); !reflect.DeepEqual(got, []string{"keep.txt"}) {
151 t.Fatalf("Paths = %v", got)
152 }
153}
154
155func TestSyncDirOfAMissingDirectoryTombstonesNothing(t *testing.T) {
156 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
157 p := open(t, s)
158 write(t, src, "a", "a")
159 if _, err := SyncDir(ctx, p, src, nil); err != nil {
160 t.Fatal(err)
161 }
162 commit(t, p)
163 if _, err := SyncDir(ctx, p, filepath.Join(src, "does-not-exist"), nil); err == nil {
164 t.Fatal("a missing root must be an error, not an empty directory")
165 }
166 if p.Dirty() {
167 t.Fatal("a missing root staged tombstones")
168 }
169}
170
171// An excluded directory's files, published before the exclusion applied,
172// are left as the manifest has them: not republished, not tombstoned.
173func TestSyncDirDoesNotTombstoneWhatASkippedDirectoryHolds(t *testing.T) {
174 ctx, s, src := context.Background(), release.NewDirStore(t.TempDir()), t.TempDir()
175 write(t, src, "cache/x", "x")
176 write(t, src, "keep.txt", "k")
177 p := open(t, s)
178 if _, err := SyncDir(ctx, p, src, nil); err != nil {
179 t.Fatal(err)
180 }
181 commit(t, p)
182 skip := func(rel string, isDir bool) bool { return isDir && rel == "cache" }
183 st, err := SyncDir(ctx, p, src, skip)
184 if err != nil || st.Deleted != 0 || p.Dirty() {
185 t.Fatalf("stats = %+v dirty=%v err=%v", st, p.Dirty(), err)
186 }
187 if got := p.Paths(); !reflect.DeepEqual(got, []string{"cache/x", "keep.txt"}) {
188 t.Fatalf("Paths = %v", got)
189 }
190}
191