1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package persistence
import (
"bytes"
"os"
"path/filepath"
"sync"
"testing"
)
func TestWriteFileReplacesAndPreservesPermissions(t *testing.T) {
path := filepath.Join(t.TempDir(), "save.yaml")
if err := WriteFile(path, []byte("old"), 0600); err != nil {
t.Fatal(err)
}
if err := WriteFile(path, []byte("new"), 0644); err != nil {
t.Fatal(err)
}
got, err := os.ReadFile(path)
if err != nil || string(got) != "new" {
t.Fatalf("read: %q, %v", got, err)
}
info, err := os.Stat(path)
if err != nil || info.Mode().Perm() != 0600 {
t.Fatalf("permissions: %v, %v", info, err)
}
entries, err := os.ReadDir(filepath.Dir(path))
if err != nil || len(entries) != 1 {
t.Fatalf("temporary files left behind: %v, %v", entries, err)
}
}
func TestFailedRenameCleansTempFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "destination")
if err := os.Mkdir(path, 0755); err != nil {
t.Fatal(err)
}
if err := WriteFile(path, []byte("new"), 0600); err == nil {
t.Fatal("expected rename over directory to fail")
}
entries, err := os.ReadDir(dir)
if err != nil || len(entries) != 1 || !entries[0].IsDir() {
t.Fatalf("destination damaged or temporary file leaked: %v, %v", entries, err)
}
}
func TestReadersSeeOnlyCompleteSaves(t *testing.T) {
path := filepath.Join(t.TempDir(), "save.yaml")
a, b := bytes.Repeat([]byte("a"), 8192), bytes.Repeat([]byte("b"), 16384)
if err := WriteFile(path, a, 0600); err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
for _, data := range [][]byte{a, b} {
wg.Add(1)
go func(data []byte) {
defer wg.Done()
for i := 0; i < 30; i++ {
if err := WriteFile(path, data, 0600); err != nil {
t.Error(err)
return
}
}
}(data)
}
for i := 0; i < 300; i++ {
got, err := os.ReadFile(path)
if err != nil || (!bytes.Equal(got, a) && !bytes.Equal(got, b)) {
t.Errorf("reader saw partial or missing save: length=%d, err=%v", len(got), err)
break
}
}
wg.Wait()
}
|