From 1e4ce148c54a16c96e273090d5ff03b2a4789469 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 10 Dec 2021 19:14:26 +0100 Subject: improve IndexedDB use --- test/unit/test_indexeddb.py | 136 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/unit/test_indexeddb.py b/test/unit/test_indexeddb.py index e5e1626..f1322fb 100644 --- a/test/unit/test_indexeddb.py +++ b/test/unit/test_indexeddb.py @@ -28,8 +28,8 @@ def indexeddb_code(): def sample_file(contents): return { - 'sha256': sha256(contents.encode()).digest().hex(), - contents: contents + 'hash_key': f'sha256-{sha256(contents.encode()).digest().hex()}', + 'contents': contents } sample_files = { @@ -37,16 +37,17 @@ sample_files = { 'LICENSES/somelicense.txt': sample_file('Permission is granted...'), 'hello.js': sample_file('console.log("hello!");\n'), 'bye.js': sample_file('console.log("bye!");\n'), + 'combined.js': sample_file('console.log("hello!\\nbye!");\n'), 'README.md': sample_file('# Python Frobnicator\n...') } -sample_files_sha256 = \ - dict([[file['sha256'], file] for file in sample_files.values()]) +sample_files_by_hash = dict([[file['hash_key'], file['contents']] + for file in sample_files.values()]) def file_ref(file_name): - return {'file': file_name, 'sha256': sample_files[file_name]['sha256']} + return {'file': file_name, 'hash_key': sample_files[file_name]['hash_key']} -def test_save_item(execute_in_page, indexeddb_code): +def test_save_remove_item(execute_in_page, indexeddb_code): """ indexeddb.js facilitates operating on Haketilo's internal database. Verify database operations work properly. @@ -79,11 +80,8 @@ def test_save_item(execute_in_page, indexeddb_code): # Facilitate retrieving all IndexedDB contents. execute_in_page( ''' - async function get_database_contents(promise=Promise.resolve()) + async function get_database_contents() { - if (promise) - await promise; - const db = await haketilodb.get(); const transaction = db.transaction(db.objectStoreNames); @@ -110,25 +108,129 @@ def test_save_item(execute_in_page, indexeddb_code): 'type': 'resource', 'identifier': 'helloapple', 'scripts': [file_ref('hello.js'), file_ref('bye.js')], - 'type': 'resource' } next(iter(sample_item['source_copyright']))['ugly_extra_property'] = True database_contents = execute_in_page( '''{ - const prom = haketilodb.get().then(db => save_item(...arguments, db)); - returnval(get_database_contents(prom)); + const promise = start_items_transaction(["resources"], arguments[1]) + .then(ctx => save_item(arguments[0], ctx).then(() => ctx)) + .then(finalize_items_transaction) + .then(get_database_contents); + returnval(promise); }''', - sample_item, sample_files_sha256) + sample_item, sample_files_by_hash) assert len(database_contents['files']) == 4 - assert all([sample_files_sha256[file['sha256']] == file['contents'] + assert all([sample_files_by_hash[file['hash_key']] == file['contents'] for file in database_contents['files']]) assert all([len(file) == 2 for file in database_contents['files']]) assert len(database_contents['file_uses']) == 4 assert all([uses['uses'] == 1 for uses in database_contents['file_uses']]) - assert set([uses['sha256'] for uses in database_contents['file_uses']]) \ - == set([file['sha256'] for file in database_contents['files']]) + assert set([uses['hash_key'] for uses in database_contents['file_uses']]) \ + == set([file['hash_key'] for file in database_contents['files']]) assert database_contents['mappings'] == [] assert database_contents['resources'] == [sample_item] + + # See if trying to add an item without providing all its files ends in an + # exception and aborts the transaction as it should. + sample_item['scripts'].append(file_ref('combined.js')) + incomplete_files = {**sample_files_by_hash} + incomplete_files.pop(sample_files['combined.js']['hash_key']) + print ('incomplete files:', incomplete_files) + print ('sample item:', sample_item) + result = execute_in_page( + '''{ + console.log('sample item', arguments[0]); + const promise = (async () => { + const context = + await start_items_transaction(["resources"], arguments[1]); + try { + await save_item(arguments[0], context); + await finalize_items_transaction(context); + return {}; + } catch(e) { + var exception = e; + } + + return {exception, db_contents: await get_database_contents()}; + })(); + returnval(promise); + }''', + sample_item, incomplete_files) + + assert result + assert 'file not present' in result['exception'] + for key, val in database_contents.items(): + keyfun = lambda item: item.get('hash_key') or item['identifier'] + assert sorted(result['db_contents'][key], key=keyfun) \ + == sorted(val, key=keyfun) + + # See if adding another item that partially uses first's files works OK. + sample_item = { + 'source_copyright': [ + file_ref('report.spdx'), + file_ref('README.md') + ], + 'type': 'mapping', + 'identifier': 'helloapple', + } + database_contents = execute_in_page( + '''{ + const promise = start_items_transaction(["mappings"], arguments[1]) + .then(ctx => save_item(arguments[0], ctx).then(() => ctx)) + .then(finalize_items_transaction) + .then(get_database_contents); + returnval(promise); + }''', + sample_item, sample_files_by_hash) + + names = ['README.md', 'report.spdx', 'LICENSES/somelicense.txt', 'hello.js', + 'bye.js'] + sample_files_list = [sample_files[name] for name in names] + uses_list = [1, 2, 1, 1, 1] + + uses = dict([(uses['hash_key'], uses['uses']) + for uses in database_contents['file_uses']]) + assert uses == dict([(file['hash_key'], nr) + for file, nr in zip(sample_files_list, uses_list)]) + + files = dict([(file['hash_key'], file['contents']) + for file in database_contents['files']]) + assert files == dict([(file['hash_key'], file['contents']) + for file in sample_files_list]) + + assert database_contents['mappings'] == [sample_item] + + # Try removing the items to get an empty database again. + results = [None, None] + for i, item_type in enumerate(['resource', 'mapping']): + results[i] = execute_in_page( + f'''{{ + const remover = remove_{item_type}; + const promise = + start_items_transaction(["{item_type}s"], {{}}) + .then(ctx => remover('helloapple', ctx).then(() => ctx)) + .then(finalize_items_transaction) + .then(get_database_contents); + returnval(promise); + }}''') + + names = ['README.md', 'report.spdx'] + sample_files_list = [sample_files[name] for name in names] + uses_list = [1, 1] + + uses = dict([(uses['hash_key'], uses['uses']) + for uses in results[0]['file_uses']]) + assert uses == dict([(file['hash_key'], 1) for file in sample_files_list]) + + files = dict([(file['hash_key'], file['contents']) + for file in results[0]['files']]) + assert files == dict([(file['hash_key'], file['contents']) + for file in sample_files_list]) + + assert results[0]['resources'] == [] + assert results[0]['mappings'] == [sample_item] + + assert results[1] == dict([(key, []) for key in results[0].keys()]) -- cgit v1.2.3