def test_join_one_app_should_produce_one_app_not_a_list(self, fixture): """ Um POST em /v2/apps, apesar de receber no body apens uma app ({...}), após o request.join(), restá produzindo um request com uma lista de apps: [{...}], e a API do marathon não aceita lista no POST apenas no PUT. O problema parece ser no request.join():89, onde fazemos if self.is_list_app_request(). Precisamos olhar se é PUT ou POST e gerar list() ou dict() apropriadamente. """ with application.test_request_context("/v2/apps/", method="POST", data=json.dumps(fixture)) as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) mock_app = get_fixture("single_full_app.json") mock_apps = [(MarathonApp.from_json(mock_app), Mock())] joined_request = request_parser.join(mock_apps) self.assertIsInstance(joined_request, HollowmanRequest) joined_request_data = json.loads(joined_request.data) self.assertFalse( isinstance(joined_request_data, list), "Body não deveria ser uma lista", ) self.assertEqual("/foo", joined_request_data["id"])
def test_split_groups_read_on_specific_group(self, group_b_fixture): with application.test_request_context('/v2/groups/group-b', method='GET') as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) with RequestsMock() as rsps: rsps.add(method='GET', url=conf.MARATHON_ADDRESSES[0] + '/v2/groups//dev/group-b', body=json.dumps(group_b_fixture), status=200) apps = list(request_parser.split()) self.assertEqual(2, len(apps)) original_app_one = MarathonApp.from_json( {"id": "/dev/group-b/appb0"}) original_app_two = MarathonApp.from_json( {"id": "/dev/group-b/group-b0/app0"}) expected_apps = [ (request_parser.merge_marathon_apps( MarathonApp(), original_app_one), original_app_one), (request_parser.merge_marathon_apps( MarathonApp(), original_app_two), original_app_two), ] self.assertEqual(expected_apps, apps)
def test_can_read_app_if_already_migrated(self, single_full_app_fixture): """ Conferimos que é possível fazer um GET em /v2/apps/<app-id> para uma app que já está migrada. O <app-id> usado é sempre *sem* namespace """ request_data = deepcopy(single_full_app_fixture) single_full_app_fixture["id"] = "/dev/foo" with application.test_request_context("/v2/apps/foo", method="GET") as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) with RequestsMock() as rsps: rsps.add( method="GET", url=conf.MARATHON_ADDRESSES[0] + "/v2/apps//dev/foo", body=json.dumps({"app": single_full_app_fixture}), status=200, ) apps = list(request_parser.split()) original_app = MarathonApp.from_json(single_full_app_fixture) expected_app = ( request_parser.merge_marathon_apps(MarathonApp(), original_app), original_app, ) self.assertEqual(apps, [expected_app])
def test_do_not_call_task_method_if_filter_does_not_implement(self): """ Caso um filtro não implemente write_task, apenas não chamamos esse filtro durante o pipeline """ class DummyFilter: def write(self, user, request_app, original_app): raise Exception() pipeline = { OperationType.WRITE: [DummyFilter(), ] } request_data = {"ids": ["task_0"]} request_app = MarathonTask.from_json({'id': request_data['ids'][0]}) original_app = request_app # Por enquanto não temos como passar a original task with application.test_request_context("/v2/tasks/delete", method="POST", data=json.dumps(request_data), headers={"Content-type": "application/json"}) as ctx: ctx.request.user = self.user request = Request(ctx.request) with patch.object(request, "split", return_value=[(request_app, original_app)]): filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_app = MarathonTask.from_json({"id": filtered_request.get_json()['ids'][0]}) self.assertEqual("task_0", filtered_app.id)
def test_a_request_with_n_apps_returns_n_marathonapps(self, fixture): with application.test_request_context("/v2/apps/", method="GET") as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) with RequestsMock() as rsps: rsps.add( method="GET", url=conf.MARATHON_ADDRESSES[0] + "/v2/groups//dev/", body=json.dumps(fixture), status=200, ) apps = list(request_parser.split()) self.assertEqual( [request_app for request_app, _ in apps], [ request_parser.merge_marathon_apps( request_app, original_app) for request_app, original_app in apps ], ) self.assertEqual( [app.id for app, _ in apps], [app["id"] for app in fixture["apps"]], )
def test_preserve_upgrade_strategy_added_by_filter(self): class AddNewUpgradeStrategyFilter: def write(self, user, request_app, original_app): us_data = { "maximumOverCapacity": 1, "minimumHealthCapacity": 0.75 } request_app.upgrade_strategy = MarathonUpgradeStrategy.from_json(us_data) return request_app pipeline = { OperationType.WRITE: [AddNewUpgradeStrategyFilter(), ] } # Simulando uma app que veio sem o campo "upgradeStrategy" request_data = {} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", headers={"Content-type": "application/json"}, data=json.dumps(request_data)) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_app = AsgardApp.from_json(filtered_request.get_json()) self.assertEqual(1, filtered_app.upgrade_strategy.maximum_over_capacity) self.assertEqual(0.75, filtered_app.upgrade_strategy.minimum_health_capacity) self._check_other_fields("upgradeStrategy", filtered_app)
def test_preserve_envs_added_by_filter(self): class AddNewEnvFilter: def write(self, user, request_app, original_app): request_app.env["env1"] = "env-value1" return request_app pipeline = { OperationType.WRITE: [AddNewEnvFilter(), ] } request_data = {"env": []} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", headers={"Content-type": "application/json"}, data=json.dumps(request_data)) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_app = AsgardApp.from_json(filtered_request.get_json()) self.assertEqual(1, len(filtered_app.env.keys())) self.assertEqual({"env1": "env-value1"}, filtered_app.env) self._check_other_fields("env", filtered_app)
def test_update_app_change_all_constraints(self): """ Devemos respeitar as constraints quem estão da request, elas devem substituir as constrains da app original """ class DummyFilter: def write(self, user, request_app, original_app): return request_app pipeline = { OperationType.WRITE: [DummyFilter(), ] } request_data = {"constraints": [["hostname", "LIKE", "myhost"]]} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", data=json.dumps(request_data), headers={"Content-type": "application/json"}) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_app = AsgardApp.from_json(filtered_request.get_json()) self.assertEqual(1, len(filtered_app.constraints)) self.assertEqual(["hostname", "LIKE", "myhost"], filtered_app.constraints[0].json_repr()) self._check_other_fields("constraints", filtered_app)
def test_preserve_constraints_added_by_filter(self): """ Certifica que quando um request remove todas as constraints e algum filtro adiciona novas constraints, essas constraints adicionadas pelo filtro são preservadas """ class AddNewConstraintFilter: def write(self, user, request_app, original_app): request_app.constraints.append(MarathonConstraint.from_json("key:LIKE:value".split(":"))) return request_app pipeline = { OperationType.WRITE: [AddNewConstraintFilter(), ] } request_data = {"constraints": []} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", headers={"Content-type": "application/json"}, data=json.dumps(request_data)) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_request_app = AsgardApp.from_json(filtered_request.get_json()) self.assertEqual(1, len(filtered_request_app.constraints)) self._check_other_fields("constraints", filtered_request_app)
def test_update_app_remove_all_constraints(self): """ Certifica que um request que remove todas as constraints, remove essas constraints na app original """ class DummyFilter: def write(self, user, request_app, original_app): return request_app pipeline = { OperationType.WRITE: [DummyFilter(), ] } request_data = {"constraints": []} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", data=json.dumps(request_data), headers={"Content-type": "application/json"}) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline) filtered_app = AsgardApp.from_json(filtered_request.get_json()) self.assertEqual(0, len(filtered_app.constraints)) self._check_other_fields("constraints", filtered_app)
def test_dispatch_should_pass_an_instance_if_SieveMarathonApp_to_filters(self): """ Certifica que quando um request remove todas as constraints e algum filtro adiciona novas constraints, essas constraints adicionadas pelo filtro são preservadas """ class AddNewConstraintFilter: def write(self, user, request_app, original_app): assert isinstance(request_app, AsgardApp) return request_app pipeline = { OperationType.WRITE: [AddNewConstraintFilter(), ] } request_data = {"constraints": []} request_app = AsgardApp.from_json(request_data) original_app = AsgardApp.from_json(deepcopy(self.single_full_app_fixture)) with application.test_request_context("/v2/apps/foo", method="PUT", headers={"Content-type": "application/json"}, data=json.dumps(request_data)) as ctx: ctx.request.user = self.user request = Request(ctx.request) filtered_request = dispatch(self.user, request, filters_pipeline=pipeline)
def test_return_correct_request_resource(self): expected_request_resources = { "/v2/apps": RequestResource.APPS, "/v2/apps/": RequestResource.APPS, "/v2/groups": RequestResource.GROUPS, "/v2/groups/": RequestResource.GROUPS, "/v2/deployments": RequestResource.DEPLOYMENTS, "/v2/deployments/": RequestResource.DEPLOYMENTS, "/v2/tasks": RequestResource.TASKS, "/v2/tasks/": RequestResource.TASKS, "/v2/queue": RequestResource.QUEUE, "/v2/queue/": RequestResource.QUEUE, } for ( request_path, expected_request_resource, ) in expected_request_resources.items(): # noinspection PyTypeChecker request_mock = Mock(path=request_path) request_wrapper = Request(request_mock) response_wrapper = Response(request_mock, Mock()) self.assertEqual(request_wrapper.request_resource, expected_request_resource) self.assertEqual(response_wrapper.request_resource, expected_request_resource)
def test_change_request_path_if_is_write_on_one_app(self, fixture): """ Quando fazemos WRITE em cima de uma app específica, devemos ajustar o request.path para que o `upstream_request` seja feito no endpoint correto. """ user = User(tx_name="User One", tx_email="*****@*****.**") user.current_account = Account(name="Dev", namespace="dev", owner="company") full_app_with_name_space = deepcopy(fixture) full_app_with_name_space['id'] = "/dev/foo" with application.test_request_context('/v2/apps//foo', method='PUT', data=json.dumps(fixture)) as ctx: with RequestsMock() as rsps: rsps.add(method='GET', url=conf.MARATHON_ADDRESSES[0] + '/v2/apps//dev/foo', body=json.dumps({'app': full_app_with_name_space}), status=200) ctx.request.user = user request_parser = Request(ctx.request) apps = list(request_parser.split()) request = request_parser.join(apps) self.assertIsInstance(request, HollowmanRequest) self.assertEqual("/v2/apps/dev/foo", request.path)
def test_adjust_groups_root_request_path(self): with application.test_request_context('/v2/groups/', method='GET') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonGroup(id="/dev/") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/groups/dev", request_wrapper.request.path)
def setUp(self, single_full_app_fixture): self.request = Request(None) self.single_full_app_fixture = single_full_app_fixture self.filter = IncompatibleFieldsFilter() self.request_app = SieveMarathonApp.from_json( self.single_full_app_fixture) self.original_app = SieveMarathonApp.from_json( self.single_full_app_fixture)
def test_adjust_apps_versions_request_path(self): with application.test_request_context("/v2/apps/my-app/versions", method="GET") as ctx: request_wrapper = Request(ctx.request) original_app = MarathonApp(id="/dev/my-app") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/apps/dev/my-app/versions", request_wrapper.request.path)
def test_split_queue_should_return_empty_iterator(self): with application.test_request_context('/v2/queue', method='GET') as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) apps = list(request_parser.split()) self.assertEqual(0, len(apps)) self.assertEqual([], apps)
def test_adjust_groups_DELETE_request_path(self): with application.test_request_context("/v2/groups/my-group/", method="DELETE") as ctx: request_wrapper = Request(ctx.request) original_app = MarathonGroup(id="/dev/my-group") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/groups/dev/my-group", request_wrapper.request.path)
def test_adjust_apps_request_path_repeating_non_final_path(self): with application.test_request_context('/v2/apps/app0/app0/app1', method='GET') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonApp(id="/dev/app0/app0/app1") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/apps/dev/app0/app0/app1", request_wrapper.request.path)
def test_adjust_groups_request_path_repeating_final_part(self): with application.test_request_context( '/v2/groups/grp0/other/parts/grp0/', method='GET') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonGroup(id="/dev/grp0/grp0") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/groups/dev/grp0/grp0", request_wrapper.request.path)
def test_adjust_apps_request_path_no_aaditional_paths(self): with application.test_request_context('/v2/apps/my-app', method='GET') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonApp(id="/dev/my-app") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/apps/dev/my-app", request_wrapper.request.path)
def test_adjust_apps_tasks_task_id_DELETE_request_path(self): with application.test_request_context('/v2/apps/my-app/tasks/task_id', method='DELETE') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonApp(id="/dev/my-app") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual("/v2/apps/dev/my-app/tasks/task_id", request_wrapper.request.path)
def test_it_parses_task_id(self): expected_paths = { '/v2/tasks': None, '/v2/tasks/': None, '/v2/tasks/delete': None, } for request_path, expected_marathon_path in expected_paths.items(): # noinspection PyTypeChecker request_wrapper = Request(Mock(path=request_path)) self.assertEqual(request_wrapper.object_id, expected_marathon_path)
def test_it_recognizes_apps_requests(self): with application.test_request_context('/v2/apps//foo', method='GET', data=b'') as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request()) with application.test_request_context('/v2/apps/', method='PUT', data=b'') as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request()) with application.test_request_context('/v2/apps//', method='DELETE', data=b'') as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request())
def test_adjust_apps_request_path_keep_additional_paths_multiple_paths( self): with application.test_request_context( '/v2/apps/my-app/versions/2017-10-31T13:01:07.768Z', method='GET') as ctx: request_wrapper = Request(ctx.request) original_app = MarathonApp(id="/dev/my-app") request_wrapper._adjust_request_path_if_needed( request_wrapper.request, original_app) self.assertEqual( "/v2/apps/dev/my-app/versions/2017-10-31T13:01:07.768Z", request_wrapper.request.path)
def test_it_recognizes_apps_requests(self): with application.test_request_context("/v2/apps//foo", method="GET", data=b"") as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request()) with application.test_request_context("/v2/apps/", method="PUT", data=b"") as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request()) with application.test_request_context("/v2/apps//", method="DELETE", data=b"") as ctx: request_parser = Request(ctx.request) self.assertTrue(request_parser.is_app_request()) self.assertFalse(request_parser.is_group_request())
def test_join_apps_read_empty_list(self): with application.test_request_context('/v2/apps', method='GET') as ctx: ctx.request.user = self.user request = Request(ctx.request) with RequestsMock() as rsps: rsps.add(method='GET', url=conf.MARATHON_ADDRESSES[0] + '/v2/apps', status=200, body='''{"apps":[]}''') apps = list(request.split()) joined_request = request.join(apps) self.assertEqual("/v2/apps", joined_request.path) self.assertEqual(b"", joined_request.data)
def test_split_tasks_GET(self): """ Quadno recebemos um GET em /v2/tasks/delete, não temos o que fazer, entãoo request pode passar direto. Isso significa que o spit retorna [] """ with application.test_request_context('/v2/tasks/delete', method='GET') as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) tasks = list(request_parser.split()) self.assertEqual(0, len(tasks))
def test_change_request_path_if_is_read_single_app( self, single_full_app_fixture): with application.test_request_context('/v2/apps/foo', method='GET') as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) single_full_app_fixture['id'] = "/dev/foo" apps = [(MarathonApp.from_json(single_full_app_fixture), MarathonApp.from_json(single_full_app_fixture))] request = request_parser.join(apps) self.assertIsInstance(request, HollowmanRequest) self.assertEqual("/v2/apps/dev/foo", request.path)
def test_it_recreates_a_post_request_for_a_single_app(self, fixture): with application.test_request_context('/v2/apps//foo', method='POST', data=json.dumps(fixture)) as ctx: ctx.request.user = self.user request_parser = Request(ctx.request) with patch.object(request_parser, 'marathon_client') as client: client.get_app.return_value = MarathonApp.from_json(fixture) apps = list(request_parser.split()) request = request_parser.join(apps) self.assertIsInstance(request, HollowmanRequest) self.assertEqual(request.get_json()['id'], '/foo')