Beispiel #1
0
func (s *statusSuite) TestPruneStatusHistory(c *gc.C) {
	var oldDoc state.StatusDoc
	var err error
	st := s.State
	globalKey := "BogusKey"
	for changeno := 1; changeno <= 200; changeno++ {
		oldDoc = state.StatusDoc{
			EnvUUID:    st.EnvironUUID(),
			Status:     "AGivenStatus",
			StatusInfo: fmt.Sprintf("Status change %d", changeno),
			StatusData: nil,
		}
		timestamp := state.NowToTheSecond()
		oldDoc.Updated = &timestamp

		hDoc := state.NewHistoricalStatusDoc(changeno, oldDoc, globalKey)

		history, closer := state.GetCollection(st, state.StatusesHistoryC)
		historyW := history.Writeable()
		err = historyW.Insert(hDoc)
		closer()

		c.Logf("Adding a history entry attempt n: %d", changeno)
		c.Assert(err, jc.ErrorIsNil)
	}
	history, err := state.StatusHistory(500, globalKey, st)
	c.Assert(history, gc.HasLen, 200)
	c.Assert(history[0].Message, gc.Equals, "Status change 200")
	c.Assert(history[199].Message, gc.Equals, "Status change 1")

	err = state.PruneStatusHistory(st, 100)
	c.Assert(err, jc.ErrorIsNil)
	history, err = state.StatusHistory(500, globalKey, st)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(history, gc.HasLen, 100)
	c.Assert(history[0].Message, gc.Equals, "Status change 200")
	c.Assert(history[99].Message, gc.Equals, "Status change 101")
}
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 *CollectionsSuite) TestGenericStateCollection(c *gc.C) {
	// The users collection does not require filtering by env UUID.
	coll, closer := state.GetCollection(s.State, state.UsersC)
	defer closer()

	c.Check(coll.Name(), gc.Equals, state.UsersC)

	s.Factory.MakeUser(c, &factory.UserParams{Name: "foo", DisplayName: "Ms Foo"})
	s.Factory.MakeUser(c, &factory.UserParams{Name: "bar"})

	collSnapshot := newCollectionSnapshot(c, coll.Writeable().Underlying())

	for i, t := range []collectionsTestCase{
		{
			label: "Count",
			test: func() (int, error) {
				return coll.Count()
			},
			expectedCount: 3,
		},
		{
			label: "FindId",
			test: func() (int, error) {
				return coll.FindId("foo").Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find with one result",
			test: func() (int, error) {
				return coll.Find(bson.D{{"displayname", "Ms Foo"}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find with nil",
			test: func() (int, error) {
				return coll.Find(nil).Count()
			},
			expectedCount: 3,
		},
		{
			label: "Insert",
			test: func() (int, error) {
				err := coll.Writeable().Insert(bson.D{{"_id", "more"}})
				c.Assert(err, jc.ErrorIsNil)
				return coll.Count()
			},
			expectedCount: 4,
		},
		{
			label: "RemoveId",
			test: func() (int, error) {
				err := coll.Writeable().RemoveId("bar")
				c.Assert(err, jc.ErrorIsNil)
				return coll.Count()
			},
			expectedCount: 2,
		},
		{
			label: "Remove",
			test: func() (int, error) {
				err := coll.Writeable().Remove(bson.D{{"displayname", "Ms Foo"}})
				c.Assert(err, jc.ErrorIsNil)
				return coll.Count()
			},
			expectedCount: 2,
		},
		{
			label: "RemoveAll",
			test: func() (int, error) {
				_, err := coll.Writeable().RemoveAll(bson.D{{"createdby", s.Owner.Name()}})
				c.Assert(err, jc.ErrorIsNil)
				return coll.Count()
			},
			expectedCount: 0,
		},
		{
			label: "Update",
			test: func() (int, error) {
				err := coll.Writeable().Update(bson.D{{"_id", "bar"}},
					bson.D{{"$set", bson.D{{"displayname", "Updated Bar"}}}})
				c.Assert(err, jc.ErrorIsNil)

				return coll.Find(bson.D{{"displayname", "Updated Bar"}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "UpdateId",
			test: func() (int, error) {
				err := coll.Writeable().UpdateId("bar",
					bson.D{{"$set", bson.D{{"displayname", "Updated Bar"}}}})
				c.Assert(err, jc.ErrorIsNil)

				return coll.Find(bson.D{{"displayname", "Updated Bar"}}).Count()
			},
			expectedCount: 1,
		},
	} {
		c.Logf("test %d: %s", i, t.label)
		collSnapshot.restore(c)

		count, err := t.test()
		c.Assert(err, jc.ErrorIsNil)
		c.Check(count, gc.Equals, t.expectedCount)
	}
}
Beispiel #4
0
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)
	}
}