ducky/devices

Paddy 2015-11-27 Parent:74dbc04879a7 Child:1ae5bae472c1

11:683050b4546b Go to Latest

ducky/devices/storer_test.go

Return ErrDeviceNotFound when updating devices. If we can't find the Device we're supposed to update, return an ErrDeviceNotFound error. Write a unit test that tests for this behaviour.

History
1 package devices
3 import (
4 "fmt"
5 "testing"
6 "time"
8 "code.secondbit.org/uuid.hg"
9 "golang.org/x/net/context"
10 )
12 type StorerFactory interface {
13 NewStorer(ctx context.Context) (Storer, error)
14 TeardownStorer(storer Storer, ctx context.Context) error
15 }
17 var storerFactories []StorerFactory
19 const (
20 changeName = 1 << iota
21 changeOwner
22 changeType
23 changeCreated
24 changeLastSeen
25 changePushToken
26 changeVariations
27 )
29 func compareDevices(device1, device2 Device) (ok bool, field string, expected, result interface{}) {
30 if !device1.ID.Equal(device2.ID) {
31 return false, "ID", device1.ID, device2.ID
32 }
33 if device1.Name != device2.Name {
34 return false, "Name", device1.Name, device2.Name
35 }
36 if !device1.Owner.Equal(device2.Owner) {
37 return false, "Owner", device1.Owner, device2.Owner
38 }
39 if device1.Type != device2.Type {
40 return false, "Type", device1.Type, device2.Type
41 }
42 if !device1.Created.Equal(device2.Created) {
43 return false, "Created", device1.Created, device2.Created
44 }
45 if !device1.LastSeen.Equal(device2.LastSeen) {
46 return false, "LastSeen", device1.LastSeen, device2.LastSeen
47 }
48 if device1.PushToken != device2.PushToken {
49 return false, "PushToken", device1.PushToken, device2.PushToken
50 }
51 return true, "", nil, nil
52 }
54 func TestCreateAndGetDevices(t *testing.T) {
55 for _, factory := range storerFactories {
56 ctx := context.Background()
57 storer, err := factory.NewStorer(ctx)
58 if err != nil {
59 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
60 }
62 devices := []Device{
63 {ID: uuid.NewID(), Name: "Test 1", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
64 {ID: uuid.NewID(), Name: "Test 2", Owner: uuid.NewID(), Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
65 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
66 }
68 err = storer.CreateDevices(devices, ctx)
69 if err != nil {
70 t.Errorf("Error creating devices in %T: %+v\n", storer, err)
71 }
73 ids := make([]uuid.ID, 0, len(devices))
74 for _, device := range devices {
75 ids = append(ids, device.ID)
76 }
78 results, err := storer.GetDevices(ids, ctx)
79 if err != nil {
80 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err)
81 }
82 for _, device := range devices {
83 d, returned := results[device.ID.String()]
84 if !returned {
85 t.Errorf("Expected device %s to be in results from %T, but wasn't present\n", device.Name, storer)
86 }
87 ok, field, expected, result := compareDevices(device, d)
88 if !ok {
89 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, device.Name, expected, result, storer)
90 }
91 }
92 err = factory.TeardownStorer(storer, ctx)
93 if err != nil {
94 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
95 }
96 }
97 }
99 func TestGetDevicesNoErrorForMissing(t *testing.T) {
100 for _, factory := range storerFactories {
101 ctx := context.Background()
102 storer, err := factory.NewStorer(ctx)
103 if err != nil {
104 t.Fatalf("Fatal error creatng Storer from %T: %+v\n", factory, err)
105 }
107 results, err := storer.GetDevices([]uuid.ID{uuid.NewID()}, ctx)
108 if err != nil {
109 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err)
110 }
111 if len(results) != 0 {
112 t.Errorf("Expected results to be empty, got %+v from %T instead\n", results, storer)
113 }
114 err = factory.TeardownStorer(storer, ctx)
115 if err != nil {
116 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
117 }
118 }
119 }
121 func TestCreateDevicesDuplicates(t *testing.T) {
122 for _, factory := range storerFactories {
123 ctx := context.Background()
124 storer, err := factory.NewStorer(ctx)
125 if err != nil {
126 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
127 }
129 devices := []Device{
130 {ID: uuid.NewID(), Name: "Test 1", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
131 {ID: uuid.NewID(), Name: "Test 2", Owner: uuid.NewID(), Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
132 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
133 }
135 err = storer.CreateDevices(devices, ctx)
136 if err != nil {
137 t.Errorf("Unexpected error creating devices in %T: %+v\n", storer, err)
138 }
140 newDevices := []Device{
141 {ID: uuid.NewID(), Name: "Test 4", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
142 {ID: uuid.NewID(), Name: "Test 5", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
143 }
145 err = storer.CreateDevices([]Device{newDevices[0], devices[1], newDevices[1]}, ctx)
146 daeErr, ok := err.(ErrDeviceAlreadyExists)
147 if !ok {
148 t.Errorf("Expected ErrDeviceAlreadyExists creating duplicate device in %T, got %+v\n", storer, err)
149 }
150 if !uuid.ID(daeErr).Equal(devices[1].ID) {
151 t.Errorf("Expected ErrDeviceAlreadyExists to be %+v, got %+v from %T\n", devices[1].ID, daeErr, storer)
152 }
154 // inserts should be a transaction; they either all make it, or none do
155 results, err := storer.GetDevices([]uuid.ID{newDevices[0].ID, newDevices[1].ID}, ctx)
156 if err != nil {
157 t.Errorf("Error retrieving devices from %T: %+v\n", storer, err)
158 }
159 if len(results) != 0 {
160 t.Errorf("Expected new inserts to not be in results, got %+v from %T\n", results, storer)
161 }
163 err = factory.TeardownStorer(storer, ctx)
164 if err != nil {
165 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
166 }
167 }
168 }
170 func TestCreateAndListDevicesByOwner(t *testing.T) {
171 for _, factory := range storerFactories {
172 ctx := context.Background()
173 storer, err := factory.NewStorer(ctx)
174 if err != nil {
175 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
176 }
178 owner1, owner2 := uuid.NewID(), uuid.NewID()
179 devices := []Device{
180 {ID: uuid.NewID(), Name: "Test 1", Owner: owner1, Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
181 {ID: uuid.NewID(), Name: "Test 2", Owner: owner2, Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
182 {ID: uuid.NewID(), Name: "Test 3", Owner: owner1, Type: TypeChromeExtension, Created: time.Now().Add(time.Minute), LastSeen: time.Now(), PushToken: "test token"},
183 }
185 err = storer.CreateDevices(devices, ctx)
186 if err != nil {
187 t.Errorf("Error creating devices in %T: %+v\n", storer, err)
188 }
190 results, err := storer.ListDevicesByOwner(owner1, ctx)
191 if err != nil {
192 t.Errorf("Error listing devices for owner1 from %T: %+v\n", storer, err)
193 }
194 if len(results) != 2 {
195 t.Errorf("Expected %d results for owner1, got %d from %T\n", 2, len(results), storer)
196 }
197 resultMap := ToMap(results)
198 d, ok := resultMap[devices[0].ID.String()]
199 if !ok {
200 t.Errorf("Expected to get %s in results, got %+v from %T\n", devices[0].Name, results, storer)
201 }
202 ok, field, expected, result := compareDevices(devices[0], d)
203 if !ok {
204 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[0].Name, expected, result, storer)
205 }
206 d, ok = resultMap[devices[2].ID.String()]
207 if !ok {
208 t.Errorf("Expected to get %s in results, got %+v from %T\n", devices[2].Name, results, storer)
209 }
210 ok, field, expected, result = compareDevices(devices[2], d)
211 if !ok {
212 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[2].Name, expected, result, storer)
213 }
215 results, err = storer.ListDevicesByOwner(owner2, ctx)
216 if err != nil {
217 t.Errorf("Error listing devices for owner2 from %T: %+v\n", storer, err)
218 }
219 if len(results) != 1 {
220 t.Errorf("Expected %d results for owner2, got %d from %T\n", 1, len(results), storer)
221 }
222 ok, field, expected, result = compareDevices(devices[1], results[0])
223 if !ok {
224 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[1].Name, expected, result, storer)
225 }
227 err = factory.TeardownStorer(storer, ctx)
228 if err != nil {
229 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
230 }
231 }
232 }
234 func TestUpdateDevicesHappyPath(t *testing.T) {
235 device := Device{
236 ID: uuid.NewID(),
237 Name: "Test 1",
238 Owner: uuid.NewID(),
239 Type: TypeAndroidPhone,
240 Created: time.Now(),
241 LastSeen: time.Now(),
242 PushToken: "test token",
243 }
244 for _, factory := range storerFactories {
245 ctx := context.Background()
246 storer, err := factory.NewStorer(ctx)
247 if err != nil {
248 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
249 }
250 for i := 1; i < changeVariations; i++ {
251 var change DeviceChange
252 var owner uuid.ID
253 var name, pushToken string
254 var created, lastSeen time.Time
255 var deviceType DeviceType
257 device.ID = uuid.NewID()
259 expectation := device
260 result := device
262 change.DeviceID = device.ID
264 if i&changeName != 0 {
265 name = fmt.Sprintf("name-%d", i)
266 change.Name = &name
267 expectation.Name = name
268 }
269 if i&changeOwner != 0 {
270 owner = uuid.NewID()
271 change.Owner = &owner
272 expectation.Owner = owner
273 }
274 if i&changeType != 0 {
275 deviceType = DeviceType(fmt.Sprintf("type-%d", i))
276 change.Type = &deviceType
277 expectation.Type = deviceType
278 }
279 if i&changeCreated != 0 {
280 created = time.Now().Add(time.Hour * time.Duration(i))
281 change.Created = &created
282 expectation.Created = created
283 }
284 if i&changeLastSeen != 0 {
285 lastSeen = time.Now().Add(time.Minute * time.Duration(i*-1))
286 change.LastSeen = &lastSeen
287 expectation.LastSeen = lastSeen
288 }
289 if i&changePushToken != 0 {
290 pushToken = fmt.Sprintf("push-token-%d", i)
291 change.PushToken = &pushToken
292 expectation.PushToken = pushToken
293 }
294 result = ApplyChange(result, change)
295 ok, field, expectedVal, resultVal := compareDevices(expectation, result)
296 if !ok {
297 t.Errorf("Expected %s of %s to be %v, got %v after applying DeviceChange %+v\n", field, device.Name, expectedVal, resultVal, change)
298 }
300 err = storer.CreateDevices([]Device{device}, ctx)
301 if err != nil {
302 t.Errorf("Unexpected error creating devices in %T: %+v\n", storer, err)
303 }
305 err = storer.UpdateDevice(change, ctx)
306 if err != nil {
307 t.Errorf("Unexpected error updating device in %T: %+v\n", storer, err)
308 }
310 retrieved, err := storer.GetDevices([]uuid.ID{device.ID}, ctx)
311 if err != nil {
312 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err)
313 }
314 retrievedDevice, ok := retrieved[device.ID.String()]
315 if !ok {
316 t.Errorf("Expected retrieved devices to contain %s, got %+v from %T\n", device.Name, retrieved, storer)
317 }
318 ok, field, expectedVal, resultVal = compareDevices(expectation, retrievedDevice)
319 if !ok {
320 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, device.Name, expectedVal, resultVal, storer)
321 }
322 }
324 err = factory.TeardownStorer(storer, ctx)
325 if err != nil {
326 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
327 }
328 }
329 }
331 func TestUpdateDeviceNotFound(t *testing.T) {
332 for _, factory := range storerFactories {
333 ctx := context.Background()
334 storer, err := factory.NewStorer(ctx)
335 if err != nil {
336 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
337 }
339 deviceID := uuid.NewID()
340 name := "my new name"
341 change := DeviceChange{DeviceID: deviceID, Name: &name}
343 err = storer.UpdateDevice(change, ctx)
344 if err != ErrDeviceNotFound {
345 t.Errorf("Expected error to be ErrDeviceNotFound, %T returned %+v\n", storer, err)
346 }
348 results, err := storer.GetDevices([]uuid.ID{deviceID}, ctx)
349 if err != nil {
350 t.Errorf("Error retrieving devices from %T: %+v\n", storer, err)
351 }
352 if len(results) != 0 {
353 t.Errorf("Expected no devices in %T, got %+v\n", storer, results)
354 }
356 err = factory.TeardownStorer(storer, ctx)
357 if err != nil {
358 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
359 }
360 }
361 }