github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/collection_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 "gopkg.in/mgo.v2" 10 "gopkg.in/mgo.v2/bson" 11 12 "github.com/juju/juju/state" 13 "github.com/juju/juju/testing/factory" 14 ) 15 16 type collectionSuite struct { 17 ConnSuite 18 } 19 20 var _ = gc.Suite(&collectionSuite{}) 21 22 type collectionTestCase struct { 23 label string 24 test func() (int, error) 25 expectedCount int 26 expectedPanic string 27 expectedError string 28 } 29 30 func (s *collectionSuite) TestGenericStateCollection(c *gc.C) { 31 // The users collection does not require filtering by model UUID. 32 coll, closer := state.GetCollection(s.State, state.UsersC) 33 defer closer() 34 35 c.Check(coll.Name(), gc.Equals, state.UsersC) 36 37 s.Factory.MakeUser(c, &factory.UserParams{Name: "foo", DisplayName: "Ms Foo"}) 38 s.Factory.MakeUser(c, &factory.UserParams{Name: "bar"}) 39 40 collSnapshot := newCollectionSnapshot(c, coll.Writeable().Underlying()) 41 42 for i, t := range []collectionTestCase{ 43 { 44 label: "Count", 45 test: func() (int, error) { 46 return coll.Count() 47 }, 48 expectedCount: 3, 49 }, 50 { 51 label: "FindId", 52 test: func() (int, error) { 53 return coll.FindId("foo").Count() 54 }, 55 expectedCount: 1, 56 }, 57 { 58 label: "Find with one result", 59 test: func() (int, error) { 60 return coll.Find(bson.D{{"displayname", "Ms Foo"}}).Count() 61 }, 62 expectedCount: 1, 63 }, 64 { 65 label: "Find with nil", 66 test: func() (int, error) { 67 return coll.Find(nil).Count() 68 }, 69 expectedCount: 3, 70 }, 71 { 72 label: "Insert", 73 test: func() (int, error) { 74 err := coll.Writeable().Insert(bson.D{{"_id", "more"}}) 75 c.Assert(err, jc.ErrorIsNil) 76 return coll.Count() 77 }, 78 expectedCount: 4, 79 }, 80 { 81 label: "RemoveId", 82 test: func() (int, error) { 83 err := coll.Writeable().RemoveId("bar") 84 c.Assert(err, jc.ErrorIsNil) 85 return coll.Count() 86 }, 87 expectedCount: 2, 88 }, 89 { 90 label: "Remove", 91 test: func() (int, error) { 92 err := coll.Writeable().Remove(bson.D{{"displayname", "Ms Foo"}}) 93 c.Assert(err, jc.ErrorIsNil) 94 return coll.Count() 95 }, 96 expectedCount: 2, 97 }, 98 { 99 label: "RemoveAll", 100 test: func() (int, error) { 101 _, err := coll.Writeable().RemoveAll(bson.D{{"createdby", s.Owner.Name()}}) 102 c.Assert(err, jc.ErrorIsNil) 103 return coll.Count() 104 }, 105 expectedCount: 0, 106 }, 107 { 108 label: "Update", 109 test: func() (int, error) { 110 err := coll.Writeable().Update(bson.D{{"_id", "bar"}}, 111 bson.D{{"$set", bson.D{{"displayname", "Updated Bar"}}}}) 112 c.Assert(err, jc.ErrorIsNil) 113 114 return coll.Find(bson.D{{"displayname", "Updated Bar"}}).Count() 115 }, 116 expectedCount: 1, 117 }, 118 { 119 label: "UpdateId", 120 test: func() (int, error) { 121 err := coll.Writeable().UpdateId("bar", 122 bson.D{{"$set", bson.D{{"displayname", "Updated Bar"}}}}) 123 c.Assert(err, jc.ErrorIsNil) 124 125 return coll.Find(bson.D{{"displayname", "Updated Bar"}}).Count() 126 }, 127 expectedCount: 1, 128 }, 129 } { 130 c.Logf("test %d: %s", i, t.label) 131 collSnapshot.restore(c) 132 133 count, err := t.test() 134 c.Assert(err, jc.ErrorIsNil) 135 c.Check(count, gc.Equals, t.expectedCount) 136 } 137 } 138 139 func (s *collectionSuite) TestModelStateCollection(c *gc.C) { 140 // The machines collection requires filtering by model UUID. Set up 141 // 2 models with machines in each. 142 m0 := s.Factory.MakeMachine(c, nil) 143 s.Factory.MakeMachine(c, nil) 144 st1 := s.Factory.MakeModel(c, nil) 145 defer st1.Close() 146 f1 := factory.NewFactory(st1) 147 otherM0 := f1.MakeMachine(c, &factory.MachineParams{Series: "trusty"}) 148 149 // Ensure that the first machine in each model have overlapping ids 150 // (otherwise tests may not fail when they should) 151 c.Assert(m0.Id(), gc.Equals, otherM0.Id()) 152 153 machines0, closer := state.GetCollection(s.State, state.MachinesC) 154 defer closer() 155 machines1, closer := state.GetCollection(st1, state.MachinesC) 156 defer closer() 157 158 machinesSnapshot := newCollectionSnapshot(c, machines0.Writeable().Underlying()) 159 160 c.Assert(machines0.Name(), gc.Equals, state.MachinesC) 161 162 for i, t := range []collectionTestCase{ 163 { 164 label: "Count filters by model", 165 test: func() (int, error) { 166 return machines0.Count() 167 }, 168 expectedCount: 2, 169 }, 170 { 171 label: "Find filters by model", 172 test: func() (int, error) { 173 return machines0.Find(bson.D{{"series", m0.Series()}}).Count() 174 }, 175 expectedCount: 2, 176 }, 177 { 178 label: "Find adds model UUID when _id is provided", 179 test: func() (int, error) { 180 return machines0.Find(bson.D{{"_id", m0.Id()}}).Count() 181 }, 182 expectedCount: 1, 183 }, 184 { 185 label: "Find tolerates model UUID prefix already being present", 186 test: func() (int, error) { 187 return machines0.Find(bson.D{ 188 {"_id", state.DocID(s.State, m0.Id())}, 189 }).Count() 190 }, 191 expectedCount: 1, 192 }, 193 { 194 label: "Find with no selector still filters by model", 195 test: func() (int, error) { 196 return machines0.Find(nil).Count() 197 }, 198 expectedCount: 2, 199 }, 200 { 201 label: "Find leaves _id alone if used with operator", 202 test: func() (int, error) { 203 return machines0.Find(bson.D{ 204 {"_id", bson.D{{"$regex", ":" + m0.Id() + "$"}}}, 205 }).Count() 206 }, 207 expectedCount: 1, // not 2 because model-uuid filter is still added 208 }, 209 { 210 label: "Find works with maps", 211 test: func() (int, error) { 212 return machines0.Find(map[string]string{"_id": m0.Id()}).Count() 213 }, 214 expectedCount: 1, 215 }, 216 { 217 label: "Find panics if model-uuid is included", 218 test: func() (int, error) { 219 machines0.Find(bson.D{{"model-uuid", "whatever"}}) 220 return 0, nil 221 }, 222 expectedPanic: "model-uuid is added automatically and should not be provided", 223 }, 224 { 225 label: "FindId adds model UUID prefix", 226 test: func() (int, error) { 227 return machines0.FindId(m0.Id()).Count() 228 }, 229 expectedCount: 1, 230 }, 231 { 232 label: "FindId tolerates model UUID prefix already being there", 233 test: func() (int, error) { 234 return machines0.FindId(state.DocID(s.State, m0.Id())).Count() 235 }, 236 expectedCount: 1, 237 }, 238 { 239 label: "Insert adds model-uuid", 240 test: func() (int, error) { 241 err := machines0.Writeable().Insert(bson.D{ 242 {"_id", state.DocID(s.State, "99")}, 243 {"machineid", 99}, 244 }) 245 c.Assert(err, jc.ErrorIsNil) 246 return machines0.Count() 247 }, 248 expectedCount: 3, 249 }, 250 { 251 label: "Insert populates model-uuid if blank", 252 test: func() (int, error) { 253 err := machines0.Writeable().Insert(bson.D{ 254 {"_id", state.DocID(s.State, "99")}, 255 {"machineid", 99}, 256 {"model-uuid", ""}, 257 }) 258 c.Assert(err, jc.ErrorIsNil) 259 return machines0.Count() 260 }, 261 expectedCount: 3, 262 }, 263 { 264 label: "Insert prefixes _id", 265 test: func() (int, error) { 266 err := machines0.Writeable().Insert(bson.D{ 267 {"_id", "99"}, 268 {"machineid", 99}, 269 }) 270 c.Assert(err, jc.ErrorIsNil) 271 return machines0.FindId("99").Count() 272 }, 273 expectedCount: 1, 274 }, 275 { 276 label: "Insert tolerates prefixed _id and correct model-uuid if provided", 277 test: func() (int, error) { 278 err := machines0.Writeable().Insert(bson.D{ 279 {"_id", state.DocID(s.State, "99")}, 280 {"machineid", 99}, 281 {"model-uuid", s.State.ModelUUID()}, 282 }) 283 c.Assert(err, jc.ErrorIsNil) 284 return machines0.Count() 285 }, 286 expectedCount: 3, 287 }, 288 { 289 label: "Insert fails if model-uuid doesn't match", 290 test: func() (int, error) { 291 err := machines0.Writeable().Insert(bson.D{ 292 {"_id", "99"}, 293 {"machineid", 99}, 294 {"model-uuid", "something-else"}, 295 }) 296 return 0, err 297 }, 298 expectedError: "bad \"model-uuid\" value: .+", 299 }, 300 { 301 label: "Remove adds model UUID prefix to _id", 302 test: func() (int, error) { 303 err := machines0.Writeable().Remove(bson.D{{"_id", "0"}}) 304 c.Assert(err, jc.ErrorIsNil) 305 return s.machines.Count() 306 }, 307 expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model 308 }, 309 { 310 label: "Remove filters by model", 311 test: func() (int, error) { 312 // Attempt to remove the trusty machine in the second 313 // model with the collection that's filtering for the 314 // first model - nothing should get removed. 315 err := machines0.Writeable().Remove(bson.D{{"series", "trusty"}}) 316 c.Assert(err, gc.ErrorMatches, "not found") 317 return s.machines.Count() 318 }, 319 expectedCount: 3, // Expect all machines to still be there. 320 }, 321 { 322 label: "Remove filters by model 2", 323 test: func() (int, error) { 324 err := machines0.Writeable().Remove(bson.D{{"machineid", "0"}}) 325 c.Assert(err, jc.ErrorIsNil) 326 return s.machines.Count() 327 }, 328 expectedCount: 2, // Expect machine 1 in first model and machine-0 in second model 329 }, 330 { 331 label: "RemoveId adds model UUID prefix", 332 test: func() (int, error) { 333 err := machines0.Writeable().RemoveId(m0.Id()) 334 c.Assert(err, jc.ErrorIsNil) 335 return s.machines.Count() 336 }, 337 expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model 338 }, 339 { 340 label: "RemoveId tolerates model UUID prefix already being there", 341 test: func() (int, error) { 342 err := machines0.Writeable().RemoveId(state.DocID(s.State, m0.Id())) 343 c.Assert(err, jc.ErrorIsNil) 344 return s.machines.Count() 345 }, 346 expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model 347 }, 348 { 349 label: "RemoveAll filters by model", 350 test: func() (int, error) { 351 _, err := machines0.Writeable().RemoveAll(bson.D{{"series", m0.Series()}}) 352 c.Assert(err, jc.ErrorIsNil) 353 return s.machines.Count() 354 }, 355 expectedCount: 1, // Expect machine-1 in second model 356 }, 357 { 358 label: "RemoveAll adds model UUID when _id is provided", 359 test: func() (int, error) { 360 _, err := machines0.Writeable().RemoveAll(bson.D{{"_id", m0.Id()}}) 361 c.Assert(err, jc.ErrorIsNil) 362 return s.machines.Count() 363 }, 364 expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model 365 }, 366 { 367 label: "RemoveAll tolerates model UUID prefix already being present", 368 test: func() (int, error) { 369 _, err := machines0.Writeable().RemoveAll(bson.D{ 370 {"_id", state.DocID(s.State, m0.Id())}, 371 }) 372 c.Assert(err, jc.ErrorIsNil) 373 return s.machines.Count() 374 }, 375 expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model 376 }, 377 { 378 label: "RemoveAll with no selector still filters by model", 379 test: func() (int, error) { 380 _, err := machines0.Writeable().RemoveAll(nil) 381 c.Assert(err, jc.ErrorIsNil) 382 return s.machines.Count() 383 }, 384 expectedCount: 1, // Expect machine-0 in second model 385 }, 386 { 387 label: "RemoveAll panics if model-uuid is included", 388 test: func() (int, error) { 389 machines0.Writeable().RemoveAll(bson.D{{"model-uuid", "whatever"}}) 390 return 0, nil 391 }, 392 expectedPanic: "model-uuid is added automatically and should not be provided", 393 }, 394 { 395 label: "Update", 396 test: func() (int, error) { 397 err := machines0.Writeable().Update(bson.D{{"_id", m0.Id()}}, 398 bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) 399 c.Assert(err, jc.ErrorIsNil) 400 return machines0.Find(bson.D{{"update-field", "field value"}}).Count() 401 }, 402 expectedCount: 1, 403 }, 404 { 405 label: "UpdateId", 406 test: func() (int, error) { 407 err := machines0.Writeable().UpdateId(m0.Id(), 408 bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) 409 c.Assert(err, jc.ErrorIsNil) 410 return machines0.Find(bson.D{{"update-field", "field value"}}).Count() 411 }, 412 expectedCount: 1, 413 }, 414 } { 415 c.Logf("test %d: %s", i, t.label) 416 machinesSnapshot.restore(c) 417 418 if t.expectedPanic == "" { 419 count, err := t.test() 420 if t.expectedError != "" { 421 c.Assert(err, gc.ErrorMatches, t.expectedError) 422 } else { 423 c.Assert(err, jc.ErrorIsNil) 424 } 425 c.Check(count, gc.Equals, t.expectedCount) 426 } else { 427 c.Check(func() { t.test() }, gc.PanicMatches, t.expectedPanic) 428 } 429 430 // Check that other model is untouched after each test 431 count, err := machines1.Count() 432 c.Assert(err, jc.ErrorIsNil) 433 c.Check(count, gc.Equals, 1) 434 } 435 } 436 437 type collectionSnapshot struct { 438 coll *mgo.Collection 439 origDocs []interface{} 440 } 441 442 func newCollectionSnapshot(c *gc.C, coll *mgo.Collection) *collectionSnapshot { 443 ss := &collectionSnapshot{coll: coll} 444 err := coll.Find(nil).All(&ss.origDocs) 445 c.Assert(err, jc.ErrorIsNil) 446 return ss 447 } 448 449 func (ss *collectionSnapshot) restore(c *gc.C) { 450 _, err := ss.coll.RemoveAll(nil) 451 c.Assert(err, jc.ErrorIsNil) 452 err = ss.coll.Insert(ss.origDocs...) 453 c.Assert(err, jc.ErrorIsNil) 454 }