ducky/devices

Paddy 2015-11-27 Parent:f5a9d5f8f28d Child:683050b4546b

10:74dbc04879a7 Go to Latest

ducky/devices/storer_test.go

Implement and test updating devices, and reuse contexts. Update all our tests to use the same context.Context instance within each test case, so static analysis about how we're passing contexts around dosn't get tripped up. Also, write a test that will check to make sure that our Storer implementations all actually update the Device correctly. We create every possible permutation of a DeviceChange, just to make sure they all work.

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