def config_with_migrations( tmp_path: Path, mocker: ptm.MockFixture, request: t.Any, ) -> t.Tuple[Path, model.Config, int]: migrations_count = 10 script_location = tmp_path if request.param == 'relative': script_location = Path( '/'.join([ '..' for _ in range(len(script_location.absolute().parents, ) + 1) ]) + str(tmp_path), ) files = [(script_location / f'migration_{i}.py') for i in range(migrations_count)] for i, f in enumerate(files): f.touch(exist_ok=False) f.write_text( '\n'.join([ '', f'revision = {i+1}', '', 'async def upgrade():', ' ...', '', 'async def downgrade():', ' ...', ]), ) return script_location, model.Config( script_location=script_location, database_name=mocker.stub(), database_dsn=mocker.stub(), ), migrations_count
def config_with_migrations( tmp_path: t.Any, mocker: ptm.MockFixture, ) -> t.Tuple[model.Config, int]: migrations_count = 10 files = [(tmp_path / f'migration_{i}.py') for i in range(migrations_count)] for i, f in enumerate(files): f.touch(exist_ok=False) f.write_text( '\n'.join([ '', f'revision = {i+1}', '', 'async def upgrade():', ' ...', '', 'async def downgrade():', ' ...', ]), ) return model.Config( script_location=tmp_path, database_name=mocker.stub(), database_dsn=mocker.stub(), ), migrations_count
async def test_get_revision_migration_table_exists_with_entries( db_connection: asyncpg.Connection, table_schema: str, table_name: str, mocker: ptm.MockFixture, ) -> None: max_revisions = 10 await migration.create_table( connection=db_connection, table_schema=table_schema, table_name=table_name, ) for i in range(1, max_revisions + 1): await migration.save( connection=db_connection, migration=model.Migration( revision=model.Revision(i), label=__name__, path=mocker.stub(), upgrade=mocker.stub(), downgrade=mocker.stub(), ), direction=secrets.choice([ model.MigrationDir.DOWN, model.MigrationDir.UP, ]), table_schema=table_schema, table_name=table_name, ) assert (await migration.latest_revision( connection=db_connection, table_schema=table_schema, table_name=table_name, )) == max_revisions
def test_app_add_api_different_base_path( mocker: ptm.MockFixture, tmp_path: Path, ) -> None: spec_one = mocker.stub() spec_one.servers = [model.OASServer(url='/v1', variables={})] spec_two = mocker.stub() spec_two.servers = [model.OASServer(url='/v2', variables={})] spec_admin = mocker.stub() spec_admin.servers = [model.OASServer(url='/admin', variables={})] apply_spec = mocker.patch('axion.plugins._aiohttp._apply_specification') the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(spec_one) the_app.add_api(spec_two) the_app.add_api(spec_admin) router_resources = list(the_app.root_app.router.resources()) assert len(router_resources) == 3 assert len(the_app.api_base_paths) == 3 for prefix, the_spec in ( ('/v1', spec_one), ('/v2', spec_two), ('/admin', spec_admin), ): sub_app_a = the_app.api_base_paths.get(prefix, None) sub_app_b = next( (r for r in router_resources if r.canonical == prefix), None) assert sub_app_a is not None assert isinstance( sub_app_a, web.Application, ) assert sub_app_b is not None assert isinstance( sub_app_b, web_urldispatcher.PrefixedSubAppResource, ) assert sub_app_a == sub_app_b._app apply_spec.assert_any_call( for_app=sub_app_b._app, spec=the_spec, ) apply_spec.assert_any_call( for_app=sub_app_a, spec=the_spec, )
def test_app_add_api_duplicated_base_path( server_url: str, mocker: ptm.MockFixture, ) -> None: spec_one = mocker.stub() spec_one.servers = [model.OASServer(url=server_url, variables={})] spec_two = mocker.stub() spec_two.servers = [model.OASServer(url=server_url, variables={})] apply_spec = mocker.patch('axion.plugins._aiohttp._apply_specification') the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(spec_one) with pytest.raises( app.DuplicateBasePath, match=(f'You tried to add API with base_path={server_url}, ' f'but it is already added. ' f'If you want to add more than one API, you will need to ' f'specify unique base paths for each API. ' f'You can do this either via OAS\'s "servers" property or ' f'base_path argument of this function.'), ): the_app.add_api(spec_two) assert len(the_app.api_base_paths) == 1 assert mock.call( for_app=the_app.api_base_paths[server_url], spec=spec_one, ) in apply_spec.call_args_list assert mock.call( for_app=the_app.api_base_paths[server_url], spec=spec_two, ) not in apply_spec.call_args_list if server_url == '/': apply_spec.assert_called_once_with( for_app=the_app.root_app, spec=spec_one, ) assert not the_app.root_app._subapps else: assert the_app.root_app._subapps assert 1 == len(the_app.root_app._subapps) apply_spec.assert_called_once_with( for_app=the_app.root_app._subapps[-1], spec=spec_one, )
def test_add_api_multiple_servers( mocker: ptm.MockFixture, caplog: _logging.LogCaptureFixture, ) -> None: loaded_spec = mocker.stub() loaded_spec.servers = [ model.OASServer(url='/v1', variables={}), model.OASServer(url='/v2', variables={}), model.OASServer(url='/v3', variables={}), ] mocker.patch('axion.plugins._aiohttp._apply_specification') the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(spec=loaded_spec) assert len(the_app.api_base_paths) == 1 assert '/v1' in the_app.api_base_paths assert '/v2' not in the_app.api_base_paths assert '/v3' not in the_app.api_base_paths msg = ('There are 3 servers, axion will assume first one. ' 'This behavior might change in the future, once axion knows ' 'how to deal with multiple servers') assert next( filter( lambda r: r.levelname.lower() == 'warning' and r.msg == msg, caplog.records, ), None, ) is not None
def test_tictactoe_draw(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('X 1 1') bot.handle_message('O 2 2') bot.handle_message('X 0 0') bot.handle_message('O 1 0') bot.handle_message('X 1 2') bot.handle_message('O 2 1') bot.handle_message('X 2 0') bot.handle_message('O 0 2') bot.handle_message('X 0 1') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('...\n.X.\n...'), mocker.call('...\n.X.\n..O'), mocker.call('X..\n.X.\n..O'), mocker.call('XO.\n.X.\n..O'), mocker.call('XO.\n.X.\n.XO'), mocker.call('XO.\n.XO\n.XO'), mocker.call('XOX\n.XO\n.XO'), mocker.call('XOX\n.XO\nOXO'), mocker.call('XOX\nXXO\nOXO'), mocker.call('Game is finished, draw'), ]
def test_app_add_api_overlapping_base_paths( server_url: str, overlapping_server_url: str, mocker: ptm.MockFixture, ) -> None: spec_one = mocker.stub() spec_one.servers = [model.OASServer(url=server_url, variables={})] spec_two = mocker.stub() spec_two.servers = [ model.OASServer(url=overlapping_server_url, variables={}) ] apply_spec = mocker.patch('axion.plugins._aiohttp._apply_specification') the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(spec_one) with pytest.raises( app.OverlappingBasePath, match= (f'You tried to add API with base_path={overlapping_server_url}, ' f'but it is overlapping one of the APIs that has been already added. ' f'You need to make sure that base paths for all APIs do ' f'not overlap each other.'), ): the_app.add_api(spec_two) assert len(the_app.api_base_paths) == 1 apply_spec.assert_called_once_with( for_app=the_app.api_base_paths[server_url], spec=spec_one, ) if server_url == '/': apply_spec.assert_called_once_with( for_app=the_app.root_app, spec=spec_one, ) assert not the_app.root_app._subapps else: assert the_app.root_app._subapps assert 1 == len(the_app.root_app._subapps) apply_spec.assert_called_once_with( for_app=the_app.root_app._subapps[-1], spec=spec_one, )
def test_game_not_started(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('O 2 1') bot.handle_message('Hello') assert send_message.call_args_list == [ mocker.call('Game is not started'), mocker.call('Game is not started') ]
def test_chat_broadcast(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = ChatBot(send_message) bot.handle_message(10, 'hello') bot.handle_message(11, 'world') assert send_message.call_args_list == [ mocker.call(10, '#10: hello'), mocker.call(10, '#11: world'), mocker.call(11, '#11: world'), ]
def test_multiple_operations(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = CalculatorUserHandler(send_message) bot.handle_message('2 * 3') bot.handle_message('3 / 2') bot.handle_message('3 - 4') assert send_message.call_args_list == [ mocker.call('6'), mocker.call('1'), mocker.call('-1') ]
def test_wrong_symbols(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('X 0 0') bot.handle_message('A 1 1') bot.handle_message('B 2 2') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('X..\n...\n...'), mocker.call('Invalid turn'), mocker.call('Invalid turn') ]
def test_game_many_starts(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('start') bot.handle_message('start') bot.handle_message('start') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('...\n...\n...'), mocker.call('...\n...\n...'), mocker.call('...\n...\n...') ]
def test_tictactoe_wrong_arguments(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('X 0 0') bot.handle_message('X 1 0') bot.handle_message('O 0 0') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('X..\n...\n...'), mocker.call('Invalid turn'), mocker.call('Invalid turn'), ]
async def test_async_iterator_updates( self, progress: DurationProgress, mocker: pytest_mock.MockFixture) -> None: stub = mocker.stub() progress.duration = servo.Duration("0.7ms") async for update in progress.every("0.1ms"): stub(update.progress) try: stub.assert_called() except AssertionError as e: # TODO yagni code is yagni, fix test if it ever gets used pytest.xfail(f"Failure in unused code: {e}") assert progress.progress == 100.0
def test_true_if_code_matches( self, mocker: ptm.MockFixture, ) -> None: oas_op = mocker.stub() oas_op.id = mocker.ANY oas_op.responses = responses = { http_code: mocker.ANY for http_code in range(200, 500) } v = validator.HttpCodeValidator(oas_op) for http_code in responses.keys(): assert v({'http_code': http_code}) == http_code
def test_always_true_default_repr( self, mocker: ptm.MockFixture, ) -> None: oas_op = mocker.stub() oas_op.id = mocker.ANY oas_op.responses = { 'default': mocker.ANY, } v = validator.HttpCodeValidator(oas_op) for http_code in range(200, 500): assert v({'http_code': http_code}) == http_code
def test_db_history( cli_runner: testing.CliRunner, mocker: ptm.MockFixture, entries_count: int, ) -> None: @dataclass class MockedConfig: database_dsn: str database_name: str entries = model.MigrationHistory([ model.MigrationHistoryEntry( revision=model.Revision(rev), timestamp=model.Timestamp(dt.datetime.today()), label=mocker.stub(), direction=model.MigrationDir.UP if rev % 2 else model.MigrationDir.DOWN, ) for rev in range(entries_count) ]) mocker.patch( 'asyncpg_migrate.loader.load_configuration', return_value=MockedConfig( 'postgres://*****:*****@test:5432/test', 'test', ), ) mocker.patch( 'asyncpg.connect', side_effect=asyncio.coroutine(lambda dsn: object()), ) mocker.patch( 'asyncpg_migrate.engine.migration.list', side_effect=asyncio.coroutine(lambda *args, **kwargs: entries), ) from asyncpg_migrate import main result = cli_runner.invoke(main.db, 'history') if not entries_count: assert result.exception is not None assert result.exit_code == 1 assert 'No revisions found, you might want to run some migrations first :)' in \ result.output else: assert result.exception is None assert result.exit_code == 0 assert len(result.output.split('\n')[3:]) - 1 == entries_count
def test_many_invalid_turns(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('O 1 1') bot.handle_message('X 1 1') bot.handle_message('X 2 2') bot.handle_message('O 0 0') bot.handle_message('O 2 2') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('Invalid turn'), mocker.call('...\n.X.\n...'), mocker.call('Invalid turn'), mocker.call('O..\n.X.\n...'), mocker.call('Invalid turn') ]
def test_add_api_single_server( server_base_path: str, add_api_base_path: t.Optional[str], mocker: ptm.MockFixture, tmp_path: Path, ) -> None: loaded_spec = mocker.stub() loaded_spec.servers = [ model.OASServer( url=server_base_path, variables={}, ) ] apply_spec = mocker.patch('axion.plugins._aiohttp._apply_specification') get_base_path = mocker.patch( 'axion.plugins._aiohttp._get_base_path', return_value=server_base_path, ) the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(loaded_spec, add_api_base_path) assert len(the_app.api_base_paths) == 1 if add_api_base_path is not None: assert add_api_base_path in the_app.api_base_paths assert server_base_path not in the_app.api_base_paths get_base_path.assert_not_called() apply_spec.assert_called_once_with( for_app=the_app.api_base_paths[add_api_base_path], spec=loaded_spec, ) else: assert server_base_path in the_app.api_base_paths assert add_api_base_path not in the_app.api_base_paths get_base_path.assert_called_once_with(loaded_spec.servers) apply_spec.assert_called_once_with( for_app=the_app.api_base_paths[server_base_path], spec=loaded_spec, )
def test_tictactoe_winner_x(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('X 0 0') bot.handle_message('O 1 0') bot.handle_message('X 1 1') bot.handle_message('O 2 0') bot.handle_message('X 2 2') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('X..\n...\n...'), mocker.call('XO.\n...\n...'), mocker.call('XO.\n.X.\n...'), mocker.call('XOO\n.X.\n...'), mocker.call('XOO\n.X.\n..X'), mocker.call('Game is finished, X wins'), ]
def test_x_wins(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('start') bot.handle_message('X 1 1') bot.handle_message('O 1 2') bot.handle_message('X 0 0') bot.handle_message('O 2 2') bot.handle_message('X 0 2') bot.handle_message('O 2 0') bot.handle_message('X 0 1') assert send_message.call_args_list == [ mocker.call('...\n...\n...'), mocker.call('...\n.X.\n...'), mocker.call('...\n.X.\n.O.'), mocker.call('X..\n.X.\n.O.'), mocker.call('X..\n.X.\n.OO'), mocker.call('X..\n.X.\nXOO'), mocker.call('X.O\n.X.\nXOO'), mocker.call('X.O\nXX.\nXOO'), mocker.call('Game is finished, X wins') ]
def test_fail_if_no_match( self, mocker: ptm.MockFixture, ) -> None: oas_op = mocker.stub() oas_op.id = op_id = mocker.ANY oas_op.responses = responses = { http_code: mocker.ANY for http_code in range(200, 204) } v = validator.HttpCodeValidator(oas_op) wrong_code = 304 with pytest.raises(validator.ValidationError) as err: v({'http_code': wrong_code}) assert err.value.oas_operation_id == mocker.ANY assert err.value.occurred_at == 'return.http_code' assert err.value.message == ( f'HTTP code {wrong_code} does not match {op_id} ' f'response codes {{{", ".join(map(str, responses.keys()))}}}.' )
def test_app_add_with_custom_base_path(mocker: ptm.MockFixture) -> None: server_url = '/api/v1/' arg_base_path = '/' spec_one = mocker.stub() spec_one.servers = [ model.OASServer(url=server_url, variables={}), ] apply_spec = mocker.patch('axion.plugins._aiohttp._apply_specification') get_base_path = mocker.patch('axion.plugins._aiohttp._get_base_path') the_app = app.AioHttpPlugin(configuration=mocker.ANY) the_app.add_api(spec_one, base_path=arg_base_path) assert 1 == len(the_app.api_base_paths) assert arg_base_path in the_app.api_base_paths assert server_url not in the_app.api_base_paths apply_spec.assert_any_call( for_app=the_app.root_app, spec=spec_one, ) get_base_path.assert_not_called()
def test_tictactoe_not_started(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') bot = TicTacToeUserHandler(send_message) bot.handle_message('START THE GAME, STUPID BOT') assert send_message.call_args_list == [mocker.call('Game is not started')]
def test_addition(mocker: pytest_mock.MockFixture) -> None: send_message = mocker.stub(name='send_message_stub') CalculatorUserHandler(send_message).handle_message('2 + 3') assert send_message.call_args_list == [mocker.call('5')]