......................F.F................F.............................. [ 9%] .............F.......................................................... [ 18%] ...............................F........................................ [ 27%] ..F..................................................................... [ 37%] ........................................................................ [ 46%] ........................................................................ [ 55%] ........................................................................ [ 65%] ........................................................................ [ 74%] ........................................................................ [ 83%] ........................................................................ [ 93%] .................................................... [100%] =================================== FAILURES =================================== _ test_return_keeps_old_note_bindings_receipts_and_retired_repeat_rule_closed __ app = def test_return_keeps_old_note_bindings_receipts_and_retired_repeat_rule_closed(app): c=client(app);j,people,_=setup(c,app);b=job(c);a=access(c,j);op=str(uuid.uuid4()) assert action(c,j,op,{'binding':a['binding']},'cancel').status_code==200 d,_,_=definition(c,app,j,people[0]);d=recurrence_action(c,d,{'action':'state','state':'retired','reason':'Close invented repeat definition before correction'}) > move(c,j,b);middle=access(c,j,a['binding']['device_id']);move(c,j,j);returned=access(c,j,a['binding']['device_id']) ^^^^^^^^^^^ tests/test_customer_moves.py:75: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ c = j = {'id': '631e415a8ccbe20da76b6c509d6cc8fc', 'customer_id': '8282a759227ec00113f8d90064fef877'} target = {'id': '9c97fbaed9f0c063435c2b0c64261183', 'customer_id': 'aa0856b704650f03819e7ffd7c484537'} ids = None, key = None def move(c,j,target,ids=None,key=None): current=c.get('/api/jobs/'+j['id']).json() p=preview(c,selection(c,current,target,kind='jobs',job_ids=ids or [j['id']])) > assert p['can_confirm'],p['errors'] E AssertionError: ['631e415a8ccbe20da76b6c509d6cc8fc: Work currently in progress'] E assert False tests/test_customer_moves.py:24: AssertionError ____ test_stale_or_invalid_batch_changes_nothing_and_retains_preview[note] _____ app = , change = 'note' @pytest.mark.parametrize('change',['new_job','note','customer','portal','active_work','one_wrong_job']) def test_stale_or_invalid_batch_changes_nothing_and_retains_preview(app,change): c=client(app);a=job(c);b=job(c);second=work(c,customer(c,a['customer_id'])).json() move(c,a,b,ids=[a['id'],second['id']]);current=c.get('/api/jobs/'+a['id']).json() p=preview(c,selection(c,current,a,kind='jobs',job_ids=[a['id'],second['id']])) if change=='new_job':work(c,customer(c,b['customer_id'])) elif change=='note': # A new association makes the saved dependency inventory stale. from test_device_notes import note > op,body,_=note(c,current);assert action(c,current,op,body).status_code==200 ^^^^^^^^^^^^^^^ tests/test_customer_moves.py:96: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tests/test_device_notes.py:22: in note a=access(c,j) ^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ c = j = {'id': 'ffe25aab5c257d7943ca5f4bea126f56', 'customer_id': 'd97b3b0c1785be82de409787d65f333d', 'service': 'Fit shelves', 'message': 'Two shelves in a demonstration room.', ...} device = None def access(c,j,device=None): r=write(c,base(j)+'/note-access',{'device_id':device or str(uuid.uuid4())}) > assert r.status_code==200,r.text E AssertionError: {"detail":"Field work is available for accepted, scheduled or in-progress jobs."} E assert 404 == 200 E + where 404 = .status_code tests/test_device_notes.py:16: AssertionError _ test_return_does_not_reopen_old_followup_or_permission_when_explicitly_unexcluded _ app = def test_return_does_not_reopen_old_followup_or_permission_when_explicitly_unexcluded(app): c=client(app);a=job(c);b=job(c) edit(c,a,next_action='Call about invented original shelf size');old=next(i for i in items(c,'next_action') if i['job_id']==a['id']);decide(c,old,'dismiss') move(c,a,b);move(c,a,a);edit(c,a,followup_excluded=False) queue=items(c);assert next(i for i in queue if i['id']==old['id'])['eligible'] is False assert not any(i['eligible'] and i['job_id']==a['id'] for i in queue) edit(c,a,next_action='A new individually reviewed step after correction');fresh=next(i for i in items(c) if i['eligible'] and i['job_id']==a['id']);assert fresh['id']!=old['id'] decide(c,fresh,'prepare');move(c,a,b);move(c,a,a);edit(c,a,followup_excluded=False) > assert all(not i['eligible'] for i in items(c) if i['job_id']==a['id']) E assert False E + where False = all(. at 0x10dec6ce0>) tests/test_customer_moves.py:196: AssertionError ________ test_transferred_source_draft_requires_explicit_renewed_review ________ app = tmp_path = PosixPath('/private/var/folders/kn/1_8nby0s6m7g8b2k6grh24wm0000gn/T/pytest-of-sandyrestall/pytest-62/test_transferred_source_draft_0') def test_transferred_source_draft_requires_explicit_renewed_review(app,tmp_path): from test_fieldwork import docact,doc as source_doc,suggested c=client(app);j,people,clock,tid=captured(c,app);d=draft(c,j).json();target=job(c) > plan=preview(c,selection(c,j,target));assert confirm(c,plan).status_code==200 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E AssertionError: assert 409 == 200 E + where 409 = .status_code E + where = confirm(, {'selection': {'kind': 'merge', 'source_id': 'ac872bb60ee04b0da55e8a5807523144', 'target_id': '5a0f24d585a7b70010ea945...ields': {'name': 'Alex Demo', 'email': 'alex@customer.example', 'phone': '000000', 'address': '1 Example Street'}, ...}) tests/test_customer_reviews.py:202: AssertionError _______ test_v19_format12_archive_remains_readable_without_schema_change _______ tmp_path = PosixPath('/private/var/folders/kn/1_8nby0s6m7g8b2k6grh24wm0000gn/T/pytest-of-sandyrestall/pytest-62/test_v19_format12_archive_rema0') def test_v19_format12_archive_remains_readable_without_schema_change(tmp_path): app = create_app(tmp_path/'old-fixture') assert submit(app, payload(photos=[photo()])).status_code == 200 original = recovery.backup(app.state.store.root, tmp_path/'current.zip') old = tmp_path/'old-format12.zip' def old_metadata(files, meta): meta['prototype_version'] = '0.19.0' sha = rewrite_archive(original['archive'], old, old_metadata) > assert recovery.verify(old, sha)['valid'] ^^^^^^^^^^^^^^^^^^^^^^^^^ tests/test_enquiry_format_recovery.py:96: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend/recovery.py:436: in verify meta,summaries=unpack_validated(archive,sha256,Path(temporary)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ backend/recovery.py:402: in unpack_validated check_metadata(meta) backend/recovery.py:191: in check_metadata require(meta['format']==FORMAT and type(meta['version']) is int and (meta['version'],meta['prototype_version']) in ((1,'0.4.0'),(2,'0.5.0'),(3,'0.6.0'),(4,'0.7.0'),(5,'0.8.0'),(6,'0.9.0'),(7,'0.10.0'),(8,'0.11.0'),(9,'0.12.0'),(10,'0.13.0'),(11,'0.14.0'),(12,'0.19.0'),(12,'0.20.0'),(13,'0.24.0')),'Unsupported recovery format/version.') _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ condition = False, message = 'Unsupported recovery format/version.' def require(condition,message): > if not condition:raise RecoveryError(message) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E backend.recovery.RecoveryError: Unsupported recovery format/version. backend/recovery.py:43: RecoveryError __ test_format_eleven_migrates_without_fabricated_intake_or_photo_permission ___ tmp_path = PosixPath('/private/var/folders/kn/1_8nby0s6m7g8b2k6grh24wm0000gn/T/pytest-of-sandyrestall/pytest-62/test_format_eleven_migrates_wi0') def test_format_eleven_migrates_without_fabricated_intake_or_photo_permission(tmp_path): root=tmp_path/'old';application=create_app(root);owner=client(application);j=job(owner);p=reviewed(owner,j) before=table_rows(root,'oak');original=tmp_path/'original.zip';recovery.backup(root,original) def make_old(files,meta): meta.update(version=11,prototype_version='0.14.0') for name,data in list(files.items()): if not name.startswith('businesses/') or not name.endswith('.sqlite3'):continue path=tmp_path/(name.split('/')[1]+'.sqlite3');path.write_bytes(data) with sqlite3.connect(path) as db: for table in TABLES:db.execute('DROP TABLE '+table) files[name]=path.read_bytes();meta['files'][name].update(bytes=len(files[name]),sha256=hashlib.sha256(files[name]).hexdigest()) > old=tmp_path/'format11.zip';sha=rewrite_archive(original,old,make_old);assert recovery.verify(old,sha)['valid'] ^^^^^^^^^^^^^^^^^^^^^^^^ tests/test_enquiry_recovery.py:114: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend/recovery.py:436: in verify meta,summaries=unpack_validated(archive,sha256,Path(temporary)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ backend/recovery.py:422: in unpack_validated summary=tenant_summary(stage/prefix/'business.sqlite3',bid,meta['dataset_id'],sanitised=True,format_version=meta['version']) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ backend/recovery.py:128: in tenant_summary check_db(db,expected_schemas(format_version)[1]) backend/recovery.py:117: in check_db require(schema(db)==expected,'Database schema is unsupported or has unexpected tables, views, indexes or triggers.') _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ condition = False message = 'Database schema is unsupported or has unexpected tables, views, indexes or triggers.' def require(condition,message): > if not condition:raise RecoveryError(message) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E backend.recovery.RecoveryError: Database schema is unsupported or has unexpected tables, views, indexes or triggers. backend/recovery.py:43: RecoveryError =============================== warnings summary =============================== ../shs-v20-venv/lib/python3.12/site-packages/fastapi/testclient.py:1 /private/tmp/shs-v20-venv/lib/python3.12/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. from starlette.testclient import TestClient as TestClient # noqa tests/test_photos.py::test_animation_and_pixel_limit /private/tmp/shs-v20-venv/lib/python3.12/site-packages/PIL/Image.py:3593: DecompressionBombWarning: Image size (12004000 pixels) exceeds limit of 12000000 pixels, could be decompression bomb DOS attack. warnings.warn( -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html =========================== short test summary info ============================ FAILED tests/test_customer_moves.py::test_return_keeps_old_note_bindings_receipts_and_retired_repeat_rule_closed FAILED tests/test_customer_moves.py::test_stale_or_invalid_batch_changes_nothing_and_retains_preview[note] FAILED tests/test_customer_moves.py::test_return_does_not_reopen_old_followup_or_permission_when_explicitly_unexcluded FAILED tests/test_customer_reviews.py::test_transferred_source_draft_requires_explicit_renewed_review FAILED tests/test_enquiry_format_recovery.py::test_v19_format12_archive_remains_readable_without_schema_change FAILED tests/test_enquiry_recovery.py::test_format_eleven_migrates_without_fabricated_intake_or_photo_permission 6 failed, 766 passed, 2 warnings in 225.19s (0:03:45)