OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package dskey | |
6 | |
7 import ( | |
8 "encoding/json" | |
9 "fmt" | |
10 "testing" | |
11 | |
12 ds "github.com/luci/gae/service/datastore" | |
13 . "github.com/luci/luci-go/common/testing/assertions" | |
14 . "github.com/smartystreets/goconvey/convey" | |
15 ) | |
16 | |
17 func mkKey(aid, ns string, elems ...interface{}) ds.Key { | |
18 if len(elems)%2 != 0 { | |
19 panic("odd number of tokens") | |
20 } | |
21 toks := make([]ds.KeyTok, len(elems)/2) | |
22 for i := 0; i < len(elems); i += 2 { | |
23 toks[i/2].Kind = elems[i].(string) | |
24 switch x := elems[i+1].(type) { | |
25 case string: | |
26 toks[i/2].StringID = x | |
27 case int: | |
28 toks[i/2].IntID = int64(x) | |
29 default: | |
30 panic("bad token id") | |
31 } | |
32 } | |
33 return NewToks(aid, ns, toks) | |
34 } | |
35 | |
36 func ShouldEqualKey(actual interface{}, expected ...interface{}) string { | |
37 if len(expected) != 1 { | |
38 return fmt.Sprintf("Assertion requires 1 expected value, got %d"
, len(expected)) | |
39 } | |
40 if Equal(actual.(ds.Key), expected[0].(ds.Key)) { | |
41 return "" | |
42 } | |
43 return fmt.Sprintf("Expected: %q\nActual: %q", actual, expected[0]) | |
44 } | |
45 | |
46 func TestKeyEncode(t *testing.T) { | |
47 t.Parallel() | |
48 | |
49 keys := []ds.Key{ | |
50 mkKey("appid", "ns", "kind", 1), | |
51 mkKey("appid", "ns", "nerd", "moo"), | |
52 mkKey("appid", "ns", "parent", 10, "renerd", "moo"), | |
53 } | |
54 | |
55 Convey("Key Round trip", t, func() { | |
56 for _, k := range keys { | |
57 k := k | |
58 Convey(k.String(), func() { | |
59 enc := Encode(k) | |
60 aid, ns, toks, err := ToksDecode(enc) | |
61 So(err, ShouldBeNil) | |
62 dec := NewToks(aid, ns, toks) | |
63 So(dec, ShouldNotBeNil) | |
64 So(dec, ShouldEqualKey, k) | |
65 | |
66 dec2, err := NewFromEncoded(enc) | |
67 So(err, ShouldBeNil) | |
68 So(dec2, ShouldEqualKey, dec) | |
69 So(dec2, ShouldEqualKey, k) | |
70 }) | |
71 | |
72 Convey(k.String()+" (json)", func() { | |
73 data, err := MarshalJSON(k) | |
74 So(err, ShouldBeNil) | |
75 | |
76 aid, ns, toks, err := UnmarshalJSON(data) | |
77 So(err, ShouldBeNil) | |
78 So(NewToks(aid, ns, toks), ShouldEqualKey, k) | |
79 }) | |
80 } | |
81 }) | |
82 | |
83 Convey("NewKey", t, func() { | |
84 Convey("single", func() { | |
85 k := New("appid", "ns", "kind", "", 1, nil) | |
86 So(k, ShouldEqualKey, keys[0]) | |
87 }) | |
88 | |
89 Convey("nest", func() { | |
90 k := New("appid", "ns", "renerd", "moo", 0, | |
91 New("appid", "ns", "parent", "", 10, nil)) | |
92 So(k, ShouldEqualKey, keys[2]) | |
93 }) | |
94 }) | |
95 | |
96 Convey("Key bad encoding", t, func() { | |
97 Convey("extra junk before", func() { | |
98 enc := Encode(keys[2]) | |
99 _, _, _, err := ToksDecode("/" + enc) | |
100 So(err, ShouldErrLike, "illegal base64") | |
101 }) | |
102 | |
103 Convey("extra junk after", func() { | |
104 enc := Encode(keys[2]) | |
105 _, _, _, err := ToksDecode(enc[:len(enc)-1]) | |
106 So(err, ShouldErrLike, "EOF") | |
107 }) | |
108 | |
109 Convey("json encoding includes quotes", func() { | |
110 data, err := MarshalJSON(keys[0]) | |
111 So(err, ShouldBeNil) | |
112 | |
113 _, _, _, err = UnmarshalJSON(append(data, '!')) | |
114 So(err, ShouldErrLike, "bad JSON key") | |
115 }) | |
116 }) | |
117 } | |
118 | |
119 type dumbKey1 struct{ ds.Key } | |
120 | |
121 func (dk dumbKey1) Namespace() string { return "ns" } | |
122 func (dk dumbKey1) Parent() ds.Key { return dk.Key } | |
123 func (dk dumbKey1) String() string { return "dumbKey1" } | |
124 | |
125 type dumbKey2 struct{ ds.Key } | |
126 | |
127 /// This is the dumb part... can't have both IDs set. | |
128 func (dk dumbKey2) IntID() int64 { return 1 } | |
129 func (dk dumbKey2) StringID() string { return "wat" } | |
130 | |
131 func (dk dumbKey2) Kind() string { return "kind" } | |
132 func (dk dumbKey2) Parent() ds.Key { return nil } | |
133 func (dk dumbKey2) Namespace() string { return "ns" } | |
134 func (dk dumbKey2) AppID() string { return "aid" } | |
135 func (dk dumbKey2) String() string { return "dumbKey2" } | |
136 func (dk dumbKey2) Valid(allowSpecial bool, aid, ns string) bool { | |
137 return Valid(dk, allowSpecial, aid, ns) | |
138 } | |
139 | |
140 func TestKeyValidity(t *testing.T) { | |
141 //t.Parallel() | |
142 | |
143 Convey("keys validity", t, func() { | |
144 Convey("incomplete", func() { | |
145 So(Incomplete(mkKey("aid", "ns", "kind", 1)), ShouldBeFa
lse) | |
146 So(Incomplete(mkKey("aid", "ns", "kind", 0)), ShouldBeTr
ue) | |
147 }) | |
148 | |
149 Convey("invalid", func() { | |
150 So(mkKey("aid", "ns", "hat", "face", "__kind__", 1).Vali
d(true, "aid", "ns"), ShouldBeTrue) | |
151 | |
152 bads := []ds.Key{ | |
153 nil, | |
154 mkKey("", "ns", "hat", "face"), | |
155 mkKey("aid", "ns", "base", 1, "", "id"), | |
156 mkKey("aid", "ns", "hat", "face", "__kind__", 1)
, | |
157 mkKey("aid", "ns", "hat", 0, "kind", 1), | |
158 dumbKey1{mkKey("aid", "badNS", "hat", 1)}, | |
159 dumbKey2{}, | |
160 } | |
161 for _, k := range bads { | |
162 s := "<nil>" | |
163 if k != nil { | |
164 s = k.String() | |
165 } | |
166 Convey(s, func() { | |
167 if k != nil { | |
168 So(k.Valid(false, "aid", "ns"),
ShouldBeFalse) | |
169 } else { | |
170 So(Valid(k, false, "aid", "ns"),
ShouldBeFalse) | |
171 } | |
172 }) | |
173 } | |
174 }) | |
175 | |
176 Convey("partially valid", func() { | |
177 So(mkKey("aid", "ns", "kind", "").PartialValid("aid", "n
s"), ShouldBeTrue) | |
178 So(mkKey("aid", "ns", "kind", "", "child", "").PartialVa
lid("aid", "ns"), ShouldBeFalse) | |
179 }) | |
180 }) | |
181 } | |
182 | |
183 type keyWrap struct{ ds.Key } | |
184 | |
185 func (k keyWrap) Parent() ds.Key { | |
186 if k.Key.Parent() != nil { | |
187 return keyWrap{k.Key.Parent()} | |
188 } | |
189 return nil | |
190 } | |
191 | |
192 func TestMiscKey(t *testing.T) { | |
193 t.Parallel() | |
194 | |
195 Convey("KeyRoot", t, func() { | |
196 k := mkKey("appid", "ns", "parent", 10, "renerd", "moo") | |
197 r := mkKey("appid", "ns", "parent", 10) | |
198 So(Root(k), ShouldEqualKey, r) | |
199 So(Root(nil), ShouldBeNil) | |
200 }) | |
201 | |
202 Convey("KeySplit", t, func() { | |
203 // keyWrap forces KeySplit to not take the GenericKey shortcut. | |
204 k := keyWrap{mkKey("appid", "ns", "parent", 10, "renerd", "moo")
} | |
205 aid, ns, toks := Split(k) | |
206 So(aid, ShouldEqual, "appid") | |
207 So(ns, ShouldEqual, "ns") | |
208 So(toks, ShouldResemble, []ds.KeyTok{ | |
209 {Kind: "parent", IntID: 10}, | |
210 {Kind: "renerd", StringID: "moo"}, | |
211 }) | |
212 }) | |
213 | |
214 Convey("KeySplit (nil)", t, func() { | |
215 aid, ns, toks := Split(nil) | |
216 So(aid, ShouldEqual, "") | |
217 So(ns, ShouldEqual, "") | |
218 So(toks, ShouldResemble, []ds.KeyTok(nil)) | |
219 }) | |
220 | |
221 Convey("KeySplit ((*GenericKey)(nil))", t, func() { | |
222 aid, ns, toks := Split((*Generic)(nil)) | |
223 So(aid, ShouldEqual, "") | |
224 So(ns, ShouldEqual, "") | |
225 So(toks, ShouldResemble, []ds.KeyTok(nil)) | |
226 }) | |
227 | |
228 Convey("KeysEqual", t, func() { | |
229 k1 := mkKey("a", "n", "knd", 1) | |
230 k2 := mkKey("a", "n", "knd", 1) | |
231 So(Equal(k1, k2), ShouldBeTrue) | |
232 k3 := mkKey("a", "n", "knd", 2) | |
233 So(Equal(k1, k3), ShouldBeFalse) | |
234 }) | |
235 | |
236 Convey("KeyString", t, func() { | |
237 k1 := mkKey("a", "n", "knd", 1, "other", "wat") | |
238 So(String(k1), ShouldEqual, "/knd,1/other,wat") | |
239 So(String(nil), ShouldEqual, "") | |
240 }) | |
241 | |
242 Convey("*GenericKey supports json encoding", t, func() { | |
243 type TestStruct struct { | |
244 Key *Generic | |
245 } | |
246 t := &TestStruct{ | |
247 New("aid", "ns", "kind", "id", 0, | |
248 New("aid", "ns", "parent", "", 1, nil), | |
249 )} | |
250 d, err := json.Marshal(t) | |
251 So(err, ShouldBeNil) | |
252 t2 := &TestStruct{} | |
253 err = json.Unmarshal(d, t2) | |
254 So(err, ShouldBeNil) | |
255 So(t, ShouldResemble, t2) | |
256 }) | |
257 } | |
OLD | NEW |