import json import pathlib import tempfile from datetime import datetime, timedelta from http import HTTPStatus import mock import pytest from PIL import Image from modules.archive import lib from modules.archive.lib import get_or_create_domain, get_new_archive_files, delete_archives, do_archive, get_domains from modules.archive.models import Archive, Domain from wrolpi.db import get_db_session from wrolpi.files import lib as files_lib from wrolpi.files.models import FileGroup from wrolpi.root_api import CustomJSONEncoder from wrolpi.test.common import skip_circleci def make_fake_request_archive(readability=True, screenshot=True, title=True): async def fake_request_archive(_): with tempfile.NamedTemporaryFile(suffix='.png') as fh: Image.new('RGB', (1, 1), color='grey').save(fh) fh.seek(0) image = fh.read() singlefile = '
''' make_files_structure({ # These are all for an individual Archive. 'archive/example.com/2021-10-05-16-20-10_NA.html': singlefile_contents, 'archive/example.com/2021-10-05-16-20-10_NA.png': None, 'archive/example.com/2021-10-05-16-20-10_NA.readability.txt': 'article text contents', 'archive/example.com/2021-10-05-16-20-10_NA.readability.json': '{"url": "https://example.com"}', 'archive/example.com/2021-10-05-16-20-10_NA.readability.html': '', # These should log an error because they are missing the singlefile path. 'archive/example.com/2021-10-05 16:20:10.readability.html': '', 'archive/example.com/2021-10-05 16:20:10.readability.json': None, # Bad files should also be ignored. 'archive/example.com/random file': 'hello', 'archive/example.com/2021-10-05 16:20:10 no extension': None, }) # The single archive is found. test_client.post('/api/files/refresh') assert test_session.query(Archive).count() == 1 # Cause a re-index of the archive. test_session.query(FileGroup).filter_by( primary_path=str(test_directory / 'archive/example.com/2021-10-05-16-20-10_NA.html')).one().indexed = False test_session.commit() # Running the refresh does not result in a new archive. test_client.post('/api/files/refresh') assert test_session.query(Archive).count() == 1 # Archives file format was changed, lets check the new formats are found. make_files_structure({ 'archive/example.com/2021-10-05-16-20-11_The Title.html': '', 'archive/example.com/2021-10-05-16-20-11_The Title.readability.json': '{"url": "https://example.com"}', }) test_client.post('/api/files/refresh') # The old formatted archive above is renamed. assert (test_directory / 'archive/example.com/2021-10-05-16-20-10_NA.html').is_file() assert test_session.query(Archive).count() == 2 content = json.dumps({'search_str': 'text', 'model': 'archive'}) request, response = test_client.post('/api/files/search', content=content) assert response.status_code == HTTPStatus.OK assert response.json['file_groups'], 'No files matched "text"' assert response.json['file_groups'][0]['model'] == 'archive', 'Returned file was not an archive' assert response.json['file_groups'][0]['data'].get('readability_path') == \ 'archive/example.com/2021-10-05-16-20-10_NA.readability.html', \ 'Could not find readability html file' assert response.json['file_groups'][0]['data'].get('readability_txt_path') == \ 'archive/example.com/2021-10-05-16-20-10_NA.readability.txt', \ 'Could not find readability text file containing "text"' assert response.json['file_groups'][0]['data']['archive_datetime'], 'Archive has no datetime' @pytest.mark.asyncio async def test_refresh_archives_index(test_session, test_directory, test_client, make_files_structure): """Archives are indexed using ArchiveIndexer and archive_modeler.""" # The start of a typical singlefile html file. singlefile_contents = ''' ''' singlefile, *_ = make_files_structure({ 'archive/example.com/2021-10-05-16-20-10_NA.html': singlefile_contents, 'archive/example.com/2021-10-05-16-20-10_NA.png': None, 'archive/example.com/2021-10-05-16-20-10_NA.readability.txt': 'article text contents', 'archive/example.com/2021-10-05-16-20-10_NA.readability.json': '{"url": "https://example.com", "title": "the title"}', 'archive/example.com/2021-10-05-16-20-10_NA.readability.html': '', }) await files_lib.refresh_files() archive: Archive = test_session.query(Archive).one() assert archive.singlefile_path == singlefile assert archive.file_group.a_text == 'the title' assert archive.file_group.d_text == 'article text contents' assert archive.file_group.indexed is True @pytest.mark.asyncio async def test_refresh_archives_deleted_singlefile(test_session, make_files_structure, singlefile_contents_factory): """Removing a Singlefile file from a FileGroup makes that group no longer an Archive.""" singlefile, readability = make_files_structure({ '2022-09-04-16-20-11_The Title.html': singlefile_contents_factory(), '2022-09-04-16-20-11_The Title.readability.json': '{"url": "https://example.com"}', }) await files_lib.refresh_files() assert test_session.query(FileGroup).one().model == 'archive' assert test_session.query(Archive).count() == 1 # Remove singlefile, FileGroup is no longer an Archive. singlefile.unlink() await files_lib.refresh_files() assert test_session.query(FileGroup).one().model is None assert test_session.query(Archive).count() == 0 @pytest.mark.parametrize( 'name,expected', [ ('foo', False), ('2000-01-01-00-00-00_Some NA.html', True), ('2000-01-01-00-00-00_Some NA.readability.html', False), ('2000-01-01-00-00-00_ジにてこちら.html', True), ('2000-01-01-00-00-00_Some Title.readability.txt', False), ('foo.txt', False), ('foo.html', True), ] ) def test_is_singlefile_file(name, expected, make_files_structure, singlefile_contents_factory): """A Singlefile may not be created by WROLPi, an HTML file created by Singlefile can also be an Archive.""" path, = make_files_structure({name: singlefile_contents_factory()}) assert lib.is_singlefile_file(path) == expected def test_archive_files(test_directory): af = lib.ArchiveFiles( singlefile=test_directory / 'archive/singlefile.html', ) assert repr(af) == \ "