Migration This is my new public git source viewer. Source coming soon. Please contact with concerns/bugs. Contact me

internal/notices/store_test.go

caching_trials · 3.6 KB · 150 lines raw

1 package notices
2
3 import (
4 "os"
5 "path/filepath"
6 "reflect"
7 "strings"
8 "testing"
9 )
10
11 func TestParseRejectsUnknownField(t *testing.T) {
12 _, err := parse([]byte("global:\n - type: info\n subject: hi\n"))
13 if err == nil {
14 t.Fatal("unknown field accepted")
15 }
16 if !strings.Contains(err.Error(), "subject") {
17 t.Fatalf("error does not name the field: %v", err)
18 }
19 }
20
21 func TestParseRejectsBadType(t *testing.T) {
22 _, err := parse([]byte("global:\n - type: critcal\n title: hi\n"))
23 if err == nil || !strings.Contains(err.Error(), "critcal") {
24 t.Fatalf("bad type: err = %v", err)
25 }
26 }
27
28 func TestParseEmpty(t *testing.T) {
29 for _, in := range [][]byte{nil, []byte(""), []byte("{}\n")} {
30 doc, err := parse(in)
31 if err != nil {
32 t.Fatalf("parse(%q): %v", in, err)
33 }
34 if len(doc.Global) != 0 || len(doc.Repos) != 0 {
35 t.Fatalf("parse(%q) = %+v, want empty", in, doc)
36 }
37 if doc.Repos == nil {
38 t.Fatalf("parse(%q): Repos is nil", in)
39 }
40 }
41 }
42
43 func TestTypeAliasesAndTrimming(t *testing.T) {
44 doc, err := parse([]byte("global:\n - type: informational\n title: ' spaced '\n"))
45 if err != nil {
46 t.Fatal(err)
47 }
48 n := doc.Global[0]
49 if n.Type != TypeInfo || n.Title != "spaced" {
50 t.Fatalf("got %+v", n)
51 }
52 }
53
54 func TestLinkSchemes(t *testing.T) {
55 cases := []struct {
56 link string
57 ok bool
58 }{
59 {"/status", true},
60 {"https://example.org/a", true},
61 {"mailto:ops@example.org", true},
62 {"javascript:alert(1)", false},
63 {"//evil.example", false},
64 {"ftp://example.org", false},
65 }
66 for _, tc := range cases {
67 doc := Document{Global: []Notice{{Type: TypeInfo, Title: "x", Link: tc.link}}}
68 err := validate(&doc)
69 if tc.ok && err != nil {
70 t.Errorf("link %q rejected: %v", tc.link, err)
71 }
72 if !tc.ok && err == nil {
73 t.Errorf("link %q accepted", tc.link)
74 }
75 }
76 }
77
78 func TestSetRoundTripAndRevision(t *testing.T) {
79 path := filepath.Join(t.TempDir(), "notices.yaml")
80 st, err := Open(path)
81 if err != nil {
82 t.Fatal(err)
83 }
84 t.Cleanup(st.Close)
85
86 if st.Revision() == "" {
87 t.Fatal("empty store has no revision")
88 }
89 doc := Document{
90 Global: []Notice{{Type: TypeCritical, Title: "Outage", Body: "line\nline"}},
91 Repos: map[string][]Notice{"demo": {{Type: "informational", Title: "Repo"}}},
92 }
93 if err := st.Set(doc); err != nil {
94 t.Fatal(err)
95 }
96 // informational must have been canonicalised in the published snapshot.
97 if got := st.Snapshot().Doc.Repos["demo"][0].Type; got != TypeInfo {
98 t.Fatalf("published type = %q, want %q", got, TypeInfo)
99 }
100 rev := st.Revision()
101
102 fi, err := os.Stat(path)
103 if err != nil {
104 t.Fatal(err)
105 }
106 if perm := fi.Mode().Perm(); perm != 0o600 {
107 t.Errorf("file mode = %o, want 600", perm)
108 }
109
110 again, err := Open(path)
111 if err != nil {
112 t.Fatal(err)
113 }
114 t.Cleanup(again.Close)
115 if !reflect.DeepEqual(st.Snapshot().Doc, again.Snapshot().Doc) {
116 t.Fatalf("round trip mismatch:\n%+v\n%+v", st.Snapshot().Doc, again.Snapshot().Doc)
117 }
118
119 // A no-op save is byte-identical and keeps the revision.
120 if err := st.Set(doc); err != nil {
121 t.Fatal(err)
122 }
123 if st.Revision() != rev {
124 t.Fatalf("no-op save changed revision %q -> %q", rev, st.Revision())
125 }
126
127 // A content change moves it.
128 doc.Global[0].Body = "different"
129 if err := st.Set(doc); err != nil {
130 t.Fatal(err)
131 }
132 if st.Revision() == rev {
133 t.Fatal("content change kept the same revision")
134 }
135 }
136
137 func TestOpenMissingFileAndBadDir(t *testing.T) {
138 dir := t.TempDir()
139 st, err := Open(filepath.Join(dir, "notices.yaml"))
140 if err != nil {
141 t.Fatal(err)
142 }
143 t.Cleanup(st.Close)
144 if len(st.Snapshot().Doc.Global) != 0 {
145 t.Fatal("missing file is not empty")
146 }
147 if _, err := Open(filepath.Join(dir, "nope", "notices.yaml")); err == nil {
148 t.Fatal("missing directory accepted")
149 }
150 }