ducky/devices
ducky/devices/storer_test.go
Separate out StorerFactory into its own interface. We had previously included a Factory and Destroy method in the Storer interface, but it makes more sense to separate those out into a StorerFactory interface. This lets us avoid the impression that we're using the same instance of a Storer for every test, separates the test methods away from the methods that are actually used in prod, and hides them from Godoc. What's not to like?
1 package devices
3 import (
4 "testing"
5 "time"
7 "code.secondbit.org/uuid.hg"
8 "golang.org/x/net/context"
9 )
11 type StorerFactory interface {
12 NewStorer(ctx context.Context) (Storer, error)
13 TeardownStorer(storer Storer, ctx context.Context) error
14 }
16 var storerFactories []StorerFactory
18 func compareDevices(device1, device2 Device) (ok bool, field string, expected, result interface{}) {
19 if !device1.ID.Equal(device2.ID) {
20 return false, "ID", device1.ID, device2.ID
21 }
22 if device1.Name != device2.Name {
23 return false, "Name", device1.Name, device2.Name
24 }
25 if !device1.Owner.Equal(device2.Owner) {
26 return false, "Owner", device1.Owner, device2.Owner
27 }
28 if device1.Type != device2.Type {
29 return false, "Type", device1.Type, device2.Type
30 }
31 if !device1.Created.Equal(device2.Created) {
32 return false, "Created", device1.Created, device2.Created
33 }
34 if !device1.LastSeen.Equal(device2.LastSeen) {
35 return false, "LastSeen", device1.LastSeen, device2.LastSeen
36 }
37 if device1.PushToken != device2.PushToken {
38 return false, "PushToken", device1.PushToken, device2.PushToken
39 }
40 return true, "", nil, nil
41 }
43 func TestCreateAndGetDevices(t *testing.T) {
44 for _, factory := range storerFactories {
45 storer, err := factory.NewStorer(context.TODO())
46 if err != nil {
47 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
48 }
50 devices := []Device{
51 {ID: uuid.NewID(), Name: "Test 1", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
52 {ID: uuid.NewID(), Name: "Test 2", Owner: uuid.NewID(), Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
53 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
54 }
56 err = storer.CreateDevices(devices, context.TODO())
57 if err != nil {
58 t.Errorf("Error creating devices in %T: %+v\n", storer, err)
59 }
61 ids := make([]uuid.ID, 0, len(devices))
62 for _, device := range devices {
63 ids = append(ids, device.ID)
64 }
66 results, err := storer.GetDevices(ids, context.TODO())
67 if err != nil {
68 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err)
69 }
70 for _, device := range devices {
71 d, returned := results[device.ID.String()]
72 if !returned {
73 t.Errorf("Expected device %s to be in results from %T, but wasn't present\n", device.Name, storer)
74 }
75 ok, field, expected, result := compareDevices(device, d)
76 if !ok {
77 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, device.Name, expected, result, storer)
78 }
79 }
80 err = factory.TeardownStorer(storer, context.TODO())
81 if err != nil {
82 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
83 }
84 }
85 }
87 func TestGetDevicesNoErrorForMissing(t *testing.T) {
88 for _, factory := range storerFactories {
89 storer, err := factory.NewStorer(context.TODO())
90 if err != nil {
91 t.Fatalf("Fatal error creatng Storer from %T: %+v\n", factory, err)
92 }
94 results, err := storer.GetDevices([]uuid.ID{uuid.NewID()}, context.TODO())
95 if err != nil {
96 t.Errorf("Unexpected error retrieving devices from %T: %+v\n", storer, err)
97 }
98 if len(results) != 0 {
99 t.Errorf("Expected results to be empty, got %+v from %T instead\n", results, storer)
100 }
101 err = factory.TeardownStorer(storer, context.TODO())
102 if err != nil {
103 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
104 }
105 }
106 }
108 func TestCreateDevicesDuplicates(t *testing.T) {
109 for _, factory := range storerFactories {
110 storer, err := factory.NewStorer(context.TODO())
111 if err != nil {
112 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
113 }
115 devices := []Device{
116 {ID: uuid.NewID(), Name: "Test 1", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
117 {ID: uuid.NewID(), Name: "Test 2", Owner: uuid.NewID(), Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
118 {ID: uuid.NewID(), Name: "Test 3", Owner: uuid.NewID(), Type: TypeChromeExtension, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
119 }
121 err = storer.CreateDevices(devices, context.TODO())
122 if err != nil {
123 t.Errorf("Unexpected error creating devices in %T: %+v\n", storer, err)
124 }
126 newDevices := []Device{
127 {ID: uuid.NewID(), Name: "Test 4", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
128 {ID: uuid.NewID(), Name: "Test 5", Owner: uuid.NewID(), Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
129 }
131 err = storer.CreateDevices([]Device{newDevices[0], devices[1], newDevices[1]}, context.TODO())
132 daeErr, ok := err.(ErrDeviceAlreadyExists)
133 if !ok {
134 t.Errorf("Expected ErrDeviceAlreadyExists creating duplicate device in %T, got %+v\n", storer, err)
135 }
136 if !uuid.ID(daeErr).Equal(devices[1].ID) {
137 t.Errorf("Expected ErrDeviceAlreadyExists to be %+v, got %+v from %T\n", devices[1].ID, daeErr, storer)
138 }
140 // inserts should be a transaction; they either all make it, or none do
141 results, err := storer.GetDevices([]uuid.ID{newDevices[0].ID, newDevices[1].ID}, context.TODO())
142 if err != nil {
143 t.Errorf("Error retrieving devices from %T: %+v\n", storer, err)
144 }
145 if len(results) != 0 {
146 t.Errorf("Expected new inserts to not be in results, got %+v from %T\n", results, storer)
147 }
149 err = factory.TeardownStorer(storer, context.TODO())
150 if err != nil {
151 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
152 }
153 }
154 }
156 func TestCreateAndListDevicesByOwner(t *testing.T) {
157 for _, factory := range storerFactories {
158 storer, err := factory.NewStorer(context.TODO())
159 if err != nil {
160 t.Fatalf("Fatal error creating Storer from %T: %+v\n", factory, err)
161 }
163 owner1, owner2 := uuid.NewID(), uuid.NewID()
164 devices := []Device{
165 {ID: uuid.NewID(), Name: "Test 1", Owner: owner1, Type: TypeAndroidPhone, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
166 {ID: uuid.NewID(), Name: "Test 2", Owner: owner2, Type: TypeAndroidTablet, Created: time.Now(), LastSeen: time.Now(), PushToken: "test token"},
167 {ID: uuid.NewID(), Name: "Test 3", Owner: owner1, Type: TypeChromeExtension, Created: time.Now().Add(time.Minute), LastSeen: time.Now(), PushToken: "test token"},
168 }
170 err = storer.CreateDevices(devices, context.TODO())
171 if err != nil {
172 t.Errorf("Error creating devices in %T: %+v\n", storer, err)
173 }
175 results, err := storer.ListDevicesByOwner(owner1, context.TODO())
176 if err != nil {
177 t.Errorf("Error listing devices for owner1 from %T: %+v\n", storer, err)
178 }
179 if len(results) != 2 {
180 t.Errorf("Expected %d results for owner1, got %d from %T\n", 2, len(results), storer)
181 }
182 resultMap := ToMap(results)
183 d, ok := resultMap[devices[0].ID.String()]
184 if !ok {
185 t.Errorf("Expected to get %s in results, got %+v from %T\n", devices[0].Name, results, storer)
186 }
187 ok, field, expected, result := compareDevices(devices[0], d)
188 if !ok {
189 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[0].Name, expected, result, storer)
190 }
191 d, ok = resultMap[devices[2].ID.String()]
192 if !ok {
193 t.Errorf("Expected to get %s in results, got %+v from %T\n", devices[2].Name, results, storer)
194 }
195 ok, field, expected, result = compareDevices(devices[2], d)
196 if !ok {
197 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[2].Name, expected, result, storer)
198 }
200 results, err = storer.ListDevicesByOwner(owner2, context.TODO())
201 if err != nil {
202 t.Errorf("Error listing devices for owner2 from %T: %+v\n", storer, err)
203 }
204 if len(results) != 1 {
205 t.Errorf("Expected %d results for owner2, got %d from %T\n", 1, len(results), storer)
206 }
207 ok, field, expected, result = compareDevices(devices[1], results[0])
208 if !ok {
209 t.Errorf("Expected %s of %s to be %v, got %v from %T\n", field, devices[1].Name, expected, result, storer)
210 }
212 err = factory.TeardownStorer(storer, context.TODO())
213 if err != nil {
214 t.Errorf("Error cleaning up after %T: %+v\n", storer, err)
215 }
216 }
217 }