async def test_fail_bad_signature(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") payload = c.sign_payload({"hds.ttl": 60, "hds.foo": "bar"}) # This should succeed await self._raw_state_send(c, "hds.foo", payload) try: payload["hds.signature"] = "notarealsig" await self._raw_state_send(c, "hds.foo", payload) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.bad_signature", "Incorrect error type " + e.type try: del payload["hds.signature"] await self._raw_state_send(c, "hds.foo", payload) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.missing_key", "Incorrect error type " + e.type # TODO: Check hds.error.servername.not_rsa # hds.error.payload.bad_signature try: payload["hds.signature"] = c.sign_payload({"hds.ttl": 60, "hds.foo": "baz"})["hds.signature"] await self._raw_state_send(c, "hds.foo", payload) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.bad_signature", "Incorrect error type " + e.type
async def test_topic_is_signed(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com", 60) await c.put_topic("hds.test.topic", ["topic1", "topic2"]) topic_data = await c.get_host_topic(c.get_pubkey(), "hds.test.topic") print(topic_data)
async def test_can_get_topic(self): inst = self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") await c.put_topic("test.topic") res = await c.get_topic("test.topic") assert c.get_pubkey() in res["hosts"]
async def _raw_state_send(self, c, key, payload): baseurl = DirectoryClient.format_baseurl(c.base_url) async with ClientSession() as session: url = "{}/hosts/{}/state/{}".format(baseurl, c.pub_key, key) res = await session.put(url=url, json=payload, ssl=False) if res.status == 201: return DirectoryClient.check_error(await res.json())
async def test_state_is_signed(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) expected_payload = c.sign_payload({"hds.ttl": 60, "hds.host": "example.com"}) await c.send_state("hds.host", "example.com", 60) state = await c.get_state(c.get_pubkey(), raw=True) print(state, expected_payload) sig1 = state["hds.host"]["hds.signature"] sig2 = expected_payload["hds.host"]["hds.signature"] assert sig1 == sig2, "Signatures should match"
async def test_keep_latest_state(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") for i in range(0, 256): time.sleep(0.001) await c.send_state("hds.test." + str(i), "state" + str(i)) state = await c.get_state(c.get_pubkey()) assert state.get("hds.test.0") is None, "hds.test.0 should not exist" assert state.get("hds.host") is not None, "hds.host should always exist" assert len(state.keys()) == 255, "there should be no more than 255 state keys"
async def test_run_many(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") p = [] t = time.time_ns() for i in range(0, 100): p.append(c.send_state("hds.record_%d" % i, "This is record %d" % i)) await asyncio.wait(p) t_end = (time.time_ns() - t) / (10 ** 6) logger.info("Took %dms", t_end)
async def test_can_get_state(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") r2 = c.send_state("hds.test1", "foo") r3 = c.send_state("hds.test2", "bar") r4 = c.send_state("hds.test3", "baz") await r2 await r3 await r4 state = await c.get_state(c.get_pubkey()) assert state["hds.host"] == "example.com", "hds.host was not found in state" assert state["hds.test1"] == "foo", "hds.test1 was not found in state" assert state["hds.test2"] == "bar", "hds.test2 was not found in state" assert state["hds.test3"] == "baz", "hds.test3 was not found in state"
async def test_can_get_state_quickly(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") r2 = c.send_state("hds.test1", "foo") r3 = c.send_state("hds.test2", "bar") r4 = c.send_state("hds.test3", "baz") await r2 await r3 await r4 # Time this one t = time.time_ns() await c.get_state(c.get_pubkey()) t_end = (time.time_ns() - t) / (10 ** 6) logger.info("Took %dms", t_end) assert t_end < 50, "Time taken to store state must be under 50 milliseconds"
async def test_can_determine_identity(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012") # If this doesn't throw, then it started okay identity = await c.identify() assert "hds.servername" in identity, "hds.servername not in identity response" assert "hds.type" in identity, "hds.type is not in identity response" assert identity["hds.type"] == "hds.directory", "hds.type is not hds.directory"
async def test_can_store_state_quickly(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") # Time this one t = time.time_ns() await c.send_state("test.key", "value") t_end = (time.time_ns() - t) / (10 ** 6) logger.info("Took %dms", t_end) assert t_end < 50, "Time taken to store state must be under 50 milliseconds"
async def test_state_tombstone(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") await c.send_state("hds.tombstone", "was hacked") try: await c.send_state("hds.newdata", "was hacked") assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.host.tombstone", "Incorrect error type " + e.type
async def test_memory_consumption_load(self): inst = self.runner.create_hds() stats = inst.stats(stream=False)["memory_stats"] max_usage = 0 # Generate rudimentary load by running 500 state puts c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") for n in range(0, 5): p = [] for i in range(0, 100): p.append(c.send_state("hds.record_%d" % i, "This is record %d" % i)) # Take a measurement at each stage max_usage = max(stats["max_usage"], max_usage) await asyncio.wait(p) max_usage = max_usage / (1024 * 1024) #MB logger.info("Used %dMB", max_usage) assert max_usage < 250, "Memory usage must be under 250 MB"
def gen_client(self, opts={}, paranoid_mode=True): c = DirectoryClient("http://localhost:11111", private_key_path="spec/unit/privkey.pem", paranoid_mode=paranoid_mode) if opts.get("state") == None: c.mock_session = MockClientSession(opts) return c serversState = {} for srvname, state in opts.get("state").items(): srvState = {} for k, v in state.items(): if k == "hds.expired": srvState["hds.expired"] = v continue srvState[k] = c.sign_payload({k: v, "hds.ttl": 60000}) val = srvState[k][k] srvState[k]["value"] = val del srvState[k][k] serversState[srvname] = srvState c.mock_session = MockClientSession(opts, serversState) return c
async def test_state_value_size(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") try: await c.send_state("hds.small_value", "") assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.body_too_short", "Incorrect error type " + e.type try: await c.send_state("hds.body_too_long", "a" * ((1024 * 64) + 1)) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.body_too_long", "Incorrect error type " + e.type await c.send_state("hds.body_too_long", "a" * (1024 * 64))
async def test_fail_bad_strings(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") try: await c.send_state("hds.test.bad_value", 123) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.bad_type", "Incorrect error type " + e.type try: await c.send_state("hds.test.bad_value", None) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.bad_type", "Incorrect error type " + e.type try: await c.send_state("hds.test.bad_value", True) assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.bad_type", "Incorrect error type " + e.type
async def test_state_key_size(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") try: await c.send_state("a", "too small") assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.key_too_short", "Incorrect error type " + e.type try: await c.send_state("aa", "too small") assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.key_too_short", "Incorrect error type " + e.type await c.send_state("a" * 1024, "should be fine") try: await c.send_state("a" * 1025, "too large") assert False, "Must throw" except HDSFailure as e: assert e.type == "hds.error.payload.key_too_long", "Incorrect error type " + e.type
def test_init_bad_key(self): with self.assertRaises(HDSBadKeyFailure): DirectoryClient("http://localhost:11111", private_key_path="spec/unit/badkey.pem")
def test_sign_payload_nokey(self): client = DirectoryClient("http://localhost:11111") with self.assertRaises(HDSBadKeyFailure): client.sign_payload({"some": "payload"})
def test_get_pubkey_nokey(self): client = DirectoryClient("http://localhost:11111") with self.assertRaises(HDSBadKeyFailure): client.get_pubkey()
def test_init_no_key(self): client = DirectoryClient("http://localhost:11111", paranoid_mode=False) self.assertEqual(client.base_url, "http://localhost:11111") self.assertEqual(client.paranoid_mode, False) self.assertIsNone(client.pub_key) self.assertIsNone(client.key)
async def test_can_store_state(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") await c.send_state("test.key", "value")
async def test_can_start(self): self.runner.create_hds() c = DirectoryClient("http://localhost:27012") # If this doesn't throw, then it started okay await c.identify()
async def test_can_store_subtopic(self): inst = self.runner.create_hds() c = DirectoryClient("http://localhost:27012", private_key_path=KEY_PATH) await c.send_state("hds.host", "example.com") await c.put_topic("test.topic", ["subtopic", "anothersubtopic"])