async def test_can_reuse_port(self): @self.app.route(["/"], type=RouteTypes.HTTP, methods=["GET"]) async def index(request): return web.json_response({"OK": True}) with mock.patch.dict(os.environ, TEST_ASYNCWORKER_HTTP_PORT="9999"): async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json()) async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json())
async def test_parse_body(self): async with HttpClientContext(self.app) as client: resp = await client.post("/body", json=Model(name="Dalton", numero=42).dict()) data = await resp.json() self.assertEqual({"name": "Dalton", "numero": 42}, data)
async def test_returns_200_ok(self): async with HttpClientContext(app) as client: resp = await client.get("/_version") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"git-commit": "", "git-tag-ref": ""}, data)
async def test_process_collector_metric(self): async with HttpClientContext(self.app) as client: metrics = await client.get(self.METRICS_PATH) self.assertEqual(HTTPStatus.OK, metrics.status) data = await metrics.text() metrics_parsed = list(text_string_to_metric_families(data)) metrics = [ m for m in metrics_parsed if m.name.startswith(f"{NAMESPACE}_process") ] self.assertEqual(6, len(metrics)) metric_names = [m.name for m in metrics] self.assertEqual( [ "asyncworker_process_virtual_memory_bytes", "asyncworker_process_resident_memory_bytes", "asyncworker_process_start_time_seconds", "asyncworker_process_cpu_seconds", "asyncworker_process_open_fds", "asyncworker_process_max_fds", ], metric_names, )
async def test_agent_app_list_apps_running(self): self._prepare_additional_fixture_data() await self.pg_data_mocker.create() async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses(passthrough=[local_address]) as rsps: build_mesos_cluster(rsps, "ead07ffb-5a61-42c9-9386-21b680597e6c-S0") resp = await client.get( "/agents/ead07ffb-5a61-42c9-9386-21b680597e6c-S0/apps", headers={"Authorization": f"Token {self.user_auth_key}"}, ) self.assertEqual(200, resp.status) data = await resp.json() self.assertEqual(5, len(data["apps"])) expected_app_ids = sorted([ "captura/wetl/visitcentral", "portal/api", "captura/kirby/feeder", "infra/asgard/api", "infra/rabbitmq", ]) self.assertEqual( expected_app_ids, sorted([app["id"] for app in data["apps"]]), )
async def test_health(self): async with HttpClientContext(app) as client: resp = await client.get("/health") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"OK": True}, data)
async def test_apps_stats_check_uri_regex(self): async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses( passthrough=[local_address, settings.STATS_API_URL ]) as rsps: agent_id = "ead07ffb-5a61-42c9-9386-21b680597e6c-S0" build_mesos_cluster(rsps, agent_id) # namespace=asgard-infra resp = await client.get( f"/apps/asgard/pgsql-9.4/stats?account_id={ACCOUNT_DEV_ID}", headers={ "Authorization": f"Token {USER_WITH_MULTIPLE_ACCOUNTS_AUTH_KEY}" }, ) self.assertEqual(HTTPStatus.OK, resp.status) resp = await client.get( f"/apps/asgard/pgsql-9..4/stats?account_id={ACCOUNT_DEV_ID}", headers={ "Authorization": f"Token {USER_WITH_MULTIPLE_ACCOUNTS_AUTH_KEY}" }, ) self.assertEqual(HTTPStatus.OK, resp.status)
async def test_resolves_handler_parameters(self): expected_user_name = "Some User Name" class User: def __init__(self, name): self.name = name def insert_user_into_type_registry(handler): async def _wrapper(wrapper: RequestWrapper): wrapper.types_registry.set(User(name=expected_user_name)) return await call_http_handler(wrapper.http_request, handler) return _wrapper @self.app.http.get(["/"]) @insert_user_into_type_registry async def handler(user: User): return web.json_response({"name": user.name}) async with HttpClientContext(self.app) as client: settings_mock = Settings() with mock.patch("asyncworker.signals.handlers.http.settings", settings_mock): resp = await client.get("/") self.assertEqual(200, resp.status) resp_data = await resp.json() self.assertEqual({"name": expected_user_name}, resp_data)
async def test_gauge_metric(self): metric_name = "myapp_example_gauge" gauge = Gauge(metric_name, "um Gauge simples", registry=REGISTRY) @self.app.http.get(["/foo/{value}"]) @parse_path async def _handler(value: int): gauge.inc(value) return web.json_response({}) async with HttpClientContext(self.app) as client: await client.get("/foo/10") await client.get("/foo/20") await client.get("/foo/-5") metrics = await client.get(self.METRICS_PATH) self.assertEqual(HTTPStatus.OK, metrics.status) data = await metrics.text() metrics_parsed = list(text_string_to_metric_families(data)) gauge_metric = [ m for m in metrics_parsed if m.name == f"{NAMESPACE}_{metric_name}" ] self.assertEqual(1, len(gauge_metric)) self.assertEqual(25.0, gauge_metric[0].samples[0].value)
async def test_agents_list_includes_app_list(self): self._prepare_additional_fixture_data() await self.pg_data_mocker.create() async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses(passthrough=[local_address]) as rsps: build_mesos_cluster( rsps, "ead07ffb-5a61-42c9-9386-21b680597e6c-S0", # namespace=asgard-infra "ead07ffb-5a61-42c9-9386-21b680597e6c-S3", # namespace=asgard-infra "ead07ffb-5a61-42c9-9386-21b680597e6c-S10", # namespace=asgard ) resp = await client.get( "/agents", headers={"Authorization": f"Token {self.user_auth_key}"}, ) self.assertEqual(200, resp.status) data = await resp.json() self.assertEqual(2, len(data["agents"])) self.assertEqual( set(["asgard-infra"]), set([ agent["attributes"]["owner"] for agent in data["agents"] ]), ) self.assertEqual( "ead07ffb-5a61-42c9-9386-21b680597e6c-S0", data["agents"][0]["id"], ) self.assertEqual( "ead07ffb-5a61-42c9-9386-21b680597e6c-S3", data["agents"][1]["id"], ) self.assertEqual("MESOS", data["agents"][0]["type"]) self.assertEqual(5, data["agents"][0]["total_apps"]) expected_app_ids = sorted([ "infra/asgard/api", "infra/rabbitmq", "captura/kirby/feeder", "portal/api", "captura/wetl/visitcentral", ]) self.assertEqual( expected_app_ids, sorted([ app["id"] for app in data["agents"][0]["applications"] ]), ) self.assertEqual(0, data["agents"][1]["total_apps"]) expected_app_ids_agent_s3 = sorted([]) self.assertEqual( expected_app_ids_agent_s3, sorted([ app["id"] for app in data["agents"][1]["applications"] ]), )
async def test_agents_with_attrs_empty_response(self): self._prepare_additional_fixture_data() await self.pg_data_mocker.create() async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses(passthrough=[local_address]) as rsps: build_mesos_cluster( rsps, "ead07ffb-5a61-42c9-9386-21b680597e6c-S44", "ead07ffb-5a61-42c9-9386-21b680597e6c-S0", # namespace=asgard-infra "ead07ffb-5a61-42c9-9386-21b680597e6c-S10", # namespace=asgard ) resp = await client.get( "/agents/with-attrs?tag1=not-found", headers={"Authorization": f"Token {self.user_auth_key}"}, ) self.assertEqual(200, resp.status) data = await resp.json() self.assertEqual( { "agents": [], "stats": { "cpu_pct": "0.00", "ram_pct": "0.00" }, }, data, )
async def test_agents_with_atrrs_two_attrs_filter(self): self._prepare_additional_fixture_data() await self.pg_data_mocker.create() async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses(passthrough=[local_address]) as rsps: build_mesos_cluster( rsps, "ead07ffb-5a61-42c9-9386-21b680597e6c-S0", # namespace=asgard-infra "ead07ffb-5a61-42c9-9386-21b680597e6c-S3", # namespace=asgard-infra "ead07ffb-5a61-42c9-9386-21b680597e6c-S4", # namespace=asgard-dev "ead07ffb-5a61-42c9-9386-21b680597e6c-S44", # namespace=dev ) resp = await client.get( "/agents/with-attrs?workload=general&dc=gcp", headers={"Authorization": f"Token {self.user_auth_key}"}, ) self.assertEqual(200, resp.status) data = await resp.json() self.assertEqual(1, len(data["agents"])) self.assertEqual( "ead07ffb-5a61-42c9-9386-21b680597e6c-S0", data["agents"][0]["id"], )
async def test_path_param_bool_false_values(self): @self.app.http.get(["/bool/{on}/{true}/{yes}/{one}"]) async def bool_true( on: PathParam[bool], true: PathParam[bool], yes: PathParam[bool], one: PathParam[bool], ): return json_response( { "on": await on.unpack(), "true": await true.unpack(), "yes": await yes.unpack(), "one": await one.unpack(), } ) async with HttpClientContext(self.app) as client: resp = await client.get("/bool/oFf/falSe/No/0") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual( {"on": False, "true": False, "yes": False, "one": False}, data )
async def test_with_multiple_custom_decorator(self): def _deco_1(handler): @wraps(handler) async def _wrap(r: RequestWrapper): r.types_registry.set(42, type_definition=int) return await call_http_handler(r, handler) return _wrap def _deco_2(handler): @wraps(handler) async def _wrap(r: RequestWrapper): r.types_registry.set("value", type_definition=str) return await call_http_handler(r, handler) return _wrap @self.app.http.get(["/path/{n}"]) @_deco_2 @_deco_1 async def _path(r: RequestWrapper, n: PathParam[int], p: int, v: str): return json_response({"n": await n.unpack(), "p": p, "v": "value"}) async with HttpClientContext(self.app) as client: resp = await client.get("/path/10") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"n": 10, "p": 42, "v": "value"}, data)
async def test_intermediate_deco_can_add_available_handler_arguments(self): """ Um decorator intemediário pode adicionar novos parametros no types_registry do request. E esse parametro será corretamente passado ao handler """ def one_deco(handler): @wraps(handler) async def _h(r: RequestWrapper): r.types_registry.set("String") return await call_http_handler(r, handler) return _h @self.app.http.get(["/parse_path_again/{p}"]) @parse_path @one_deco async def _parse_path_handler(r: RequestWrapper, p: int, s: str): return web.json_response({"p": p}) async with HttpClientContext(self.app) as client: resp = await client.get("/parse_path_again/42") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"p": 42}, data)
async def test_returns_200_ok(self): async with HttpClientContext(app) as client: resp = await client.get("/echo/10") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"value": 10, "x2": 20}, data)
async def test_handler_decorator_can_receive_aiohttp_request(self): def my_decorator(handler): async def _wrapper(request: web.Request): r_wrapper = RequestWrapper( http_request=request, types_registry=request["types_registry"], ) return await call_http_handler(r_wrapper, handler) return _wrapper @self.app.http.get(["/"]) @my_decorator async def handler(wrapper: RequestWrapper): return web.json_response({"num": wrapper.http_request.query["num"]}) async with HttpClientContext(self.app) as client: settings_mock = Settings() with mock.patch( "asyncworker.signals.handlers.http.settings", settings_mock ): resp = await client.get("/", params={"num": 42}) self.assertEqual(200, resp.status) resp_data = await resp.json() self.assertEqual({"num": "42"}, resp_data)
async def test_user_can_use_metrics_endpoint_if_default_is_renamed(self): """ Se estivermos com o endpoint de métricas habilitado mas o path foi trocado então devemos poder registrar um endpoint com o valor default do path de metricas Não é o melhor jeito de fazer o teste mas como o settings é instanciado no nível do módulo não temos muito com controlar o desencadear dos imports então por isso preciso ficar substiuindo os módulos para que o "novo" settings (após reload()) surta efeito. """ from asyncworker import routes app = App() with mock.patch.dict( os.environ, ASYNCWORKER_METRICS_ROUTE_PATH="/asyncworker-metrics"): reload(conf) with mock.patch.object(routes, "conf", conf): @app.http.get(["/metrics"]) async def _h(): return web.json_response({}) async with HttpClientContext(app) as client: resp = await client.get("/metrics") self.assertEqual(HTTPStatus.OK, resp.status)
async def test_apps_stats_app_not_found(self): async with HttpClientContext(app) as client: local_address = ( f"http://{client._server.host}:{client._server.port}") with aioresponses( passthrough=[local_address, settings.STATS_API_URL ]) as rsps: agent_id = "ead07ffb-5a61-42c9-9386-21b680597e6c-S0" build_mesos_cluster(rsps, agent_id) # namespace=asgard-infra app_stats_datapoints = get_fixture( f"agents/{agent_id}/app_stats.json") resp = await client.get( f"/apps/asgard/api/not-exist/stats?account_id={ACCOUNT_DEV_ID}", headers={ "Authorization": f"Token {USER_WITH_MULTIPLE_ACCOUNTS_AUTH_KEY}" }, ) self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual( AppStats(cpu_pct="0", ram_pct="0", cpu_thr_pct="0").dict(), data["stats"], )
async def test_client_can_perform_requests(self): @self.app.http.get(["/"]) async def index(request): return web.json_response({"OK": True}) async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json())
async def test_client_can_perform_requests(self): @self.app.route(["/"], type=RouteTypes.HTTP, methods=["GET"]) async def index(request): return web.json_response({"OK": True}) async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json())
async def test_parse_simple_int(self): @self.app.http.get(["/num/{n}"]) async def _get(n: PathParam[int]): return json_response({"n": await n.unpack()}) async with HttpClientContext(self.app) as client: resp = await client.get("/num/42") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"n": 42}, data)
async def test_can_reuse_port(self): """ Esse teste falha em alguns locais e não em outros. Imagino que por causa de configurações de Stack TCP. Por enquanto vamos deixá-lo desligado até conseguirmos fazê-lo passar de forma consistente. """ @self.app.http.get(["/"]) async def index(request): return web.json_response({"OK": True}) with mock.patch.dict(os.environ, TEST_ASYNCWORKER_HTTP_PORT="10000"): async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json()) async with HttpClientContext(self.app) as http_client: resp = await http_client.get("/") self.assertEqual({"OK": True}, await resp.json())
async def test_path_param_bool_invalid_values(self): @self.app.http.get(["/bool/{on}"]) async def bool_true(on: PathParam[bool],): return json_response({"on": await on.unpack()}) async with HttpClientContext(self.app) as client: resp = await client.get("/bool/invalid") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"on": False}, data)
async def test_can_registrer_a_coroutine_as_a_valid_handler(self): app = App() @app.route(["/"], type=RouteTypes.HTTP, methods=["GET"]) async def handler(wrapper: RequestWrapper): return web.json_response({"OK": True}) async with HttpClientContext(app) as client: resp = await client.get("/") data = await resp.json() self.assertEqual({"OK": True}, data)
async def test_one_param_mismatched_type(self): @self.app.http.get(["/num/{n}"]) async def _get(n: PathParam[int]): return json_response({"n": await n.unpack()}) async with HttpClientContext(self.app) as client: resp = await client.get("/num/abc") self.assertEqual(HTTPStatus.BAD_REQUEST, resp.status) data = await resp.text() self.assertEqual( "invalid literal for int() with base 10: 'abc'", data )
async def test_can_registrer_a_coroutine_as_a_valid_handler_new_decorator( self): app = App() @app.http.get(["/"]) async def handler(wrapper: RequestWrapper): return web.json_response({"OK": True}) async with HttpClientContext(app) as client: resp = await client.get("/") data = await resp.json() self.assertEqual({"OK": True}, data)
async def test_registers_http_route(self): @self.app.http(["/hello"]) async def _hello_handler(): return web.json_response({"Hello": True}) async with HttpClientContext(self.app) as client: resp = await client.get("/hello") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"Hello": True}, data)
async def test_multiple_params_of_the_same_type(self): @self.app.http.get(["/num/{n}/{other}"]) async def _get(other: PathParam[int], n: PathParam[int]): return json_response( {"n": await n.unpack(), "other": await other.unpack()} ) async with HttpClientContext(self.app) as client: resp = await client.get("/num/42/99") self.assertEqual(HTTPStatus.OK, resp.status) data = await resp.json() self.assertEqual({"n": 42, "other": 99}, data)
async def test_handler_can_receive_aiohttp_request(self): @self.app.http.get(["/"]) async def handler(request: web.Request) -> web.Response: return web.json_response({"num": request.query["num"]}) async with HttpClientContext(self.app) as client: settings_mock = Settings() with mock.patch("asyncworker.signals.handlers.http.settings", settings_mock): resp = await client.get("/", params={"num": 42}) self.assertEqual(200, resp.status) resp_data = await resp.json() self.assertEqual({"num": "42"}, resp_data)