ducky/devices
10:74dbc04879a7 Browse Files
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.
1.1 --- a/memstore.go Fri Nov 20 01:14:58 2015 -0800 1.2 +++ b/memstore.go Fri Nov 27 10:37:27 2015 -0800 1.3 @@ -35,6 +35,17 @@ 1.4 } 1.5 1.6 func (m *Memstore) UpdateDevice(change DeviceChange, c context.Context) error { 1.7 + m.lock.Lock() 1.8 + defer m.lock.Unlock() 1.9 + 1.10 + device, ok := m.devices[change.DeviceID.String()] 1.11 + if !ok { 1.12 + return nil // TODO: return an error 1.13 + } 1.14 + 1.15 + device = ApplyChange(device, change) 1.16 + m.devices[change.DeviceID.String()] = device 1.17 + 1.18 return nil 1.19 } 1.20
2.1 --- a/storer_test.go Fri Nov 20 01:14:58 2015 -0800 2.2 +++ b/storer_test.go Fri Nov 27 10:37:27 2015 -0800 2.3 @@ -1,6 +1,7 @@ 2.4 package devices 2.5 2.6 import ( 2.7 + "fmt" 2.8 "testing" 2.9 "time" 2.10 2.11 @@ -15,6 +16,16 @@ 2.12 2.13 var storerFactories []StorerFactory 2.14 2.15 +const ( 2.16 + changeName = 1 << iota 2.17 + changeOwner 2.18 + changeType 2.19 + changeCreated 2.20 + changeLastSeen 2.21 + changePushToken 2.22 + changeVariations 2.23 +) 2.24 + 2.25 func compareDevices(device1, device2 Device) (ok bool, field string, expected, result interface{}) { 2.26 if !device1.ID.Equal(device2.ID) { 2.27 return false, "ID", device1.ID, device2.ID 2.28 @@ -42,7 +53,8 @@ 2.29 2.30 func TestCreateAndGetDevices(t *testing.T) { 2.31 for _, factory := range storerFactories { 2.32 - storer, err := factory.NewStorer(context.TODO()) 2.33 + ctx := context.Background() 2.34 + storer, err := factory.NewStorer(ctx) 2.35 if err != nil { 2.36 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err) 2.37 } 2.38 @@ -53,7 +65,7 @@ 2.39 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"}, 2.40 } 2.41 2.42 - err = storer.CreateDevices(devices, context.TODO()) 2.43 + err = storer.CreateDevices(devices, ctx) 2.44 if err != nil { 2.45 t.Errorf("Error creating devices in %T: %+v\n", storer, err) 2.46 } 2.47 @@ -63,7 +75,7 @@ 2.48 ids = append(ids, device.ID) 2.49 } 2.50 2.51 - results, err := storer.GetDevices(ids, context.TODO()) 2.52 + results, err := storer.GetDevices(ids, ctx) 2.53 if err != nil { 2.54 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err) 2.55 } 2.56 @@ -77,7 +89,7 @@ 2.57 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, device.Name, expected, result, storer) 2.58 } 2.59 } 2.60 - err = factory.TeardownStorer(storer, context.TODO()) 2.61 + err = factory.TeardownStorer(storer, ctx) 2.62 if err != nil { 2.63 t.Errorf("Error cleaning up after %T: %+v\n", storer, err) 2.64 } 2.65 @@ -86,19 +98,20 @@ 2.66 2.67 func TestGetDevicesNoErrorForMissing(t *testing.T) { 2.68 for _, factory := range storerFactories { 2.69 - storer, err := factory.NewStorer(context.TODO()) 2.70 + ctx := context.Background() 2.71 + storer, err := factory.NewStorer(ctx) 2.72 if err != nil { 2.73 t.Fatalf("Fatal error creatng Storer from %T: %+v\n", factory, err) 2.74 } 2.75 2.76 - results, err := storer.GetDevices([]uuid.ID{uuid.NewID()}, context.TODO()) 2.77 + results, err := storer.GetDevices([]uuid.ID{uuid.NewID()}, ctx) 2.78 if err != nil { 2.79 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err) 2.80 } 2.81 if len(results) != 0 { 2.82 t.Errorf("Expected results to be empty, got %+v from %T instead\n", results, storer) 2.83 } 2.84 - err = factory.TeardownStorer(storer, context.TODO()) 2.85 + err = factory.TeardownStorer(storer, ctx) 2.86 if err != nil { 2.87 t.Errorf("Error cleaning up after %T: %+v\n", storer, err) 2.88 } 2.89 @@ -107,7 +120,8 @@ 2.90 2.91 func TestCreateDevicesDuplicates(t *testing.T) { 2.92 for _, factory := range storerFactories { 2.93 - storer, err := factory.NewStorer(context.TODO()) 2.94 + ctx := context.Background() 2.95 + storer, err := factory.NewStorer(ctx) 2.96 if err != nil { 2.97 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err) 2.98 } 2.99 @@ -118,7 +132,7 @@ 2.100 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"}, 2.101 } 2.102 2.103 - err = storer.CreateDevices(devices, context.TODO()) 2.104 + err = storer.CreateDevices(devices, ctx) 2.105 if err != nil { 2.106 t.Errorf("Unexpected error creating devices in %T: %+v\n", storer, err) 2.107 } 2.108 @@ -128,7 +142,7 @@ 2.109 {ID: uuid.NewID(), Name: "Test 5", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"}, 2.110 } 2.111 2.112 - err = storer.CreateDevices([]Device{newDevices[0], devices[1], newDevices[1]}, context.TODO()) 2.113 + err = storer.CreateDevices([]Device{newDevices[0], devices[1], newDevices[1]}, ctx) 2.114 daeErr, ok := err.(ErrDeviceAlreadyExists) 2.115 if !ok { 2.116 t.Errorf("Expected ErrDeviceAlreadyExists creating duplicate device in %T, got %+v\n", storer, err) 2.117 @@ -138,7 +152,7 @@ 2.118 } 2.119 2.120 // inserts should be a transaction; they either all make it, or none do 2.121 - results, err := storer.GetDevices([]uuid.ID{newDevices[0].ID, newDevices[1].ID}, context.TODO()) 2.122 + results, err := storer.GetDevices([]uuid.ID{newDevices[0].ID, newDevices[1].ID}, ctx) 2.123 if err != nil { 2.124 t.Errorf("Error retrieving devices from %T: %+v\n", storer, err) 2.125 } 2.126 @@ -146,7 +160,7 @@ 2.127 t.Errorf("Expected new inserts to not be in results, got %+v from %T\n", results, storer) 2.128 } 2.129 2.130 - err = factory.TeardownStorer(storer, context.TODO()) 2.131 + err = factory.TeardownStorer(storer, ctx) 2.132 if err != nil { 2.133 t.Errorf("Error cleaning up after %T: %+v\n", storer, err) 2.134 } 2.135 @@ -155,7 +169,8 @@ 2.136 2.137 func TestCreateAndListDevicesByOwner(t *testing.T) { 2.138 for _, factory := range storerFactories { 2.139 - storer, err := factory.NewStorer(context.TODO()) 2.140 + ctx := context.Background() 2.141 + storer, err := factory.NewStorer(ctx) 2.142 if err != nil { 2.143 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err) 2.144 } 2.145 @@ -167,12 +182,12 @@ 2.146 {ID: uuid.NewID(), Name: "Test 3", Owner: owner1, Type: TypeChromeExtension, Created: time.Now().Add(time.Minute), LastSeen: time.Now(), PushToken: "test token"}, 2.147 } 2.148 2.149 - err = storer.CreateDevices(devices, context.TODO()) 2.150 + err = storer.CreateDevices(devices, ctx) 2.151 if err != nil { 2.152 t.Errorf("Error creating devices in %T: %+v\n", storer, err) 2.153 } 2.154 2.155 - results, err := storer.ListDevicesByOwner(owner1, context.TODO()) 2.156 + results, err := storer.ListDevicesByOwner(owner1, ctx) 2.157 if err != nil { 2.158 t.Errorf("Error listing devices for owner1 from %T: %+v\n", storer, err) 2.159 } 2.160 @@ -197,7 +212,7 @@ 2.161 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[2].Name, expected, result, storer) 2.162 } 2.163 2.164 - results, err = storer.ListDevicesByOwner(owner2, context.TODO()) 2.165 + results, err = storer.ListDevicesByOwner(owner2, ctx) 2.166 if err != nil { 2.167 t.Errorf("Error listing devices for owner2 from %T: %+v\n", storer, err) 2.168 } 2.169 @@ -209,9 +224,106 @@ 2.170 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[1].Name, expected, result, storer) 2.171 } 2.172 2.173 - err = factory.TeardownStorer(storer, context.TODO()) 2.174 + err = factory.TeardownStorer(storer, ctx) 2.175 if err != nil { 2.176 t.Errorf("Error cleaning up after %T: %+v\n", storer, err) 2.177 } 2.178 } 2.179 } 2.180 + 2.181 +func TestUpdateDevicesHappyPath(t *testing.T) { 2.182 + device := Device{ 2.183 + ID: uuid.NewID(), 2.184 + Name: "Test 1", 2.185 + Owner: uuid.NewID(), 2.186 + Type: TypeAndroidPhone, 2.187 + Created: time.Now(), 2.188 + LastSeen: time.Now(), 2.189 + PushToken: "test token", 2.190 + } 2.191 + for _, factory := range storerFactories { 2.192 + ctx := context.Background() 2.193 + storer, err := factory.NewStorer(ctx) 2.194 + if err != nil { 2.195 + t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err) 2.196 + } 2.197 + for i := 1; i < changeVariations; i++ { 2.198 + var change DeviceChange 2.199 + var owner uuid.ID 2.200 + var name, pushToken string 2.201 + var created, lastSeen time.Time 2.202 + var deviceType DeviceType 2.203 + 2.204 + device.ID = uuid.NewID() 2.205 + 2.206 + expectation := device 2.207 + result := device 2.208 + 2.209 + change.DeviceID = device.ID 2.210 + 2.211 + if i&changeName != 0 { 2.212 + name = fmt.Sprintf("name-%d", i) 2.213 + change.Name = &name 2.214 + expectation.Name = name 2.215 + } 2.216 + if i&changeOwner != 0 { 2.217 + owner = uuid.NewID() 2.218 + change.Owner = &owner 2.219 + expectation.Owner = owner 2.220 + } 2.221 + if i&changeType != 0 { 2.222 + deviceType = DeviceType(fmt.Sprintf("type-%d", i)) 2.223 + change.Type = &deviceType 2.224 + expectation.Type = deviceType 2.225 + } 2.226 + if i&changeCreated != 0 { 2.227 + created = time.Now().Add(time.Hour * time.Duration(i)) 2.228 + change.Created = &created 2.229 + expectation.Created = created 2.230 + } 2.231 + if i&changeLastSeen != 0 { 2.232 + lastSeen = time.Now().Add(time.Minute * time.Duration(i*-1)) 2.233 + change.LastSeen = &lastSeen 2.234 + expectation.LastSeen = lastSeen 2.235 + } 2.236 + if i&changePushToken != 0 { 2.237 + pushToken = fmt.Sprintf("push-token-%d", i) 2.238 + change.PushToken = &pushToken 2.239 + expectation.PushToken = pushToken 2.240 + } 2.241 + result = ApplyChange(result, change) 2.242 + ok, field, expectedVal, resultVal := compareDevices(expectation, result) 2.243 + if !ok { 2.244 + t.Errorf("Expected %s of %s to be %v, got %v after applying DeviceChange %+v\n", field, device.Name, expectedVal, resultVal, change) 2.245 + } 2.246 + 2.247 + err = storer.CreateDevices([]Device{device}, ctx) 2.248 + if err != nil { 2.249 + t.Errorf("Unexpected error creating devices in %T: %+v\n", storer, err) 2.250 + } 2.251 + 2.252 + err = storer.UpdateDevice(change, ctx) 2.253 + if err != nil { 2.254 + t.Errorf("Unexpected error updating device in %T: %+v\n", storer, err) 2.255 + } 2.256 + 2.257 + retrieved, err := storer.GetDevices([]uuid.ID{device.ID}, ctx) 2.258 + if err != nil { 2.259 + t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err) 2.260 + } 2.261 + retrievedDevice, ok := retrieved[device.ID.String()] 2.262 + if !ok { 2.263 + t.Errorf("Expected retrieved devices to contain %s, got %+v from %T\n", device.Name, retrieved, storer) 2.264 + } 2.265 + ok, field, expectedVal, resultVal = compareDevices(expectation, retrievedDevice) 2.266 + if !ok { 2.267 + t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, device.Name, expectedVal, resultVal, storer) 2.268 + } 2.269 + } 2.270 + 2.271 + err = factory.TeardownStorer(storer, ctx) 2.272 + if err != nil { 2.273 + t.Errorf("Error cleaning up after %T: %+v\n", storer, err) 2.274 + } 2.275 + } 2.276 +}