def test_key_dict_arbitrary_keys(self): """ KeyDict doesn't actually need to have strings as keys, just any object which hashes the same. """ key = object() self.assertEqual(KeyDict({key: Int()}).coerce({key: 32}), {key: 32})
def test_key_dict_must_have_all_keys(self): """ dicts which are applied to a KeyDict must have all the keys specified in the KeyDict. """ schema = KeyDict({"foo": Int()}) self.assertRaises(InvalidError, schema.coerce, {})
def test_atomic_message_writing(self): """ If the server gets unplugged halfway through writing a file, the message should not be half-written. """ self.store.add_schema(Message("data", {"data": Int()})) self.store.add({"type": "data", "data": 1}) # We simulate it by creating a fake file which raises halfway through # writing a file. mock_open = mock.mock_open() with mock.patch("landscape.lib.fs.open", mock_open): mock_open().write.side_effect = IOError("Sorry, pal!") # This kind of ensures that raising an exception is somewhat # similar to unplugging the power -- i.e., we're not relying # on special exception-handling in the file-writing code. self.assertRaises(IOError, self.store.add, { "type": "data", "data": 2 }) mock_open.assert_called_with(mock.ANY, "wb") mock_open().write.assert_called_once_with(mock.ANY) self.assertEqual(self.store.get_pending_messages(), [{ "type": "data", "data": 1, "api": b"3.2" }])
def test_message_is_coerced_to_its_api_schema(self): """ A message gets coerced to the schema of the API its targeted to. """ self.store.set_server_api(b"3.3") # Add a new schema for the 'data' message type, with a slightly # different definition. self.store.add_schema(Message("data", {"data": Int()}, api=b"3.3")) # The message is coerced against the new schema. self.store.add({"type": "data", "data": 123}) self.assertEqual( self.store.get_pending_messages(), [{"type": "data", "api": b"3.3", "data": 123}])
def test_message_is_coerced_to_highest_compatible_api_schema(self): """ A message gets coerced to the schema of the highest compatible API version. """ # Add a new schema for the 'data' message type, with a slightly # different definition. self.store.set_server_api(b"3.2") self.store.add_schema(Message("data", {"data": Int()}, api=b"3.3")) # The message is coerced against the older schema. self.store.add({"type": "data", "data": b"foo"}) self.assertEqual( self.store.get_pending_messages(), [{"type": "data", "api": b"3.2", "data": b"foo"}])
def test_pass_optional_key(self): """Regression test. It should be possible to pass an optional key. """ schema = KeyDict({"foo": Int()}, optional=["foo"]) self.assertEqual(schema.coerce({"foo": 32}), {"foo": 32})
def test_key_dict_optional_keys(self): """KeyDict allows certain keys to be optional. """ schema = KeyDict({"foo": Int(), "bar": Int()}, optional=["bar"]) self.assertEqual(schema.coerce({"foo": 32}), {"foo": 32})
def test_coerce(self): """The L{Message} schema should be very similar to KeyDict.""" schema = Message("foo", {"data": Int()}) self.assertEqual( schema.coerce({"type": "foo", "data": 3}), {"type": "foo", "data": 3})
def test_list(self): self.assertEqual(List(Int()).coerce([1]), [1])
def test_int_bad_float(self): self.assertRaises(InvalidError, Int().coerce, 3.0)
def test_int_accepts_long(self): # This test can be removed after dropping Python 2 support self.assertEqual(Int().coerce(long(3)), 3)
def test_dict_wrong_type(self): self.assertRaises(InvalidError, Dict(Int(), Int()).coerce, 32)
def test_tuple_must_have_no_more_items(self): self.assertRaises(InvalidError, Tuple(Int()).coerce, (1, 2))
def test_tuple_must_have_all_items(self): self.assertRaises(InvalidError, Tuple(Int(), Int()).coerce, (1, ))
def test_tuple_inner_schema_bad(self): self.assertRaises(InvalidError, Tuple(Int()).coerce, (object(), ))
def test_tuple_coerces(self): self.assertEqual( Tuple(Int(), DummySchema()).coerce((23, object())), (23, "hello!"))
def test_tuple(self): self.assertEqual(Tuple(Int()).coerce((1, )), (1, ))
def test_list_bad_inner_schema(self): self.assertRaises(InvalidError, List(Int()).coerce, ["hello"])
def test_list_bad(self): self.assertRaises(InvalidError, List(Int()).coerce, 32)
def test_dict(self): self.assertEqual( Dict(Int(), Bytes()).coerce({32: b"hello."}), {32: b"hello."})
def test_dict_inner_bad(self): self.assertRaises(InvalidError, Dict(Int(), Int()).coerce, {"32": 32})
def test_key_dict(self): self.assertEqual( KeyDict({ "foo": Int() }).coerce({"foo": 1}), {"foo": 1})
def test_int(self): self.assertEqual(Int().coerce(3), 3)
def test_optional(self): """The L{Message} schema should allow additional optional keys.""" schema = Message("foo", {"data": Int()}, optional=["data"]) self.assertEqual(schema.coerce({"type": "foo"}), {"type": "foo"})
def test_int_bad_str(self): self.assertRaises(InvalidError, Int().coerce, "3")
def setUp(self): super(MessageExchangeTest, self).setUp() self.mstore.add_schema(Message("empty", {})) self.mstore.add_schema(Message("data", {"data": Int()})) self.mstore.add_schema(Message("holdme", {})) self.identity.secure_id = 'needs-to-be-set-for-tests-to-pass'
def test_key_dict_bad_inner_schema(self): self.assertRaises(InvalidError, KeyDict({ "foo": Int() }).coerce, {"foo": "hello"})
def setUp(self): LandscapeTest.setUp(self) self.plugin = StubDataWatchingPlugin(1) self.plugin.register(self.monitor) self.mstore.add_schema(Message("wubble", {"wubblestuff": Int()}))
def test_key_dict_multiple_items(self): schema = KeyDict({"one": Int(), "two": List(Float())}) input = {"one": 32, "two": [1.5, 2.3]} self.assertEqual(schema.coerce(input), {"one": 32, "two": [1.5, 2.3]})
"SWIFT_DEVICE_INFO", "KEYSTONE_TOKEN", "JUJU_UNITS_INFO", "CLOUD_METADATA", ] # When adding a new schema, which deprecates an older schema, the recommended # naming convention, is to name it SCHEMA_NAME_ and the last API version that # the schema works with. # # i.e. if I have USERS and I'm deprecating it, in API 2.2, then USERS becomes # USERS_2_1 process_info = KeyDict( { "pid": Int(), "name": Unicode(), "state": Bytes(), "sleep-average": Int(), "uid": Int(), "gid": Int(), "vm-size": Int(), "start-time": Int(), "percent-cpu": Float() }, # Optional for backwards compatibility optional=["vm-size", "sleep-average", "percent-cpu"]) ACTIVE_PROCESS_INFO = Message( "active-process-info", {