def test_property_fragment(): """Property fragment dictionaries can be represented and serialized.""" init = { "description": "Shows the current status of the lamp", "readOnly": True, "observable": False, "type": "string", "security": [{ "scheme": "nosec" }], "forms": [{ "href": "coaps://mylamp.example.com/status", "contentType": "application/json" }] } prop_fragment = PropertyFragmentDict(init) assert prop_fragment.read_only == init["readOnly"] assert prop_fragment.write_only is False assert prop_fragment.observable == init["observable"] assert isinstance(prop_fragment.data_schema, DataSchemaDict) assert prop_fragment.data_schema.type == init["type"] assert len(prop_fragment.forms) == len(init["forms"]) assert prop_fragment.forms[0].href == init["forms"][0]["href"] assert prop_fragment.security[0].scheme == init["security"][0]["scheme"] assert json.dumps(prop_fragment.to_dict()) with pytest.raises(Exception): PropertyFragmentDict({})
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 add_prop(pname): exposed_thing.add_property(pname, PropertyFragmentDict({ "type": "number", "observable": True }), value=Faker().pyint())
def client_test_write_property(servient, protocol_client_cls, timeout=None): """Helper function to test Property writes on bindings clients.""" exposed_thing = next(servient.exposed_things) prop_name = uuid.uuid4().hex exposed_thing.add_property(prop_name, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().sentence()) servient.refresh_forms() td = ThingDescription.from_thing(exposed_thing.thing) @tornado.gen.coroutine def test_coroutine(): protocol_client = protocol_client_cls() prop_value = Faker().sentence() prev_value = yield exposed_thing.properties[prop_name].read() assert prev_value != prop_value yield protocol_client.write_property(td, prop_name, prop_value, timeout=timeout) curr_value = yield exposed_thing.properties[prop_name].read() assert curr_value == prop_value run_test_coroutine(test_coroutine)
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 properties(self): """The properties optional attribute represents a dict with keys that correspond to Property names and values of type PropertyFragment.""" return { key: PropertyFragmentDict(val) for key, val in six.iteritems(self._init.get("properties", {})) }
def _build_property_fragment(): """Builds and returns a random Property init fragment.""" return PropertyFragmentDict({ "description": Faker().sentence(), "readOnly": False, "observable": True, "type": "string" })
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 add_property(self, name, property_init, value=None): """Adds a Property defined by the argument and updates the Thing Description. Takes an instance of ThingPropertyInit as argument.""" if isinstance(property_init, dict): property_init = PropertyFragmentDict(property_init) prop = Property(thing=self._thing, name=name, init_dict=property_init) self._thing.add_interaction(prop) self._set_property_value(prop, value) event_data = ThingDescriptionChangeEventInit( td_change_type=TDChangeType.PROPERTY, method=TDChangeMethod.ADD, name=name, data=property_init.to_dict(), description=ThingDescription.from_thing(self.thing).to_dict()) self._events_stream.on_next( ThingDescriptionChangeEmittedEvent(init=event_data))
def client_test_on_property_change(servient, protocol_client_cls): """Helper function to test observation of Property updates on bindings clients.""" exposed_thing = next(servient.exposed_things) prop_name = uuid.uuid4().hex exposed_thing.add_property(prop_name, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().sentence()) servient.refresh_forms() td = ThingDescription.from_thing(exposed_thing.thing) @tornado.gen.coroutine def test_coroutine(): protocol_client = protocol_client_cls() values = [Faker().sentence() for _ in range(10)] values_observed = {value: tornado.concurrent.Future() for value in values} @tornado.gen.coroutine def write_next(): try: next_value = next(val for val, fut in six.iteritems(values_observed) if not fut.done()) yield exposed_thing.properties[prop_name].write(next_value) except StopIteration: pass def on_next(ev): prop_value = ev.data.value if prop_value in values_observed and not values_observed[prop_value].done(): values_observed[prop_value].set_result(True) observable = protocol_client.on_property_change(td, prop_name) subscription = observable.subscribe_on(IOLoopScheduler()).subscribe(on_next) periodic_emit = tornado.ioloop.PeriodicCallback(write_next, 10) periodic_emit.start() yield list(values_observed.values()) periodic_emit.stop() subscription.dispose() run_test_coroutine(test_coroutine)
def client_test_on_property_change_error(servient, protocol_client_cls): """Helper function to test propagation of errors raised during observation of Property updates on bindings clients.""" exposed_thing = next(servient.exposed_things) prop_name = uuid.uuid4().hex exposed_thing.add_property(prop_name, PropertyFragmentDict({ "type": "string", "observable": True }), value=Faker().sentence()) servient.refresh_forms() td = ThingDescription.from_thing(exposed_thing.thing) @tornado.gen.coroutine def test_coroutine(): protocol_client = protocol_client_cls() yield servient.shutdown() future_err = tornado.concurrent.Future() # noinspection PyUnusedLocal def on_next(item): future_err.set_exception(Exception("Should not have emitted any items")) def on_error(err): future_err.set_result(err) observable = protocol_client.on_property_change(td, prop_name) subscribe_kwargs = { "on_next": on_next, "on_error": on_error } subscription = observable.subscribe_on(IOLoopScheduler()).subscribe(**subscribe_kwargs) observe_err = yield future_err assert isinstance(observe_err, Exception) subscription.dispose() run_test_coroutine(test_coroutine)
def test_write_non_writable_property(exposed_thing): """Attempts to write a non-writable property should return an error.""" prop_init_non_writable = PropertyFragmentDict({ "type": "string", "readOnly": True }) @tornado.gen.coroutine def test_coroutine(): prop_name = Faker().pystr() exposed_thing.add_property(prop_name, prop_init_non_writable) with pytest.raises(Exception): yield exposed_thing.write_property(prop_name, Faker().pystr()) run_test_coroutine(test_coroutine)
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_thing_fragment_setters(): """Thing fragment properties can be set.""" thing_fragment = ThingFragment(THING_INIT) with pytest.raises(AttributeError): thing_fragment.id = Faker().pystr() assert thing_fragment.title == THING_INIT["title"] title = Faker().pystr() # noinspection PyPropertyAccess thing_fragment.title = title assert thing_fragment.title != THING_INIT["title"] assert thing_fragment.title == title prop_fragment = PropertyFragmentDict(description=Faker().pystr(), type=DataType.NUMBER) props_updated = {Faker().pystr(): prop_fragment} # noinspection PyPropertyAccess thing_fragment.properties = props_updated assert next(six.itervalues( thing_fragment.properties)).description == prop_fragment.description security_updated = [SecuritySchemeDict(scheme=SecuritySchemeType.PSK)] # noinspection PyPropertyAccess thing_fragment.security = security_updated assert thing_fragment.security[0].scheme == security_updated[0].scheme version_updated = VersioningDict(instance=Faker().pystr()) # noinspection PyPropertyAccess thing_fragment.version = version_updated assert thing_fragment.version.instance == version_updated.instance
def test_on_property_change_non_observable(exposed_thing): """Observe requests to non-observable properties are rejected.""" prop_init_non_observable = PropertyFragmentDict({ "type": "string", "observable": False }) @tornado.gen.coroutine def test_coroutine(): prop_name = Faker().pystr() exposed_thing.add_property(prop_name, prop_init_non_observable) observable_prop = exposed_thing.on_property_change(prop_name) future_next = Future() future_error = Future() def on_next(item): future_next.set_result(item) def on_error(err): future_error.set_exception(err) subscription = observable_prop.subscribe(on_next=on_next, on_error=on_error) yield exposed_thing.write_property(prop_name, Faker().pystr()) with pytest.raises(Exception): future_error.result() assert not future_next.done() subscription.dispose() run_test_coroutine(test_coroutine)
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)