func (s *ActionSuite) TestMergeIds(c *gc.C) { var tests = []struct { changes string adds string removes string expected string }{ {changes: "", adds: "a0,a1", removes: "", expected: "a0,a1"}, {changes: "a0,a1", adds: "", removes: "a0", expected: "a1"}, {changes: "a0,a1", adds: "a2", removes: "a0", expected: "a1,a2"}, {changes: "", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"}, {changes: "", adds: "a0,a1,a2", removes: "a0,a1,a2", expected: ""}, {changes: "a0", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"}, {changes: "a1", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"}, {changes: "a2", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"}, {changes: "a3,a4", adds: "a1,a4,a5", removes: "a1,a3", expected: "a4,a5"}, {changes: "a0,a1,a2", adds: "a1,a4,a5", removes: "a1,a3", expected: "a0,a2,a4,a5"}, } prefix := state.DocID(s.State, "") for ix, test := range tests { updates := mapify(prefix, test.adds, test.removes) changes := sliceify("", test.changes) expected := sliceify("", test.expected) c.Log(fmt.Sprintf("test number %d %#v", ix, test)) err := state.WatcherMergeIds(s.State, &changes, updates, state.ActionNotificationIdToActionId) c.Assert(err, jc.ErrorIsNil) c.Assert(changes, jc.SameContents, expected) } }
func (s *CharmSuite) TestAddCharmUpdatesPlaceholder(c *gc.C) { // Check that adding charms updates any existing placeholder charm // with the same URL. ch := testcharms.Repo.CharmDir("dummy") // Add a placeholder charm. curl := charm.MustParseURL("cs:quantal/dummy-1") err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, jc.ErrorIsNil) // Add a deployed charm. info := state.CharmInfo{ Charm: ch, ID: curl, StoragePath: "dummy-1", SHA256: "dummy-1-sha256", } dummy, err := s.State.AddCharm(info) c.Assert(err, jc.ErrorIsNil) c.Assert(dummy.URL().String(), gc.Equals, curl.String()) // Charm doc has been updated. var docs []state.CharmDoc err = s.charms.FindId(state.DocID(s.State, curl.String())).All(&docs) c.Assert(err, jc.ErrorIsNil) c.Assert(docs, gc.HasLen, 1) c.Assert(docs[0].URL, gc.DeepEquals, curl) c.Assert(docs[0].StoragePath, gc.DeepEquals, info.StoragePath) // No more placeholder charm. _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) }
// assertMachineStorageRefs ensures that the specified machine's set of volume // and filesystem references corresponds exactly to the volume and filesystem // attachments that relate to the machine. func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) { machines, closer := state.GetRawCollection(st, state.MachinesC) defer closer() var doc struct { Volumes []string `bson:"volumes,omitempty"` Filesystems []string `bson:"filesystems,omitempty"` } err := machines.FindId(state.DocID(st, m.Id())).One(&doc) c.Assert(err, jc.ErrorIsNil) have := make(set.Tags) for _, v := range doc.Volumes { have.Add(names.NewVolumeTag(v)) } for _, f := range doc.Filesystems { have.Add(names.NewFilesystemTag(f)) } expect := make(set.Tags) volumeAttachments, err := st.MachineVolumeAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range volumeAttachments { expect.Add(a.Volume()) } filesystemAttachments, err := st.MachineFilesystemAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range filesystemAttachments { expect.Add(a.Filesystem()) } c.Assert(have, jc.DeepEquals, expect) }
func (s *AssignSuite) TestAssignMachinePrincipalsChange(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, jc.ErrorIsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToMachine(machine) c.Assert(err, jc.ErrorIsNil) unit, err = s.wordpress.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToMachine(machine) c.Assert(err, jc.ErrorIsNil) subUnit := s.addSubordinate(c, unit) checkPrincipals := func() []string { docID := state.DocID(s.State, machine.Id()) doc := make(map[string][]string) s.machines.FindId(docID).One(&doc) principals, ok := doc["principals"] if !ok { c.Errorf(`machine document does not have a "principals" field`) } return principals } c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0", "wordpress/1"}) err = subUnit.EnsureDead() c.Assert(err, jc.ErrorIsNil) err = subUnit.Remove() c.Assert(err, jc.ErrorIsNil) err = unit.EnsureDead() c.Assert(err, jc.ErrorIsNil) err = unit.Remove() c.Assert(err, jc.ErrorIsNil) c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0"}) }
func (s *EnvUserSuite) TestCaseUserNameVsId(c *gc.C) { env, err := s.State.Environment() c.Assert(err, jc.ErrorIsNil) user, err := s.State.AddEnvironmentUser(names.NewUserTag("Bob@RandomProvider"), env.Owner(), "") c.Assert(err, gc.IsNil) c.Assert(user.UserName(), gc.Equals, "Bob@RandomProvider") c.Assert(user.ID(), gc.Equals, state.DocID(s.State, "bob@randomprovider")) }
func (s *ModelUserSuite) TestCaseUserNameVsId(c *gc.C) { model, err := s.State.Model() c.Assert(err, jc.ErrorIsNil) user, err := s.State.AddModelUser(state.ModelUserSpec{ User: names.NewUserTag("Bob@RandomProvider"), CreatedBy: model.Owner()}) c.Assert(err, gc.IsNil) c.Assert(user.UserName(), gc.Equals, "Bob@RandomProvider") c.Assert(user.ID(), gc.Equals, state.DocID(s.State, "bob@randomprovider")) }
func (s *StorageStateSuite) storageInstanceExists(c *gc.C, tag names.StorageTag) bool { _, err := state.TxnRevno( s.State, state.StorageInstancesC, state.DocID(s.State, tag.Id()), ) if err != nil { c.Assert(err, gc.Equals, mgo.ErrNotFound) return false } return true }
func (s *CharmSuite) TestAddCharm(c *gc.C) { // Check that adding charms from scratch works correctly. info := s.dummyCharm(c, "") dummy, err := s.State.AddCharm(info) c.Assert(err, jc.ErrorIsNil) c.Assert(dummy.URL().String(), gc.Equals, info.ID.String()) doc := state.CharmDoc{} err = s.charms.FindId(state.DocID(s.State, info.ID.String())).One(&doc) c.Assert(err, jc.ErrorIsNil) c.Logf("%#v", doc) c.Assert(doc.URL, gc.DeepEquals, info.ID) }
func (s *BlockDevicesSuite) TestSetMachineBlockDevicesUnchanged(c *gc.C) { sda := state.BlockDeviceInfo{DeviceName: "sda"} err := s.machine.SetMachineBlockDevices(sda) c.Assert(err, jc.ErrorIsNil) s.assertBlockDevices(c, s.machine.MachineTag(), []state.BlockDeviceInfo{sda}) // Setting the same should not change txn-revno. docID := state.DocID(s.State, s.machine.Id()) before, err := state.TxnRevno(s.State, state.BlockDevicesC, docID) c.Assert(err, jc.ErrorIsNil) err = s.machine.SetMachineBlockDevices(sda) c.Assert(err, jc.ErrorIsNil) after, err := state.TxnRevno(s.State, state.BlockDevicesC, docID) c.Assert(err, jc.ErrorIsNil) c.Assert(after, gc.Equals, before) }
func (s *CharmSuite) assertPlaceholderCharmExists(c *gc.C, curl *charm.URL) { // Find charm directly and verify only the charm URL and // Placeholder are set. doc := state.CharmDoc{} err := s.charms.FindId(state.DocID(s.State, curl.String())).One(&doc) c.Assert(err, jc.ErrorIsNil) c.Assert(doc.URL, gc.DeepEquals, curl) c.Assert(doc.PendingUpload, jc.IsFalse) c.Assert(doc.Placeholder, jc.IsTrue) c.Assert(doc.Meta, gc.IsNil) c.Assert(doc.Config, gc.IsNil) c.Assert(doc.StoragePath, gc.Equals, "") c.Assert(doc.BundleSha256, gc.Equals, "") // Make sure we can't find it with st.Charm(). _, err = s.State.Charm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) }
func (s *ActionSuite) TestMakeIdFilter(c *gc.C) { marker := "-marker-" badmarker := "-bad-" fn := state.WatcherMakeIdFilter(s.State, marker) c.Assert(fn, gc.IsNil) ar1 := mockAR{id: "mock/1"} ar2 := mockAR{id: "mock/2"} fn = state.WatcherMakeIdFilter(s.State, marker, ar1, ar2) c.Assert(fn, gc.Not(gc.IsNil)) var tests = []struct { id string match bool }{ {id: "mock/1" + marker + "", match: true}, {id: "mock/1" + marker + "asdf", match: true}, {id: "mock/2" + marker + "", match: true}, {id: "mock/2" + marker + "asdf", match: true}, {id: "mock/1" + badmarker + "", match: false}, {id: "mock/1" + badmarker + "asdf", match: false}, {id: "mock/2" + badmarker + "", match: false}, {id: "mock/2" + badmarker + "asdf", match: false}, {id: "mock/1" + marker + "0", match: true}, {id: "mock/10" + marker + "0", match: false}, {id: "mock/2" + marker + "0", match: true}, {id: "mock/20" + marker + "0", match: false}, {id: "mock" + marker + "0", match: false}, {id: "" + marker + "0", match: false}, {id: "mock/1-0", match: false}, {id: "mock/1-0", match: false}, } for _, test := range tests { c.Assert(fn(state.DocID(s.State, test.id)), gc.Equals, test.match) } }
func (l *unitLife) id() (coll string, id interface{}) { return "units", state.DocID(l.st, l.unit.Name()) }
func (l *machineLife) id() (coll string, id interface{}) { return "machines", state.DocID(l.st, l.machine.Id()) }
func (s *CollectionsSuite) TestEnvStateCollection(c *gc.C) { // The machines collection requires filtering by env UUID. Set up // 2 environments with machines in each. m0 := s.Factory.MakeMachine(c, nil) s.Factory.MakeMachine(c, nil) st1 := s.Factory.MakeEnvironment(c, nil) defer st1.Close() f1 := factory.NewFactory(st1) otherM0 := f1.MakeMachine(c, &factory.MachineParams{Series: "trusty"}) // Ensure that the first machine in each env have overlapping ids // (otherwise tests may not fail when they should) c.Assert(m0.Id(), gc.Equals, otherM0.Id()) getIfaceId := func(st *state.State) bson.ObjectId { var doc bson.M coll, closer := state.GetRawCollection(st, state.NetworkInterfacesC) defer closer() err := coll.Find(bson.D{{"env-uuid", st.EnvironUUID()}}).One(&doc) c.Assert(err, jc.ErrorIsNil) return doc["_id"].(bson.ObjectId) } // Also add a network interface to test collections with ObjectId ids _, err := s.State.AddNetwork(state.NetworkInfo{"net1", "net1", "0.1.2.3/24", 0}) c.Assert(err, jc.ErrorIsNil) _, err = m0.AddNetworkInterface(state.NetworkInterfaceInfo{ MACAddress: "91:de:f1:02:f6:f0", InterfaceName: "foo0", NetworkName: "net1", }) c.Assert(err, jc.ErrorIsNil) // Grab the document id of the just added network interface for use in tests. ifaceId := getIfaceId(s.State) // Add a network interface to the other environment to test collections that rely on the env-uuid field. _, err = st1.AddNetwork(state.NetworkInfo{"net2", "net2", "0.1.2.4/24", 0}) c.Assert(err, jc.ErrorIsNil) _, err = otherM0.AddNetworkInterface(state.NetworkInterfaceInfo{ MACAddress: "91:de:f1:02:f6:f0", InterfaceName: "foo1", NetworkName: "net2", }) c.Assert(err, jc.ErrorIsNil) // Grab the document id of the network interface just added to the other environment for use in tests. otherIfaceId := getIfaceId(st1) machines0, closer := state.GetCollection(s.State, state.MachinesC) defer closer() machines1, closer := state.GetCollection(st1, state.MachinesC) defer closer() networkInterfaces, closer := state.GetCollection(s.State, state.NetworkInterfacesC) defer closer() machinesSnapshot := newCollectionSnapshot(c, machines0.Writeable().Underlying()) networkInterfacesSnapshot := newCollectionSnapshot(c, networkInterfaces.Writeable().Underlying()) c.Assert(machines0.Name(), gc.Equals, state.MachinesC) c.Assert(networkInterfaces.Name(), gc.Equals, state.NetworkInterfacesC) for i, t := range []collectionsTestCase{ { label: "Count filters by env", test: func() (int, error) { return machines0.Count() }, expectedCount: 2, }, { label: "Find filters by env", test: func() (int, error) { return machines0.Find(bson.D{{"series", m0.Series()}}).Count() }, expectedCount: 2, }, { label: "Find adds env UUID when _id is provided", test: func() (int, error) { return machines0.Find(bson.D{{"_id", m0.Id()}}).Count() }, expectedCount: 1, }, { label: "Find tolerates env UUID prefix already being present", test: func() (int, error) { return machines0.Find(bson.D{ {"_id", state.DocID(s.State, m0.Id())}, }).Count() }, expectedCount: 1, }, { label: "Find with no selector still filters by env", test: func() (int, error) { return machines0.Find(nil).Count() }, expectedCount: 2, }, { label: "Find leaves _id alone if used with operator", test: func() (int, error) { return machines0.Find(bson.D{ {"_id", bson.D{{"$regex", ":" + m0.Id() + "$"}}}, }).Count() }, expectedCount: 1, // not 2 because env-uuid filter is still added }, { label: "Find works with collections with ObjectId ids", test: func() (int, error) { return networkInterfaces.Find(bson.D{{"interfacename", "foo0"}}).Count() }, expectedCount: 1, }, { label: "Find works with ObjectId ids", test: func() (int, error) { return networkInterfaces.Find(bson.D{{"_id", ifaceId}}).Count() }, expectedCount: 1, }, { label: "Find panics if env-uuid is included", test: func() (int, error) { machines0.Find(bson.D{{"env-uuid", "whatever"}}) return 0, nil }, expectedPanic: "env-uuid is added automatically and should not be provided", }, { label: "Find panics if query type is unsupported", test: func() (int, error) { machines0.Find(map[string]string{"foo": "bar"}) return 0, nil }, expectedPanic: "query must be bson.D, bson.M, or nil", }, { label: "FindId adds env UUID prefix", test: func() (int, error) { return machines0.FindId(m0.Id()).Count() }, expectedCount: 1, }, { label: "FindId tolerates env UUID prefix already being there", test: func() (int, error) { return machines0.FindId(state.DocID(s.State, m0.Id())).Count() }, expectedCount: 1, }, { label: "FindId works with ObjectId ids", test: func() (int, error) { return networkInterfaces.FindId(ifaceId).Count() }, expectedCount: 1, }, { label: "FindId adds env-uuid field", test: func() (int, error) { return networkInterfaces.FindId(otherIfaceId).Count() }, // expect to find no networks, as we are searching with the id of // the network in the other environment. expectedCount: 0, }, { label: "Insert works", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", state.DocID(s.State, "99")}, {"machineid", 99}, {"env-uuid", s.State.EnvironUUID()}, }) c.Assert(err, jc.ErrorIsNil) return machines0.Count() }, expectedCount: 3, }, { label: "Remove adds env UUID prefix to _id", test: func() (int, error) { err := machines0.Writeable().Remove(bson.D{{"_id", "0"}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env }, { label: "Remove filters by env", test: func() (int, error) { // Attempt to remove the trusty machine in the second // env with the collection that's filtering for the // first env - nothing should get removed. err := machines0.Writeable().Remove(bson.D{{"series", "trusty"}}) c.Assert(err, gc.ErrorMatches, "not found") return s.machines.Count() }, expectedCount: 3, // Expect all machines to still be there. }, { label: "Remove filters by env 2", test: func() (int, error) { err := machines0.Writeable().Remove(bson.D{{"machineid", "0"}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine 1 in first env and machine-0 in second env }, { label: "RemoveId adds env UUID prefix", test: func() (int, error) { err := machines0.Writeable().RemoveId(m0.Id()) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env }, { label: "RemoveId tolerates env UUID prefix already being there", test: func() (int, error) { err := machines0.Writeable().RemoveId(state.DocID(s.State, m0.Id())) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env }, { label: "RemoveId filters by env-uuid field", test: func() (int, error) { err := networkInterfaces.Writeable().RemoveId(otherIfaceId) c.Assert(err, gc.ErrorMatches, "not found") return networkInterfaces.Count() }, expectedCount: 1, // ensure doc was not removed }, { label: "RemoveAll filters by env", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{{"series", m0.Series()}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 1, // Expect machine-1 in second env }, { label: "RemoveAll adds env UUID when _id is provided", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{{"_id", m0.Id()}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env }, { label: "RemoveAll tolerates env UUID prefix already being present", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{ {"_id", state.DocID(s.State, m0.Id())}, }) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env }, { label: "RemoveAll with no selector still filters by env", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(nil) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 1, // Expect machine-0 in second env }, { label: "RemoveAll panics if env-uuid is included", test: func() (int, error) { machines0.Writeable().RemoveAll(bson.D{{"env-uuid", "whatever"}}) return 0, nil }, expectedPanic: "env-uuid is added automatically and should not be provided", }, { label: "RemoveAll panics if query type is unsupported", test: func() (int, error) { machines0.Writeable().RemoveAll(map[string]string{"foo": "bar"}) return 0, nil }, expectedPanic: "query must be bson.D, bson.M, or nil", }, { label: "Update", test: func() (int, error) { err := machines0.Writeable().Update(bson.D{{"_id", m0.Id()}}, bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) c.Assert(err, jc.ErrorIsNil) return machines0.Find(bson.D{{"update-field", "field value"}}).Count() }, expectedCount: 1, }, { label: "UpdateId", test: func() (int, error) { err := machines0.Writeable().UpdateId(m0.Id(), bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) c.Assert(err, jc.ErrorIsNil) return machines0.Find(bson.D{{"update-field", "field value"}}).Count() }, expectedCount: 1, }, } { c.Logf("test %d: %s", i, t.label) machinesSnapshot.restore(c) networkInterfacesSnapshot.restore(c) if t.expectedPanic == "" { count, err := t.test() if t.expectedError != "" { c.Assert(err, gc.ErrorMatches, t.expectedError) } else { c.Assert(err, jc.ErrorIsNil) } c.Check(count, gc.Equals, t.expectedCount) } else { c.Check(func() { t.test() }, gc.PanicMatches, t.expectedPanic) } // Check that other environment is untouched after each test count, err := machines1.Count() c.Assert(err, jc.ErrorIsNil) c.Check(count, gc.Equals, 1) } }
func (s *collectionSuite) TestModelStateCollection(c *gc.C) { // The machines collection requires filtering by model UUID. Set up // 2 models with machines in each. m0 := s.Factory.MakeMachine(c, nil) s.Factory.MakeMachine(c, nil) st1 := s.Factory.MakeModel(c, nil) defer st1.Close() f1 := factory.NewFactory(st1) otherM0 := f1.MakeMachine(c, &factory.MachineParams{Series: "trusty"}) // Ensure that the first machine in each model have overlapping ids // (otherwise tests may not fail when they should) c.Assert(m0.Id(), gc.Equals, otherM0.Id()) machines0, closer := state.GetCollection(s.State, state.MachinesC) defer closer() machines1, closer := state.GetCollection(st1, state.MachinesC) defer closer() machinesSnapshot := newCollectionSnapshot(c, machines0.Writeable().Underlying()) c.Assert(machines0.Name(), gc.Equals, state.MachinesC) for i, t := range []collectionTestCase{ { label: "Count filters by model", test: func() (int, error) { return machines0.Count() }, expectedCount: 2, }, { label: "Find filters by model", test: func() (int, error) { return machines0.Find(bson.D{{"series", m0.Series()}}).Count() }, expectedCount: 2, }, { label: "Find adds model UUID when _id is provided", test: func() (int, error) { return machines0.Find(bson.D{{"_id", m0.Id()}}).Count() }, expectedCount: 1, }, { label: "Find tolerates model UUID prefix already being present", test: func() (int, error) { return machines0.Find(bson.D{ {"_id", state.DocID(s.State, m0.Id())}, }).Count() }, expectedCount: 1, }, { label: "Find with no selector still filters by model", test: func() (int, error) { return machines0.Find(nil).Count() }, expectedCount: 2, }, { label: "Find leaves _id alone if used with operator", test: func() (int, error) { return machines0.Find(bson.D{ {"_id", bson.D{{"$regex", ":" + m0.Id() + "$"}}}, }).Count() }, expectedCount: 1, // not 2 because model-uuid filter is still added }, { label: "Find works with maps", test: func() (int, error) { return machines0.Find(map[string]string{"_id": m0.Id()}).Count() }, expectedCount: 1, }, { label: "Find panics if model-uuid is included", test: func() (int, error) { machines0.Find(bson.D{{"model-uuid", "whatever"}}) return 0, nil }, expectedPanic: "model-uuid is added automatically and should not be provided", }, { label: "FindId adds model UUID prefix", test: func() (int, error) { return machines0.FindId(m0.Id()).Count() }, expectedCount: 1, }, { label: "FindId tolerates model UUID prefix already being there", test: func() (int, error) { return machines0.FindId(state.DocID(s.State, m0.Id())).Count() }, expectedCount: 1, }, { label: "Insert adds model-uuid", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", state.DocID(s.State, "99")}, {"machineid", 99}, }) c.Assert(err, jc.ErrorIsNil) return machines0.Count() }, expectedCount: 3, }, { label: "Insert populates model-uuid if blank", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", state.DocID(s.State, "99")}, {"machineid", 99}, {"model-uuid", ""}, }) c.Assert(err, jc.ErrorIsNil) return machines0.Count() }, expectedCount: 3, }, { label: "Insert prefixes _id", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", "99"}, {"machineid", 99}, }) c.Assert(err, jc.ErrorIsNil) return machines0.FindId("99").Count() }, expectedCount: 1, }, { label: "Insert tolerates prefixed _id and correct model-uuid if provided", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", state.DocID(s.State, "99")}, {"machineid", 99}, {"model-uuid", s.State.ModelUUID()}, }) c.Assert(err, jc.ErrorIsNil) return machines0.Count() }, expectedCount: 3, }, { label: "Insert fails if model-uuid doesn't match", test: func() (int, error) { err := machines0.Writeable().Insert(bson.D{ {"_id", "99"}, {"machineid", 99}, {"model-uuid", "something-else"}, }) return 0, err }, expectedError: "bad \"model-uuid\" value: .+", }, { label: "Remove adds model UUID prefix to _id", test: func() (int, error) { err := machines0.Writeable().Remove(bson.D{{"_id", "0"}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model }, { label: "Remove filters by model", test: func() (int, error) { // Attempt to remove the trusty machine in the second // model with the collection that's filtering for the // first model - nothing should get removed. err := machines0.Writeable().Remove(bson.D{{"series", "trusty"}}) c.Assert(err, gc.ErrorMatches, "not found") return s.machines.Count() }, expectedCount: 3, // Expect all machines to still be there. }, { label: "Remove filters by model 2", test: func() (int, error) { err := machines0.Writeable().Remove(bson.D{{"machineid", "0"}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine 1 in first model and machine-0 in second model }, { label: "RemoveId adds model UUID prefix", test: func() (int, error) { err := machines0.Writeable().RemoveId(m0.Id()) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model }, { label: "RemoveId tolerates model UUID prefix already being there", test: func() (int, error) { err := machines0.Writeable().RemoveId(state.DocID(s.State, m0.Id())) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model }, { label: "RemoveAll filters by model", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{{"series", m0.Series()}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 1, // Expect machine-1 in second model }, { label: "RemoveAll adds model UUID when _id is provided", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{{"_id", m0.Id()}}) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model }, { label: "RemoveAll tolerates model UUID prefix already being present", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(bson.D{ {"_id", state.DocID(s.State, m0.Id())}, }) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model }, { label: "RemoveAll with no selector still filters by model", test: func() (int, error) { _, err := machines0.Writeable().RemoveAll(nil) c.Assert(err, jc.ErrorIsNil) return s.machines.Count() }, expectedCount: 1, // Expect machine-0 in second model }, { label: "RemoveAll panics if model-uuid is included", test: func() (int, error) { machines0.Writeable().RemoveAll(bson.D{{"model-uuid", "whatever"}}) return 0, nil }, expectedPanic: "model-uuid is added automatically and should not be provided", }, { label: "Update", test: func() (int, error) { err := machines0.Writeable().Update(bson.D{{"_id", m0.Id()}}, bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) c.Assert(err, jc.ErrorIsNil) return machines0.Find(bson.D{{"update-field", "field value"}}).Count() }, expectedCount: 1, }, { label: "UpdateId", test: func() (int, error) { err := machines0.Writeable().UpdateId(m0.Id(), bson.D{{"$set", bson.D{{"update-field", "field value"}}}}) c.Assert(err, jc.ErrorIsNil) return machines0.Find(bson.D{{"update-field", "field value"}}).Count() }, expectedCount: 1, }, } { c.Logf("test %d: %s", i, t.label) machinesSnapshot.restore(c) if t.expectedPanic == "" { count, err := t.test() if t.expectedError != "" { c.Assert(err, gc.ErrorMatches, t.expectedError) } else { c.Assert(err, jc.ErrorIsNil) } c.Check(count, gc.Equals, t.expectedCount) } else { c.Check(func() { t.test() }, gc.PanicMatches, t.expectedPanic) } // Check that other model is untouched after each test count, err := machines1.Count() c.Assert(err, jc.ErrorIsNil) c.Check(count, gc.Equals, 1) } }