def test_duplicated_forms(): """Duplicated Forms are rejected on an Interaction.""" thing = Thing(id=uuid.uuid4().urn) interaction = Action(thing=thing, name="my_interaction") thing.add_interaction(interaction) href_01 = "/href-01" href_02 = "/href-02" mtype_01 = "application/json" mtype_02 = "text/html" form_01 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_01, content_type=mtype_01) form_02 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_01, content_type=mtype_01) form_03 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_01, content_type=mtype_02) form_04 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_02, content_type=mtype_01) form_05 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_02, content_type=mtype_02) form_06 = Form(interaction=interaction, protocol=Protocols.HTTP, href=href_02, content_type=mtype_02) interaction.add_form(form_01) with pytest.raises(ValueError): interaction.add_form(form_02) interaction.add_form(form_03) interaction.add_form(form_04) interaction.add_form(form_05) with pytest.raises(ValueError): interaction.add_form(form_06)
def http_server(): """Builds an HTTPServer instance that contains an ExposedThing.""" exposed_thing = ExposedThing(servient=Servient(), thing=Thing(id=uuid.uuid4().urn)) exposed_thing.add_property(uuid.uuid4().hex, PropertyFragmentDict({ "type": "number", "observable": True }), value=Faker().pyint()) exposed_thing.add_property(uuid.uuid4().hex, PropertyFragmentDict({ "type": "number", "observable": True }), value=Faker().pyint()) exposed_thing.add_event(uuid.uuid4().hex, EventFragmentDict({"type": "object"})) action_name = uuid.uuid4().hex @tornado.gen.coroutine def triple(parameters): input_value = parameters.get("input") yield tornado.gen.sleep(0) raise tornado.gen.Return(input_value * 3) exposed_thing.add_action( action_name, ActionFragmentDict({ "input": { "type": "number" }, "output": { "type": "number" } }), triple) port = find_free_port() server = HTTPServer(port=port) server.add_exposed_thing(exposed_thing) @tornado.gen.coroutine def start(): yield server.start() tornado.ioloop.IOLoop.current().run_sync(start) yield server @tornado.gen.coroutine def stop(): yield server.stop() tornado.ioloop.IOLoop.current().run_sync(stop)
def consumed_exposed_pair(): """Returns a dict with two keys: * consumed_thing: A ConsumedThing instance. The Servient instance that contains this ConsumedThing has been patched to use the ExposedThingProxyClient Protocol Binding client. * exposed_thing: The ExposedThing behind the previous ConsumedThing (for assertion purposes).""" servient = Servient() exp_thing = ExposedThing(servient=servient, thing=Thing(id=uuid.uuid4().urn)) servient.select_client = MagicMock( return_value=ExposedThingProxyClient(exp_thing)) @tornado.gen.coroutine def lower(parameters): input_value = parameters.get("input") yield tornado.gen.sleep(0) raise tornado.gen.Return(str(input_value).lower()) exp_thing.add_property(uuid.uuid4().hex, _build_property_fragment()) exp_thing.add_action(uuid.uuid4().hex, _build_action_fragment(), lower) exp_thing.add_event(uuid.uuid4().hex, _build_event_fragment()) td = ThingDescription.from_thing(exp_thing.thing) return { "consumed_thing": ConsumedThing(servient=servient, td=td), "exposed_thing": exp_thing }
def test_ssl_context(self_signed_ssl_context): """An SSL context can be passed to the WebSockets server to enable encryption.""" exposed_thing = ExposedThing(servient=Servient(), thing=Thing(id=uuid.uuid4().urn)) prop_name = uuid.uuid4().hex exposed_thing.add_property(prop_name, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().pystr()) port = find_free_port() server = WebsocketServer(port=port, ssl_context=self_signed_ssl_context) server.add_exposed_thing(exposed_thing) @tornado.gen.coroutine def test_coroutine(): yield server.start() ws_url = build_websocket_url(exposed_thing, server, port) assert WebsocketSchemes.WSS in ws_url with pytest.raises(ssl.SSLError): http_req = tornado.httpclient.HTTPRequest(ws_url, method="GET") yield tornado.websocket.websocket_connect(http_req) http_req = tornado.httpclient.HTTPRequest(ws_url, method="GET", validate_cert=False) conn = yield tornado.websocket.websocket_connect(http_req) request_id = Faker().pyint() msg_req = WebsocketMessageRequest( method=WebsocketMethods.READ_PROPERTY, params={"name": prop_name}, msg_id=request_id) conn.write_message(msg_req.to_json()) msg_resp_raw = yield conn.read_message() msg_resp = WebsocketMessageResponse.from_raw(msg_resp_raw) assert msg_resp.id == request_id value = yield exposed_thing.read_property(prop_name) assert value == msg_resp.result yield conn.close() yield server.stop() run_test_coroutine(test_coroutine)
def mqtt_server(request): """Builds a MQTTServer instance that contains an ExposedThing.""" from wotpy.protocols.mqtt.server import MQTTServer from tests.protocols.mqtt.broker import get_test_broker_url exposed_thing = ExposedThing(servient=Servient(), thing=Thing(id=uuid.uuid4().urn)) exposed_thing.add_property(uuid.uuid4().hex, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().sentence()) exposed_thing.add_event(uuid.uuid4().hex, EventFragmentDict({"type": "number"})) action_name = uuid.uuid4().hex @tornado.gen.coroutine def handler(parameters): input_value = parameters.get("input") yield tornado.gen.sleep(random.random() * 0.1) raise tornado.gen.Return("{:f}".format(input_value)) exposed_thing.add_action( action_name, ActionFragmentDict({ "input": { "type": "number" }, "output": { "type": "string" } }), handler) server = MQTTServer(broker_url=get_test_broker_url(), **request.param) server.add_exposed_thing(exposed_thing) @tornado.gen.coroutine def start(): yield server.start() tornado.ioloop.IOLoop.current().run_sync(start) yield server @tornado.gen.coroutine def stop(): yield server.stop() tornado.ioloop.IOLoop.current().run_sync(stop)
def test_thing_fragment_getters_setters(): """ThingFragment attributes can be get and set from the ExposedThing.""" thing_fragment = ThingFragment({ "id": uuid.uuid4().urn, "name": Faker().pystr(), "description": Faker().pystr(), "properties": { uuid.uuid4().hex: { "description": Faker().pystr(), "type": DataType.STRING } } }) thing = Thing(thing_fragment=thing_fragment) exp_thing = ExposedThing(servient=Servient(), thing=thing) assert exp_thing.name == thing_fragment.name assert exp_thing.description == thing_fragment.description assert list(exp_thing.properties) == list( six.iterkeys(thing_fragment.properties)) name_original = thing_fragment.name name_updated = Faker().pystr() description_original = thing_fragment.description description_updated = Faker().pystr() exp_thing.name = name_updated exp_thing.description = description_updated assert exp_thing.name == name_updated assert exp_thing.name != name_original assert exp_thing.description == description_updated assert exp_thing.description != description_original with pytest.raises(AttributeError): # noinspection PyPropertyAccess exp_thing.id = Faker().pystr() with pytest.raises(AttributeError): # noinspection PyPropertyAccess exp_thing.properties = Faker().pylist() with pytest.raises(AttributeError): # noinspection PyPropertyAccess exp_thing.actions = Faker().pylist() with pytest.raises(AttributeError): # noinspection PyPropertyAccess exp_thing.events = Faker().pylist()
def test_unique_url_name(): """URL names are always unique as long as the IDs are.""" thing_id_base = uuid.uuid4().urn thing_ids = [thing_id_base] for ch in string.punctuation.replace("-", ""): thing_ids.append(thing_id_base.replace("-", ch)) things = [Thing(id=item) for item in thing_ids] url_names = [item.url_name for item in things] assert len(url_names) == len(set(url_names))
def test_interaction_invalid_name(): """Invalid names for Interaction objects are rejected.""" names_valid = ["safename", "safename02", "SafeName_03", "Safe_Name-04"] names_invalid = ["!unsafename", "unsafe_name_ñ", "unsafe name", "?"] thing = Thing(id=uuid.uuid4().urn) for name in names_valid: Action(thing=thing, name=name) for name in names_invalid: with pytest.raises(ValueError): Action(thing=thing, name=name)
def _test_equivalent_interaction_names(base_name, transform_name): """Helper function to test that interaction names are equivalent given a certain transformation function.""" thing = Thing(id=uuid.uuid4().urn) exp_thing = ExposedThing(servient=Servient(), thing=thing) prop_name = "property" + base_name prop_name_transform = transform_name(prop_name) prop_default_value = Faker().pybool() exp_thing.add_property(prop_name, {"type": DataType.BOOLEAN}, value=prop_default_value) with pytest.raises(ValueError): exp_thing.add_property(prop_name_transform, {"type": DataType.BOOLEAN}) @tornado.gen.coroutine def assert_prop_read(): assert (yield exp_thing.properties[prop_name].read()) is prop_default_value assert (yield exp_thing.properties[prop_name_transform].read() ) is prop_default_value tornado.ioloop.IOLoop.current().run_sync(assert_prop_read) action_name = "action" + base_name action_name_transform = transform_name(action_name) exp_thing.add_action(action_name, {}) with pytest.raises(ValueError): exp_thing.add_action(action_name_transform, {}) assert exp_thing.actions[action_name] assert exp_thing.actions[action_name_transform] event_name = "event" + base_name event_name_transform = transform_name(event_name) exp_thing.add_event(event_name, {}) with pytest.raises(ValueError): exp_thing.add_event(event_name_transform, {}) assert exp_thing.events[event_name] assert exp_thing.events[event_name_transform]
def test_from_thing(): """ThingDescription objects can be built from Thing objects.""" fake = Faker() thing_id = uuid.uuid4().urn action_id = uuid.uuid4().hex prop_id = uuid.uuid4().hex event_id = uuid.uuid4().hex action_form_href = fake.url() prop_form_href = fake.url() thing = Thing(id=thing_id) action = Action(thing=thing, name=action_id) action_form = Form(interaction=action, protocol=Protocols.HTTP, href=action_form_href) action.add_form(action_form) thing.add_interaction(action) prop = Property(thing=thing, name=prop_id, type="string") prop_form = Form(interaction=prop, protocol=Protocols.HTTP, href=prop_form_href) prop.add_form(prop_form) thing.add_interaction(prop) event = Event(thing=thing, name=event_id) thing.add_interaction(event) json_td = ThingDescription.from_thing(thing) td_dict = json_td.to_dict() assert td_dict["id"] == thing.id assert td_dict["title"] == thing.title assert len(td_dict["properties"]) == 1 assert len(td_dict["actions"]) == 1 assert len(td_dict["events"]) == 1 assert len(td_dict["actions"][action_id]["forms"]) == 1 assert len(td_dict["properties"][prop_id]["forms"]) == 1 assert td_dict["actions"][action_id]["forms"][0][ "href"] == action_form_href assert td_dict["properties"][prop_id]["forms"][0]["href"] == prop_form_href
def thing_from_model(cls, model): """Takes a ThingModel and builds a Thing. Raises if the model has an unexpected type.""" expected_types = (six.string_types, ThingFragment, ConsumedThing) if not isinstance(model, expected_types): raise ValueError("Expected one of: {}".format(expected_types)) if isinstance(model, six.string_types): thing = ThingDescription(doc=model).build_thing() elif isinstance(model, ThingFragment): thing = Thing(thing_fragment=model) else: thing = model.td.build_thing() return thing
def test_ssl_context(self_signed_ssl_context): """An SSL context can be passed to the HTTP server to enable encryption.""" exposed_thing = ExposedThing(servient=Servient(), thing=Thing(id=uuid.uuid4().urn)) prop_name = uuid.uuid4().hex exposed_thing.add_property(prop_name, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().pystr()) port = find_free_port() server = HTTPServer(port=port, ssl_context=self_signed_ssl_context) server.add_exposed_thing(exposed_thing) href = _get_property_href(exposed_thing, prop_name, server) assert HTTPSchemes.HTTPS in href @tornado.gen.coroutine def test_coroutine(): yield server.start() prop_value = Faker().pystr() yield exposed_thing.properties[prop_name].write(prop_value) http_client = tornado.httpclient.AsyncHTTPClient() with pytest.raises(ssl.SSLError): yield http_client.fetch( tornado.httpclient.HTTPRequest(href, method="GET")) http_request = tornado.httpclient.HTTPRequest(href, method="GET", validate_cert=False) response = yield http_client.fetch(http_request) assert json.loads(response.body).get("value") == prop_value yield server.stop() run_test_coroutine(test_coroutine)
def test_find_interaction(): """Interactions may be retrieved by name on a Thing.""" thing = Thing(id=uuid.uuid4().urn) interaction_01 = Action(thing=thing, name="my_interaction") interaction_02 = Action(thing=thing, name="AnotherInteraction") thing.add_interaction(interaction_01) thing.add_interaction(interaction_02) assert thing.find_interaction(interaction_01.name) is interaction_01 assert thing.find_interaction(interaction_02.name) is interaction_02 assert thing.find_interaction(slugify( interaction_01.name)) is interaction_01 assert thing.find_interaction(slugify( interaction_02.name)) is interaction_02
def test_duplicated_interactions(): """Duplicated Interactions are rejected on a Thing.""" thing = Thing(id=uuid.uuid4().urn) interaction_01 = Action(thing=thing, name="my_interaction") interaction_02 = Action(thing=thing, name="AnotherInteraction") interaction_03 = Action(thing=thing, name="my_interaction") thing.add_interaction(interaction_01) thing.add_interaction(interaction_02) with pytest.raises(ValueError): thing.add_interaction(interaction_03)
def test_empty_thing_valid(): """An empty Thing initialized by default has a valid JSON-LD serialization.""" thing = Thing(id=uuid.uuid4().urn) json_td = ThingDescription.from_thing(thing) ThingDescription.validate(json_td.to_dict())
def websocket_server(): """Builds a WebsocketServer instance with some ExposedThings.""" servient = Servient() thing_01_id = uuid.uuid4().urn thing_02_id = uuid.uuid4().urn exposed_thing_01 = ExposedThing(servient=servient, thing=Thing(id=thing_01_id)) exposed_thing_02 = ExposedThing(servient=servient, thing=Thing(id=thing_02_id)) prop_name_01 = uuid.uuid4().hex prop_name_02 = uuid.uuid4().hex prop_name_03 = uuid.uuid4().hex event_name_01 = uuid.uuid4().hex action_name_01 = uuid.uuid4().hex prop_value_01 = Faker().sentence() prop_value_02 = Faker().sentence() prop_value_03 = Faker().sentence() prop_init_01 = PropertyFragmentDict({"type": "string", "observable": True}) prop_init_02 = PropertyFragmentDict({"type": "string", "observable": True}) prop_init_03 = PropertyFragmentDict({"type": "string", "observable": True}) event_init_01 = EventFragmentDict({"type": "object"}) action_init_01 = ActionFragmentDict({ "input": { "type": "string" }, "output": { "type": "string" } }) def async_lower(parameters): loop = tornado.ioloop.IOLoop.current() input_value = parameters.get("input") return loop.run_in_executor(None, lambda x: time.sleep(0.1) or x.lower(), input_value) exposed_thing_01.add_property(prop_name_01, prop_init_01, value=prop_value_01) exposed_thing_01.add_property(prop_name_02, prop_init_02, value=prop_value_02) exposed_thing_01.add_event(event_name_01, event_init_01) exposed_thing_01.add_action(action_name_01, action_init_01, async_lower) exposed_thing_02.add_property(prop_name_03, prop_init_03, value=prop_value_03) ws_port = find_free_port() ws_server = WebsocketServer(port=ws_port) ws_server.add_exposed_thing(exposed_thing_01) ws_server.add_exposed_thing(exposed_thing_02) @tornado.gen.coroutine def start(): yield ws_server.start() tornado.ioloop.IOLoop.current().run_sync(start) url_thing_01 = build_websocket_url(exposed_thing_01, ws_server, ws_port) url_thing_02 = build_websocket_url(exposed_thing_02, ws_server, ws_port) yield { "exposed_thing_01": exposed_thing_01, "exposed_thing_02": exposed_thing_02, "prop_name_01": prop_name_01, "prop_init_01": prop_init_01, "prop_value_01": prop_value_01, "prop_name_02": prop_name_02, "prop_init_02": prop_init_02, "prop_value_02": prop_value_02, "prop_name_03": prop_name_03, "prop_init_03": prop_init_03, "prop_value_03": prop_value_03, "event_name_01": event_name_01, "event_init_01": event_init_01, "action_name_01": action_name_01, "action_init_01": action_init_01, "ws_server": ws_server, "url_thing_01": url_thing_01, "url_thing_02": url_thing_02, "ws_port": ws_port } @tornado.gen.coroutine def stop(): yield ws_server.stop() tornado.ioloop.IOLoop.current().run_sync(stop)
def build_thing(self): """Builds a new Thing object from the serialized Thing Description.""" return Thing(thing_fragment=self.to_thing_fragment())
def test_remove_interaction(): """Interactions may be removed from a Thing by name.""" thing = Thing(id=uuid.uuid4().urn) interaction_01 = Action(thing=thing, name="my_interaction") interaction_02 = Action(thing=thing, name="AnotherInteraction") interaction_03 = Action(thing=thing, name="YetAnother_interaction") thing.add_interaction(interaction_01) thing.add_interaction(interaction_02) thing.add_interaction(interaction_03) assert thing.find_interaction(interaction_01.name) is not None assert thing.find_interaction(interaction_02.name) is not None assert thing.find_interaction(interaction_03.name) is not None thing.remove_interaction(interaction_01.name) thing.remove_interaction(slugify(interaction_03.name)) assert thing.find_interaction(interaction_01.name) is None assert thing.find_interaction(interaction_02.name) is not None assert thing.find_interaction(interaction_03.name) is None
def exposed_thing(): """Builds and returns a random ExposedThing.""" return ExposedThing(servient=Servient(), thing=Thing(id=uuid.uuid4().urn))