def test_multiple_hooks(): recorded_tasks = {} counter = 0 def record_page(task_info): recorded_tasks[task_info.get_a_url()] = task_info def increment_counter(task_info): nonlocal counter counter += 1 with context_for_test('app_2pages') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'page_frozen': [record_page, increment_counter] }, } freeze(module.app, config) print(recorded_tasks) assert len(recorded_tasks) == 2 assert counter == 2 info = recorded_tasks['http://example.com:80/'] assert info.path == 'index.html' info = recorded_tasks['http://example.com:80/second_page.html'] assert info.path == 'second_page.html'
def test_taskinfo_has_freezeinfo(): freezeinfos = [] def start_hook(freezeinfo): freezeinfos.append(freezeinfo) def page_frozen_hook(pageinfo): freezeinfos.append(pageinfo.freeze_info) with context_for_test('app_simple') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'start': [start_hook], 'page_frozen': [page_frozen_hook], }, } freeze(module.app, config) a, b = freezeinfos assert a is b
def test_add_hook(): recorded_tasks = {} def register_hook(freeze_info): freeze_info.add_hook('page_frozen', record_page) def record_page(task_info): recorded_tasks[task_info.get_a_url()] = task_info with context_for_test('app_2pages') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'start': [register_hook] }, } freeze(module.app, config) print(recorded_tasks) assert len(recorded_tasks) == 2 info = recorded_tasks['http://example.com:80/'] assert info.path == 'index.html' info = recorded_tasks['http://example.com:80/second_page.html'] assert info.path == 'second_page.html'
def test_task_counts_extra_page(): recorded_done_counts = [] recorded_paths = set() expected_total = 3 def record_start(freeze_info): check_task_counts(freeze_info, expected_total) assert freeze_info.done_task_count == 0 def record_page(task_info): check_task_counts(task_info.freeze_info, expected_total) recorded_done_counts.append(task_info.freeze_info.done_task_count) recorded_paths.add(task_info.path) with context_for_test('app_with_extra_page_deep') as module: config = { **module.freeze_config, 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'start': [record_start], 'page_frozen': [record_page], }, } freeze(module.app, config) assert recorded_done_counts == [1, 2, 3] assert recorded_paths == { 'index.html', 'extra/index.html', 'extra/extra_deep/index.html', }
def test_modified_mime_db_file(tmp_path): """Integration test with modified mime-db, where is purposely set wrong extensions for MIME image/png to check if our db was used. """ MIME_DB_TO_JSON = { "image/png": { "source": "iana", "compressible": False, "extensions": ["Jpeg","jPg"] }, 'text/html': { "extensions": ["htmL"] } } builddir = tmp_path / 'build' db_path = tmp_path / "mime_db.json" with open(db_path, mode="w") as mime_db: json.dump(MIME_DB_TO_JSON, mime_db) with context_for_test('app_wrong_mimetype') as module: freeze_config = { 'output': str(builddir), 'mime_db_file': str(db_path) } freeze(module.app, freeze_config) assert (builddir / 'index.html').exists() # 'image.jpg' exists because we linked jpg extension with MIME 'image/png' assert (builddir / 'image.jpg').exists()
def test_one_page(tmp_path): freeze(app, tmp_path) with open(tmp_path / "index.html", encoding='utf-8') as f: read_text = f.read() assert read_text == """
def test_tasks_are_limited(tmp_path): NUM_PAGES = MAX_RUNNING_TASKS * 2 currently_processed_pages = 0 class ResultIterator: def __init__(self): self.contents = iter([b'a', b'b']) def __iter__(self): return self def __next__(self): assert currently_processed_pages <= MAX_RUNNING_TASKS result = next(self.contents) return result def close(self): nonlocal currently_processed_pages currently_processed_pages -= 1 def app(environ, start_response): nonlocal currently_processed_pages currently_processed_pages += 1 start_response('200 OK', [('Content-type', 'text/html')]) return ResultIterator() config = { 'output': str(tmp_path), 'extra_pages': [f'{n}.html' for n in range(NUM_PAGES)], } freeze(app, config)
def test_output_dict(tmp_path, monkeypatch, app_name): app_path = FIXTURES_PATH / app_name error_path = app_path / 'error.txt' # Add FIXTURES_PATH to sys.path, the list of directories that `import` # looks in monkeypatch.syspath_prepend(str(app_path)) sys.modules.pop('app', None) try: module = importlib.import_module('app') app = module.app freeze_config = getattr(module, 'freeze_config', {}) freeze_config['output'] = 'dict' if error_path.exists(): with pytest.raises(ValueError): freeze(app, freeze_config) else: result = freeze(app, freeze_config) expected_dict = getattr(module, 'expected_dict', None) if expected_dict is None: pytest.skip('No expected_dict') assert result == expected_dict finally: sys.modules.pop('app', None)
def test_freezeinfo_add_404_url(reason, expected_reasons): hook_called = False def start_hook(freezeinfo): nonlocal hook_called hook_called = True freezeinfo.add_url( 'http://example.com/404-page.html', reason=reason, ) with context_for_test('app_with_extra_page') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'start': [start_hook] }, } with raises_multierror_with_one_exception(UnexpectedStatus) as e: freeze(module.app, config) assert e.freezeyt_task.reasons == expected_reasons
def test_overwrite_dir_with_index(tmp_path): builddir = tmp_path / 'build' builddir.mkdir() index_path = builddir / 'index.html' index_path.write_text('<html></html>') old_path = builddir / 'old.dat' old_path.write_text('1234') old_dir_path = builddir / 'dir' old_dir_path.mkdir() config = { **freeze_config, 'output': { 'type': 'dir', 'dir': builddir }, } freeze(app, config) assert index_path.exists() assert not old_path.exists() assert not old_dir_path.exists()
def test_let_incomplete_dir_intact(tmp_path): output_dir = tmp_path / "output" config = {"cleanup": False, "output": str(output_dir)} with raises_multierror_with_one_exception(UnexpectedStatus): freeze(app, config) assert output_dir.exists() # the output dir has to exist assert (output_dir / "index.html" ).exists() # the index.html file inside output dir has to exist
def test_cleanup_empty_app(tmp_path): """Test that cleanup succeeds even when no page is frozen""" output_dir = tmp_path / "output2" config = {"cleanup": True, "output": str(output_dir)} with raises_multierror_with_one_exception(UnexpectedStatus): freeze(empty_app, config) assert not output_dir.exists() # the output dir has to be gone
def test_circular_redirect(tmp_path, monkeypatch): with context_for_test('circular_redirect') as module: freeze_config = module.freeze_config freeze_config['output'] = {'type': 'dir', 'dir': tmp_path} freeze_config['status_handlers'] = {'3xx': 'follow'} with pytest.raises(InfiniteRedirection): freeze(module.app, freeze_config)
def test_simple_output_specification(tmp_path): expected = FIXTURES_PATH / 'app_2pages' / 'test_expected_output' with context_for_test('app_2pages') as module: freeze_config = {'output': str(tmp_path)} freeze(module.app, freeze_config) assert_dirs_same(tmp_path, expected)
def test_output(tmp_path, monkeypatch, app_name): app_path = FIXTURES_PATH / app_name error_path = app_path / 'error.txt' with context_for_test(app_name) as module: module = importlib.import_module('app') app = module.app freeze_config = getattr(module, 'freeze_config', {}) expected_dict = getattr(module, 'expected_dict', None) expecting_dir = not getattr(module, 'no_expected_directory', False) freeze_config['output'] = {'type': 'dir', 'dir': tmp_path} if error_path.exists(): with pytest.raises(ValueError) as exc: freeze(app, freeze_config) exception_name = exc.type.__name__ expected_name = error_path.read_text().strip() assert exception_name == expected_name else: # Non error app. # We want to check against expected data stored in a directory # and/or in a dict. At least one of those must exist. if not expecting_dir and expected_dict is None: raise AssertionError(f'({app_name}) is not contain any' + 'expected output (dict or dir)') if expecting_dir: # test the output saved in dir 'test_expected_output' freeze(app, freeze_config) # freeze content to tmp_path expected = app_path / 'test_expected_output' if not expected.exists(): make_output = os.environ.get('TEST_CREATE_EXPECTED_OUTPUT') if make_output == '1': shutil.copytree(tmp_path, expected) else: raise AssertionError( f'Expected output directory ({expected}) does not exist. ' + 'Run with TEST_CREATE_EXPECTED_OUTPUT=1 to create it' ) assert_dirs_same(tmp_path, expected) if expected_dict is not None: # test the output saved in dictionary freeze_config['output'] = {'type': 'dict'} result = freeze(app, freeze_config) # freeze content to dict assert result == expected_dict
def test_external_extra_files(tmp_path): with context_for_test('app_2pages') as module: freeze_config = { 'output': { 'type': 'dict' }, 'extra_pages': ['http://external.example/foo.html'], } with pytest.raises(ExternalURLError): freeze(module.app, freeze_config)
def test_except_star(): with context_for_test('app_various_errors') as module: app = module.app config = { 'output': {'type': 'dict'}, } try: freeze(app, config) except* TypeError as type_errors: assert len(type_errors.exceptions) == 2 except* UnexpectedStatus as status_errors: assert len(status_errors.exceptions) == 1
def test_do_not_cleanup_if_directory_exists_error(tmp_path): output_dir = tmp_path / "output4" output_dir.mkdir() file_path = output_dir / 'important.dat' file_path.write_text('inportant text') config = {"cleanup": True, "output": str(output_dir)} with pytest.raises(DirectoryExistsError): freeze(app, config) assert output_dir.exists() # the output dir has to exist assert (output_dir / 'important.dat' ).exists() # the important.dat file inside output dir has to exist
def test_multierror(tmp_path, monkeypatch): with context_for_test('app_2_broken_links') as module: freeze_config = { 'output': {'type': 'dir', 'dir': tmp_path}, } with pytest.raises(MultiError) as excinfo: freeze(module.app, freeze_config) multierror = excinfo.value assert len(multierror.exceptions) == 2 for exception in multierror.exceptions: with pytest.raises(UnexpectedStatus): raise exception
def test_reason_homepage(): app = Flask(__name__) config = { 'prefix': 'http://localhost/', 'output': { 'type': 'dict' }, } with raises_multierror_with_one_exception(UnexpectedStatus) as e: freeze(app, config) assert str(e.value.url) == 'http://localhost:80/' assert e.value.status[:3] == '404' assert e.freezeyt_task.reasons == ['site root (homepage)']
def test_overwrite_empty_dir(tmp_path): builddir = tmp_path / 'build' builddir.mkdir() index_path = builddir / 'index.html' config = { **freeze_config, 'output': { 'type': 'dir', 'dir': builddir }, } freeze(app, config) assert index_path.exists()
def test_page_frozen_hook_by_name(): with context_for_test('app_2pages') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'page_frozen': [f'{__name__}:hook'] }, } _recorded_hook_calls.clear() freeze(module.app, config) assert len(_recorded_hook_calls) == 2
def test_succesfully_loaded_get_mimetype_config(tmp_path, get_mimetypeID): """Test if user configuration of external functions get_mimetype is loaded and used during web app freezing. """ builddir = tmp_path / 'build' with context_for_test('default_mimetype_plain') as module: freeze_config = { 'output': str(builddir), 'get_mimetype': GET_MIMETYPES[get_mimetypeID], } freeze(module.app, freeze_config) assert (builddir / 'index.html').exists() assert (builddir / 'textfile').exists()
def test_get_no_links(tmp_path): """Test configuration of no url_finders. We should get just root page. """ builddir = tmp_path / 'build' with context_for_test('app_2pages') as module: freeze_config = { 'output': str(builddir), 'url_finders': {}, } freeze(module.app, freeze_config) assert (builddir / 'index.html').exists() assert not (builddir / 'second_page.html').exists()
def test_close(): closed = False class SpecialResult: def __init__(self): self.values = [b'body', b' ', b'content'] self.position = 0 def __next__(self): if self.position < len(self.values): value = self.values[self.position] self.position += 1 return value else: raise StopIteration() def __iter__(self): return self def close(self): nonlocal closed closed = True def simple_app(environ, start_response): # regular application code here status = "200 OK" response_headers = [("content-type", "text/html")] start_response(status, response_headers) return SpecialResult() config = {'output': {'type': 'dict'}} expected = {'index.html': b'body content'} assert freeze(simple_app, config) == expected assert closed == True
def test_do_not_overwrite_existing_dir(tmp_path): builddir = tmp_path / 'build' builddir.mkdir() file_path = builddir / 'important.dat' file_path.write_text('1234') config = { **freeze_config, 'output': { 'type': 'dir', 'dir': builddir }, } with pytest.raises(DirectoryExistsError): freeze(app, config)
def test_page_frozen_hook_with_redirects(policy): recorded_tasks = [] def record_page(task_info): recorded_tasks.append(task_info.path) with context_for_test('app_redirects') as module: config = dict(module.freeze_config) config['output'] = {'type': 'dict'} config['hooks'] = {'page_frozen': [record_page]} config['redirect_policy'] = policy output = freeze(module.app, config) output_paths = [] def add_output_to_output_paths(dict_to_add, path_so_far): for key, value in dict_to_add.items(): if isinstance(value, bytes): output_paths.append(path_so_far + key) else: add_output_to_output_paths(value, path_so_far + key + '/') add_output_to_output_paths(output, '') recorded_tasks.sort() output_paths.sort() assert recorded_tasks == output_paths
def test_default_handlers(response_status): app = Flask(__name__) config = { 'output': { 'type': 'dict' }, } @app.route('/') def index(): return Response(response='Hello world!', status=response_status) with pytest.raises(UnexpectedStatus) as e: freeze(app, config) assert e.value.status[:3] == f'{response_status}'
def test_func_to_files(tmp_path): builddir = tmp_path / 'build' config = { **freeze_config, 'output': {'type': 'dir', 'dir': builddir}, } freeze(app, config) assert (builddir / 'index.html').exists() assert (builddir / '.nojekyll').exists() assert (builddir / 'CNAME').exists() assert (builddir / 'smile.png').exists() assert (builddir / 'bin_range.dat').exists() assert (builddir / 'smile2.png').exists()
def test_page_failed_hook(): records = [] expected_total = 3 def record_frozen(task_info): check_task_counts(task_info.freeze_info, expected_total) records.append(( 'frozen', task_info.path, task_info.freeze_info.total_task_count, task_info.freeze_info.done_task_count, task_info.freeze_info.failed_task_count, )) assert task_info.exception == None def record_fail(task_info): check_task_counts(task_info.freeze_info, expected_total) records.append(( 'failed', task_info.path, task_info.freeze_info.total_task_count, task_info.freeze_info.done_task_count, task_info.freeze_info.failed_task_count, )) assert isinstance(task_info.exception, UnexpectedStatus) with context_for_test('app_2_broken_links') as module: config = { 'output': { 'type': 'dict' }, 'prefix': 'http://example.com/', 'hooks': { 'page_frozen': [record_frozen], 'page_failed': [record_fail], }, } with pytest.raises(MultiError): freeze(module.app, config) assert records == [ ('frozen', 'index.html', 3, 1, 0), ('failed', 'nowhere', 3, 2, 1), ('failed', 'also_nowhere', 3, 3, 2), ]