|
17e2c1d…
|
lmata
|
1 |
package config |
|
17e2c1d…
|
lmata
|
2 |
|
|
17e2c1d…
|
lmata
|
3 |
import ( |
|
17e2c1d…
|
lmata
|
4 |
"os" |
|
17e2c1d…
|
lmata
|
5 |
"path/filepath" |
|
17e2c1d…
|
lmata
|
6 |
"testing" |
|
17e2c1d…
|
lmata
|
7 |
"time" |
|
17e2c1d…
|
lmata
|
8 |
) |
|
17e2c1d…
|
lmata
|
9 |
|
|
17e2c1d…
|
lmata
|
10 |
func TestSnapshotAndPrune(t *testing.T) { |
|
17e2c1d…
|
lmata
|
11 |
dir := t.TempDir() |
|
17e2c1d…
|
lmata
|
12 |
histDir := filepath.Join(dir, "history") |
|
17e2c1d…
|
lmata
|
13 |
configPath := filepath.Join(dir, "scuttlebot.yaml") |
|
17e2c1d…
|
lmata
|
14 |
|
|
17e2c1d…
|
lmata
|
15 |
// No-op when config file doesn't exist yet. |
|
17e2c1d…
|
lmata
|
16 |
if err := SnapshotConfig(histDir, configPath); err != nil { |
|
17e2c1d…
|
lmata
|
17 |
t.Fatalf("SnapshotConfig (no file): %v", err) |
|
17e2c1d…
|
lmata
|
18 |
} |
|
17e2c1d…
|
lmata
|
19 |
|
|
17e2c1d…
|
lmata
|
20 |
// Write a config file and snapshot it. |
|
17e2c1d…
|
lmata
|
21 |
if err := os.WriteFile(configPath, []byte("bridge:\n enabled: true\n"), 0o600); err != nil { |
|
17e2c1d…
|
lmata
|
22 |
t.Fatal(err) |
|
17e2c1d…
|
lmata
|
23 |
} |
|
17e2c1d…
|
lmata
|
24 |
if err := SnapshotConfig(histDir, configPath); err != nil { |
|
17e2c1d…
|
lmata
|
25 |
t.Fatalf("SnapshotConfig: %v", err) |
|
17e2c1d…
|
lmata
|
26 |
} |
|
17e2c1d…
|
lmata
|
27 |
|
|
17e2c1d…
|
lmata
|
28 |
entries, err := ListHistory(histDir, "scuttlebot.yaml") |
|
17e2c1d…
|
lmata
|
29 |
if err != nil { |
|
17e2c1d…
|
lmata
|
30 |
t.Fatal(err) |
|
17e2c1d…
|
lmata
|
31 |
} |
|
17e2c1d…
|
lmata
|
32 |
if len(entries) != 1 { |
|
17e2c1d…
|
lmata
|
33 |
t.Fatalf("want 1 snapshot, got %d", len(entries)) |
|
17e2c1d…
|
lmata
|
34 |
} |
|
17e2c1d…
|
lmata
|
35 |
if entries[0].Size == 0 { |
|
17e2c1d…
|
lmata
|
36 |
t.Error("snapshot size should be non-zero") |
|
17e2c1d…
|
lmata
|
37 |
} |
|
17e2c1d…
|
lmata
|
38 |
if entries[0].Timestamp.IsZero() { |
|
17e2c1d…
|
lmata
|
39 |
t.Error("snapshot timestamp should be set") |
|
17e2c1d…
|
lmata
|
40 |
} |
|
17e2c1d…
|
lmata
|
41 |
} |
|
17e2c1d…
|
lmata
|
42 |
|
|
17e2c1d…
|
lmata
|
43 |
func TestPruneHistory(t *testing.T) { |
|
17e2c1d…
|
lmata
|
44 |
dir := t.TempDir() |
|
17e2c1d…
|
lmata
|
45 |
base := "scuttlebot.yaml" |
|
17e2c1d…
|
lmata
|
46 |
keep := 3 |
|
17e2c1d…
|
lmata
|
47 |
|
|
17e2c1d…
|
lmata
|
48 |
// Write 5 snapshot files with distinct timestamps. |
|
17e2c1d…
|
lmata
|
49 |
for i := range 5 { |
|
17e2c1d…
|
lmata
|
50 |
stamp := time.Now().Add(time.Duration(i) * time.Second).Format(historyTimestampFormat) |
|
17e2c1d…
|
lmata
|
51 |
name := filepath.Join(dir, base+"."+stamp) |
|
17e2c1d…
|
lmata
|
52 |
if err := os.WriteFile(name, []byte("v"), 0o600); err != nil { |
|
17e2c1d…
|
lmata
|
53 |
t.Fatal(err) |
|
17e2c1d…
|
lmata
|
54 |
} |
|
17e2c1d…
|
lmata
|
55 |
// Ensure distinct mtime ordering. |
|
17e2c1d…
|
lmata
|
56 |
time.Sleep(2 * time.Millisecond) |
|
17e2c1d…
|
lmata
|
57 |
} |
|
17e2c1d…
|
lmata
|
58 |
|
|
17e2c1d…
|
lmata
|
59 |
if err := PruneHistory(dir, base, keep); err != nil { |
|
17e2c1d…
|
lmata
|
60 |
t.Fatal(err) |
|
17e2c1d…
|
lmata
|
61 |
} |
|
17e2c1d…
|
lmata
|
62 |
|
|
17e2c1d…
|
lmata
|
63 |
entries, err := ListHistory(dir, base) |
|
17e2c1d…
|
lmata
|
64 |
if err != nil { |
|
17e2c1d…
|
lmata
|
65 |
t.Fatal(err) |
|
17e2c1d…
|
lmata
|
66 |
} |
|
17e2c1d…
|
lmata
|
67 |
if len(entries) != keep { |
|
17e2c1d…
|
lmata
|
68 |
t.Errorf("want %d entries after prune, got %d", keep, len(entries)) |
|
17e2c1d…
|
lmata
|
69 |
} |
|
17e2c1d…
|
lmata
|
70 |
} |
|
17e2c1d…
|
lmata
|
71 |
|
|
17e2c1d…
|
lmata
|
72 |
func TestConfigSaveRoundTrip(t *testing.T) { |
|
17e2c1d…
|
lmata
|
73 |
dir := t.TempDir() |
|
17e2c1d…
|
lmata
|
74 |
path := filepath.Join(dir, "scuttlebot.yaml") |
|
17e2c1d…
|
lmata
|
75 |
|
|
17e2c1d…
|
lmata
|
76 |
var cfg Config |
|
17e2c1d…
|
lmata
|
77 |
cfg.Defaults() |
|
17e2c1d…
|
lmata
|
78 |
cfg.Topology.Channels = []StaticChannelConfig{ |
|
17e2c1d…
|
lmata
|
79 |
{Name: "#general", Topic: "Fleet coordination", Autojoin: []string{"bridge"}}, |
|
17e2c1d…
|
lmata
|
80 |
} |
|
17e2c1d…
|
lmata
|
81 |
cfg.Topology.Types = []ChannelTypeConfig{ |
|
17e2c1d…
|
lmata
|
82 |
{Name: "task", Prefix: "task.", Ephemeral: true, TTL: Duration{72 * time.Hour}}, |
|
17e2c1d…
|
lmata
|
83 |
} |
|
17e2c1d…
|
lmata
|
84 |
|
|
17e2c1d…
|
lmata
|
85 |
if err := cfg.Save(path); err != nil { |
|
17e2c1d…
|
lmata
|
86 |
t.Fatalf("Save: %v", err) |
|
17e2c1d…
|
lmata
|
87 |
} |
|
17e2c1d…
|
lmata
|
88 |
|
|
17e2c1d…
|
lmata
|
89 |
var loaded Config |
|
17e2c1d…
|
lmata
|
90 |
loaded.Defaults() |
|
17e2c1d…
|
lmata
|
91 |
if err := loaded.LoadFile(path); err != nil { |
|
17e2c1d…
|
lmata
|
92 |
t.Fatalf("LoadFile: %v", err) |
|
17e2c1d…
|
lmata
|
93 |
} |
|
17e2c1d…
|
lmata
|
94 |
|
|
17e2c1d…
|
lmata
|
95 |
if len(loaded.Topology.Channels) != 1 || loaded.Topology.Channels[0].Name != "#general" { |
|
17e2c1d…
|
lmata
|
96 |
t.Errorf("topology channels not round-tripped: %+v", loaded.Topology.Channels) |
|
17e2c1d…
|
lmata
|
97 |
} |
|
17e2c1d…
|
lmata
|
98 |
if len(loaded.Topology.Types) != 1 || loaded.Topology.Types[0].Name != "task" { |
|
17e2c1d…
|
lmata
|
99 |
t.Errorf("topology types not round-tripped: %+v", loaded.Topology.Types) |
|
17e2c1d…
|
lmata
|
100 |
} |
|
17e2c1d…
|
lmata
|
101 |
if loaded.Topology.Types[0].TTL.Duration != 72*time.Hour { |
|
17e2c1d…
|
lmata
|
102 |
t.Errorf("TTL = %v, want 72h", loaded.Topology.Types[0].TTL.Duration) |
|
17e2c1d…
|
lmata
|
103 |
} |
|
17e2c1d…
|
lmata
|
104 |
} |