<commit_before>package mstate_test

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	state "launchpad.net/juju-core/mstate"
)

type RelationSuite struct {
	ConnSuite
	charm *state.Charm
}

var _ = Suite(&RelationSuite{})

func (s *RelationSuite) SetUpTest(c *C) {
	s.ConnSuite.SetUpTest(c)
	s.charm = s.AddTestingCharm(c, "dummy")
}

func (s *RelationSuite) TestRelationErrors(c *C) {
	req, err := s.State.AddService("req", s.charm)
	c.Assert(err, IsNil)
	reqep := state.RelationEndpoint{"req", "ifce", "bar", state.RoleRequirer, charm.ScopeGlobal}

	// Check we can't add a relation until both services exist.
	proep := state.RelationEndpoint{"pro", "ifce", "foo", state.RoleProvider, charm.ScopeGlobal}
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": .*`)
	assertNoRelations(c, req)
	pro, err := s.State.AddService("pro", s.charm)
	c.Assert(err, IsNil)

	// Check that interfaces have to match.
	proep2 := state.RelationEndpoint{"pro", "other", "foo", state.RoleProvider, charm.ScopeGlobal}
	_, err = s.State.AddRelation(proep2, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": endpoints do not relate`)
	assertNoRelations(c, pro)
	assertNoRelations(c, req)

	// Check a variety of surprising endpoint combinations.
	_, err = s.State.AddRelation(reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "req:bar": single endpoint must be a peer relation`)
	assertNoRelations(c, req)

	peer, err := s.State.AddService("peer", s.charm)
	c.Assert(err, IsNil)
	peerep := state.RelationEndpoint{"peer", "ifce", "baz", state.RolePeer, charm.ScopeGlobal}
	_, err = s.State.AddRelation(peerep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz req:bar": endpoints do not relate`)
	assertNoRelations(c, peer)
	assertNoRelations(c, req)

	_, err = s.State.AddRelation(peerep, peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz peer:baz": endpoints do not relate`)
	assertNoRelations(c, peer)

	_, err = s.State.AddRelation()
	c.Assert(err, ErrorMatches, `cannot add relation "": cannot relate 0 endpoints`)
	_, err = s.State.AddRelation(proep, reqep, peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz pro:foo req:bar": cannot relate 3 endpoints`)

	_, err = s.State.Relation(peerep)
	c.Assert(err, ErrorMatches, `cannot get relation "peer:baz": .*`)
}

func (s *RelationSuite) TestProviderRequirerRelation(c *C) {
	req, err := s.State.AddService("req", s.charm)
	c.Assert(err, IsNil)
	pro, err := s.State.AddService("pro", s.charm)
	c.Assert(err, IsNil)
	assertNoRelations(c, req)
	assertNoRelations(c, pro)

	// Add a relation, and check we can only do so once.
	proep := state.RelationEndpoint{"pro", "ifce", "foo", state.RoleProvider, charm.ScopeGlobal}
	reqep := state.RelationEndpoint{"req", "ifce", "bar", state.RoleRequirer, charm.ScopeGlobal}
	rel, err := s.State.AddRelation(proep, reqep)
	c.Assert(err, IsNil)
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": .*`)
	assertOneRelation(c, pro, 0, proep, reqep)
	assertOneRelation(c, req, 0, reqep, proep)

	// Remove the relation, and check it can't be removed again.
	err = rel.SetLife(state.Dying)
	c.Assert(err, IsNil)
	err = rel.SetLife(state.Dead)
	c.Assert(err, IsNil)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, IsNil)
	assertNoRelations(c, pro)
	assertNoRelations(c, req)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, ErrorMatches, `cannot remove relation "pro:foo req:bar": not found`)

	// Check that we can add it again if we want to; but this time,
	// give one of the endpoints container scope and check that both
	// resulting service relations get that scope.
	reqep.RelationScope = charm.ScopeContainer
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, IsNil)
	// After adding relation, make proep container-scoped as well, for
	// simplicity of testing.
	proep.RelationScope = charm.ScopeContainer
	assertOneRelation(c, req, 2, reqep, proep)
	assertOneRelation(c, pro, 2, proep, reqep)
}

func (s *RelationSuite) TestPeerRelation(c *C) {
	peer, err := s.State.AddService("peer", s.charm)
	c.Assert(err, IsNil)
	peerep := state.RelationEndpoint{"peer", "ifce", "baz", state.RolePeer, charm.ScopeGlobal}
	assertNoRelations(c, peer)

	// Add a relation, and check we can only do so once.
	rel, err := s.State.AddRelation(peerep)
	c.Assert(err, IsNil)
	_, err = s.State.AddRelation(peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz": .*`)
	assertOneRelation(c, peer, 0, peerep)

	// Remove the relation, and check it can't be removed again.
	err = rel.SetLife(state.Dying)
	c.Assert(err, IsNil)
	err = rel.SetLife(state.Dead)
	c.Assert(err, IsNil)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, IsNil)
	assertNoRelations(c, peer)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, ErrorMatches, `cannot remove relation "peer:baz": not found`)
}

var stateChanges = []struct {
	cached, desired    state.Life
	dbinitial, dbfinal state.Life
}{
	{state.Alive, state.Dying, state.Alive, state.Dying},
	{state.Alive, state.Dying, state.Dying, state.Dying},
	{state.Alive, state.Dying, state.Dead, state.Dead},
	{state.Dying, state.Dying, state.Dying, state.Dying},
	{state.Dying, state.Dying, state.Dead, state.Dead},
	{state.Dying, state.Dead, state.Dying, state.Dead},
	{state.Dying, state.Dead, state.Dead, state.Dead},
	{state.Dead, state.Dead, state.Dead, state.Dead},
}

func createRelationFromDoc(c *C, s *RelationSuite, doc *state.RelationDoc, dblife state.Life) *state.Relation {
	rdoc := *doc
	rdoc.Life = dblife
	err := s.relations.Insert(rdoc)
	c.Assert(err, IsNil)
	return state.NewRelation(s.State, doc)
}

func (s *RelationSuite) TestLifecycleStateChanges(c *C) {
	for _, v := range stateChanges {
		doc := state.RelationDoc{
			Id:   0,
			Life: v.cached,
		}
		r := createRelationFromDoc(c, s, &doc, v.dbinitial)
		r.SetLife(v.desired)
		err := s.relations.FindId(0).One(&doc)
		c.Assert(err, IsNil)
		c.Assert(doc.Life, Equals, v.dbfinal)
		err = s.relations.DropCollection()
		c.Assert(err, IsNil)
	}
}

func assertNoRelations(c *C, srv *state.Service) {
	rels, err := srv.Relations()
	c.Assert(err, IsNil)
	c.Assert(rels, HasLen, 0)
}<commit_msg>mstate: improve table driven SetLife tests to check error states as well<commit_after>package mstate_test

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	state "launchpad.net/juju-core/mstate"
)

type RelationSuite struct {
	ConnSuite
	charm *state.Charm
}

var _ = Suite(&RelationSuite{})

func (s *RelationSuite) SetUpTest(c *C) {
	s.ConnSuite.SetUpTest(c)
	s.charm = s.AddTestingCharm(c, "dummy")
}

func (s *RelationSuite) TestRelationErrors(c *C) {
	req, err := s.State.AddService("req", s.charm)
	c.Assert(err, IsNil)
	reqep := state.RelationEndpoint{"req", "ifce", "bar", state.RoleRequirer, charm.ScopeGlobal}

	// Check we can't add a relation until both services exist.
	proep := state.RelationEndpoint{"pro", "ifce", "foo", state.RoleProvider, charm.ScopeGlobal}
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": .*`)
	assertNoRelations(c, req)
	pro, err := s.State.AddService("pro", s.charm)
	c.Assert(err, IsNil)

	// Check that interfaces have to match.
	proep2 := state.RelationEndpoint{"pro", "other", "foo", state.RoleProvider, charm.ScopeGlobal}
	_, err = s.State.AddRelation(proep2, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": endpoints do not relate`)
	assertNoRelations(c, pro)
	assertNoRelations(c, req)

	// Check a variety of surprising endpoint combinations.
	_, err = s.State.AddRelation(reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "req:bar": single endpoint must be a peer relation`)
	assertNoRelations(c, req)

	peer, err := s.State.AddService("peer", s.charm)
	c.Assert(err, IsNil)
	peerep := state.RelationEndpoint{"peer", "ifce", "baz", state.RolePeer, charm.ScopeGlobal}
	_, err = s.State.AddRelation(peerep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz req:bar": endpoints do not relate`)
	assertNoRelations(c, peer)
	assertNoRelations(c, req)

	_, err = s.State.AddRelation(peerep, peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz peer:baz": endpoints do not relate`)
	assertNoRelations(c, peer)

	_, err = s.State.AddRelation()
	c.Assert(err, ErrorMatches, `cannot add relation "": cannot relate 0 endpoints`)
	_, err = s.State.AddRelation(proep, reqep, peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz pro:foo req:bar": cannot relate 3 endpoints`)

	_, err = s.State.Relation(peerep)
	c.Assert(err, ErrorMatches, `cannot get relation "peer:baz": .*`)
}

func (s *RelationSuite) TestProviderRequirerRelation(c *C) {
	req, err := s.State.AddService("req", s.charm)
	c.Assert(err, IsNil)
	pro, err := s.State.AddService("pro", s.charm)
	c.Assert(err, IsNil)
	assertNoRelations(c, req)
	assertNoRelations(c, pro)

	// Add a relation, and check we can only do so once.
	proep := state.RelationEndpoint{"pro", "ifce", "foo", state.RoleProvider, charm.ScopeGlobal}
	reqep := state.RelationEndpoint{"req", "ifce", "bar", state.RoleRequirer, charm.ScopeGlobal}
	rel, err := s.State.AddRelation(proep, reqep)
	c.Assert(err, IsNil)
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, ErrorMatches, `cannot add relation "pro:foo req:bar": .*`)
	assertOneRelation(c, pro, 0, proep, reqep)
	assertOneRelation(c, req, 0, reqep, proep)

	// Remove the relation, and check it can't be removed again.
	err = rel.SetLife(state.Dying)
	c.Assert(err, IsNil)
	err = rel.SetLife(state.Dead)
	c.Assert(err, IsNil)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, IsNil)
	assertNoRelations(c, pro)
	assertNoRelations(c, req)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, ErrorMatches, `cannot remove relation "pro:foo req:bar": not found`)

	// Check that we can add it again if we want to; but this time,
	// give one of the endpoints container scope and check that both
	// resulting service relations get that scope.
	reqep.RelationScope = charm.ScopeContainer
	_, err = s.State.AddRelation(proep, reqep)
	c.Assert(err, IsNil)
	// After adding relation, make proep container-scoped as well, for
	// simplicity of testing.
	proep.RelationScope = charm.ScopeContainer
	assertOneRelation(c, req, 2, reqep, proep)
	assertOneRelation(c, pro, 2, proep, reqep)
}

func (s *RelationSuite) TestPeerRelation(c *C) {
	peer, err := s.State.AddService("peer", s.charm)
	c.Assert(err, IsNil)
	peerep := state.RelationEndpoint{"peer", "ifce", "baz", state.RolePeer, charm.ScopeGlobal}
	assertNoRelations(c, peer)

	// Add a relation, and check we can only do so once.
	rel, err := s.State.AddRelation(peerep)
	c.Assert(err, IsNil)
	_, err = s.State.AddRelation(peerep)
	c.Assert(err, ErrorMatches, `cannot add relation "peer:baz": .*`)
	assertOneRelation(c, peer, 0, peerep)

	// Remove the relation, and check it can't be removed again.
	err = rel.SetLife(state.Dying)
	c.Assert(err, IsNil)
	err = rel.SetLife(state.Dead)
	c.Assert(err, IsNil)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, IsNil)
	assertNoRelations(c, peer)
	err = s.State.RemoveRelation(rel)
	c.Assert(err, ErrorMatches, `cannot remove relation "peer:baz": not found`)
}

var stateChanges = []struct {
	cached, desired    state.Life
	dbinitial, dbfinal state.Life
	panic              bool
	panicMessage       string
}{
	{
		state.Alive, state.Alive,
		state.Alive, state.Alive,
		true,
		`illegal lifecycle state change from "alive" to "alive"`,
	},
	{
		state.Alive, state.Alive,
		state.Dying, state.Dying,
		true,
		`illegal lifecycle state change from "alive" to "alive"`,
	},
	{
		state.Alive, state.Alive,
		state.Dead, state.Dead,
		true,
		`illegal lifecycle state change from "alive" to "alive"`,
	},
	{
		state.Alive, state.Dying,
		state.Alive, state.Dying,
		false,
		"",
	},
	{
		state.Alive, state.Dying,
		state.Dying, state.Dying,
		false,
		"",
	},
	{
		state.Alive, state.Dying,
		state.Dead, state.Dead,
		false,
		"",
	},
	{
		state.Alive, state.Dead,
		state.Alive, state.Alive,
		true,
		`illegal lifecycle state change from "alive" to "dead"`,
	},
	{
		state.Alive, state.Dead,
		state.Dying, state.Dying,
		true,
		`illegal lifecycle state change from "alive" to "dead"`,
	},
	{
		state.Alive, state.Dead,
		state.Dead, state.Dead,
		true,
		`illegal lifecycle state change from "alive" to "dead"`,
	},
	{
		state.Dying, state.Alive,
		state.Dying, state.Dying,
		true,
		`illegal lifecycle state change from "dying" to "alive"`,
	},
	{
		state.Dying, state.Alive,
		state.Dead, state.Dead,
		true,
		`illegal lifecycle state change from "dying" to "alive"`,
	},
	{
		state.Dying, state.Dying,
		state.Dying, state.Dying,
		false,
		"",
	},
	{
		state.Dying, state.Dying,
		state.Dead, state.Dead,
		false,
		"",
	},
	{
		state.Dying, state.Dead,
		state.Dying, state.Dead,
		false,
		"",
	},
	{
		state.Dying, state.Dead,
		state.Dead, state.Dead,
		false,
		"",
	},
	{
		state.Dead, state.Alive,
		state.Dead, state.Dead,
		true,
		`illegal lifecycle state change from "dead" to "alive"`,
	},
	{
		state.Dead, state.Dying,
		state.Dead, state.Dead,
		true,
		`illegal lifecycle state change from "dead" to "dying"`,
	},
	{
		state.Dead, state.Dead,
		state.Dead, state.Dead,
		false,
		"",
	},
}

func createRelationFromDoc(c *C, s *RelationSuite, doc *state.RelationDoc, dblife state.Life) *state.Relation {
	rdoc := *doc
	rdoc.Life = dblife
	err := s.relations.Insert(rdoc)
	c.Assert(err, IsNil)
	return state.NewRelation(s.State, doc)
}

func (s *RelationSuite) TestLifecycleStateChanges(c *C) {
	for _, v := range stateChanges {
		doc := state.RelationDoc{
			Id:   0,
			Life: v.cached,
		}
		r := createRelationFromDoc(c, s, &doc, v.dbinitial)
		if v.panic {
			c.Assert(func() { r.SetLife(v.desired) }, PanicMatches, v.panicMessage)
		} else {
			r.SetLife(v.desired)
		}
		err := s.relations.FindId(0).One(&doc)
		c.Assert(err, IsNil)
		c.Assert(doc.Life, Equals, v.dbfinal)
		err = s.relations.DropCollection()
		c.Assert(err, IsNil)
	}
}

func assertNoRelations(c *C, srv *state.Service) {
	rels, err := srv.Relations()
	c.Assert(err, IsNil)
	c.Assert(rels, HasLen, 0)
}
{'fraction_non_alphanumeric': 0.14785228785649218, 'fraction_numerical': 0.0016817321841496743, 'mean_word_length': 4.495571813631113, 'pattern_counts': {'":': 24, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6055038', 'n_tokens_mistral': 5645, 'n_tokens_neox': 5361, 'n_words': 1322}