@ -1,8 +1,6 @@
""" The local owner imports synthetic files without sending source rows to the UI. """
""" The local owner imports synthetic files without sending source rows to the UI. """
from pathlib import Path
from pathlib import Path
import random
import string
import pytest
import pytest
from django . core import signing
from django . core import signing
@ -108,7 +106,7 @@ def test_mapping_prepares_then_explicit_commit_creates_with_local_human_actor(cl
payload = signing . loads ( prepared . context [ " prepared_token " ] , salt = " guided-imports-prepared " )
payload = signing . loads ( prepared . context [ " prepared_token " ] , salt = " guided-imports-prepared " )
assert set ( payload [ 0 ] ) == { " name " , " document_hash " , " profile " , " sections " , " batch_id " , " revision " }
assert set ( payload [ 0 ] ) == { " name " , " document_hash " , " profile " , " sections " , " batch_id " , " revision " }
response = commit ( client , prepared )
response = commit ( client , prepared )
assert response . redirect_chain == [ ( reverse ( " imports-index " ) , 302 ) ]
assert not response . redirect_chain
assert response . context [ " files " ] [ 0 ] [ " state " ] == " COMMITTED "
assert response . context [ " files " ] [ 0 ] [ " state " ] == " COMMITTED "
assert response . context [ " files " ] [ 0 ] [ " counts " ] [ " created " ] == 2
assert response . context [ " files " ] [ 0 ] [ " counts " ] [ " created " ] == 2
assert Transaction . objects . count ( ) == SourceRecord . objects . count ( ) == 2
assert Transaction . objects . count ( ) == SourceRecord . objects . count ( ) == 2
@ -136,42 +134,10 @@ def test_scan_and_prepared_form_metadata_do_not_depend_on_session_payload(client
assert " batch_id " not in str ( dict ( client . session ) )
assert " batch_id " not in str ( dict ( client . session ) )
def test_oversized_scan_has_safe_result_and_leaves_every_file_untouched ( client , source , bank ) :
for index in range ( 20 ) :
( source / f " synthetic- { index } .csv " ) . write_bytes ( DATA )
response = scan ( client )
assert " SCAN_TOO_LARGE " in response . content . decode ( )
assert not response . context . get ( " scan_token " )
assert not ImportBatch . objects . exists ( )
assert len ( list ( source . iterdir ( ) ) ) == 21
if " sessionid " in client . cookies :
assert len ( client . cookies [ " sessionid " ] . value ) < 4096
def test_long_filename_summaries_fit_browser_cookie_and_do_not_accumulate_across_tabs ( client , source , bank ) :
( source / " bank.csv " ) . unlink ( )
names = random . Random ( 41 )
filenames = [ " " . join ( names . choices ( string . ascii_letters + string . digits , k = 250 ) ) + " .csv "
for _ in range ( 10 ) ]
for filename in filenames :
( source / filename ) . write_bytes ( DATA )
scanned = scan ( client )
fields = { f " file- { index } -section_0 " : str ( bank . pk ) for index in range ( 10 ) }
prepared = prepare ( client , scanned , bank , * * fields )
# A second tab scans other files before the first tab finishes its old form.
for filename in filenames :
( source / filename ) . unlink ( )
( source / filename . replace ( " .csv " , " .ofx " ) ) . write_text ( " Synthetic unsupported " )
scan ( client )
response = commit ( client , prepared )
assert len ( response . context [ " files " ] ) == 10
assert all ( row [ " code " ] == " UNREADABLE_FILE " for row in response . context [ " files " ] )
assert len ( client . cookies [ " sessionid " ] . OutputString ( ) . encode ( ) ) < 4096
assert not Transaction . objects . exists ( )
def test_exact_reimport_reports_skips_without_new_transactions ( client , source , bank ) :
def test_exact_reimport_reports_skips_without_new_transactions ( client , source , bank ) :
commit ( client , prepare ( client , scan ( client ) , bank ) )
commit ( client , prepare ( client , scan ( client ) , bank ) )
( source / " bank.csv " ) . write_bytes ( DATA )
prepared = prepare ( client , scan ( client ) , bank )
prepared = prepare ( client , scan ( client ) , bank )
assert prepared . context [ " files " ] [ 0 ] [ " state " ] == " ALREADY_IMPORTED "
assert prepared . context [ " files " ] [ 0 ] [ " state " ] == " ALREADY_IMPORTED "
result = commit ( client , prepared )
result = commit ( client , prepared )
@ -214,9 +180,9 @@ def test_review_exception_does_not_prevent_another_file_commit(client, source, b
( source / " ambiguous.csv " ) . write_bytes ( DATA + DATA . splitlines ( keepends = True ) [ 1 ] )
( source / " ambiguous.csv " ) . write_bytes ( DATA + DATA . splitlines ( keepends = True ) [ 1 ] )
prepared = prepare ( client , scan ( client ) , bank , * * { " file-1-section_0 " : str ( bank . pk ) } )
prepared = prepare ( client , scan ( client ) , bank , * * { " file-1-section_0 " : str ( bank . pk ) } )
assert [ row [ " state " ] for row in prepared . context [ " files " ] ] == [ " NEEDS_ATTENTION " , " READY " ]
assert [ row [ " state " ] for row in prepared . context [ " files " ] ] == [ " NEEDS_ATTENTION " , " READY " ]
assert prepared . context [ " files " ] [ 0 ] [ " code " ] == " REVIEW_REQUIRED "
result = commit ( client , prepared )
result = commit ( client , prepared )
assert [ row [ " state " ] for row in result . context [ " files " ] ] == [ " NEEDS_ATTENTION " , " COMMITTED " ]
assert [ row [ " state " ] for row in result . context [ " files " ] ] == [ " COMMITTED " ]
assert result . context [ " files " ] [ 0 ] [ " code " ] == " REVIEW_REQUIRED "
assert Transaction . objects . count ( ) == 2
assert Transaction . objects . count ( ) == 2
assert not ImportBatch . objects . filter ( state = " PREVIEW " ) . exists ( )
assert not ImportBatch . objects . filter ( state = " PREVIEW " ) . exists ( )
@ -241,13 +207,18 @@ def test_unexpected_per_file_error_never_exposes_source_or_blocks_next_file(clie
monkeypatch . setattr ( views . guided_imports , service_name , fail_one_file )
monkeypatch . setattr ( views . guided_imports , service_name , fail_one_file )
if stage == " prepare " :
if stage == " prepare " :
prepared = prepare ( client , scanned , bank , * * { " file-1-section_0 " : str ( bank . pk ) } )
prepared = prepare ( client , scanned , bank , * * { " file-1-section_0 " : str ( bank . pk ) } )
assert prepared . context [ " files " ] [ 0 ] [ " code " ] == " IMPORT_FAILED "
result = commit ( client , prepared )
result = commit ( client , prepared )
if stage == " commit " :
assert [ row [ " state " ] for row in result . context [ " files " ] ] == [ " NEEDS_ATTENTION " , " COMMITTED " ]
assert [ row [ " state " ] for row in result . context [ " files " ] ] == [ " NEEDS_ATTENTION " , " COMMITTED " ]
assert result . context [ " files " ] [ 0 ] [ " code " ] == " IMPORT_FAILED "
assert result . context [ " files " ] [ 0 ] [ " code " ] == " IMPORT_FAILED "
assert " Synthetic private source text " not in result . content . decode ( )
else :
assert " 1234.56 " not in result . content . decode ( )
assert [ row [ " state " ] for row in result . context [ " files " ] ] == [ " COMMITTED " ]
response = prepared if stage == " prepare " else result
assert " Synthetic private source text " not in response . content . decode ( )
assert " 1234.56 " not in response . content . decode ( )
assert Transaction . objects . count ( ) == 2
assert Transaction . objects . count ( ) == 2
assert_private ( result , source )
assert_private ( response , source )
@pytest . mark . parametrize ( " stage " , [ " prepare " , " commit " ] )
@pytest . mark . parametrize ( " stage " , [ " prepare " , " commit " ] )
@ -281,7 +252,10 @@ def test_signed_workflow_rejects_altered_expired_and_wrong_stage_tokens(
def test_changed_source_rejects_previously_signed_metadata ( client , source , bank , stage ) :
def test_changed_source_rejects_previously_signed_metadata ( client , source , bank , stage ) :
scanned = scan ( client )
scanned = scan ( client )
prepared = prepare ( client , scanned , bank ) if stage == " commit " else None
prepared = prepare ( client , scanned , bank ) if stage == " commit " else None
( source / " bank.csv " ) . write_bytes ( DATA . replace ( b " 12.34 " , b " 22.34 " ) )
changed = source / " bank.csv "
if stage == " commit " :
changed = source / " staging " / " bank.csv "
changed . write_bytes ( DATA . replace ( b " 12.34 " , b " 22.34 " ) )
response = commit ( client , prepared ) if prepared else prepare ( client , scanned , bank )
response = commit ( client , prepared ) if prepared else prepare ( client , scanned , bank )
assert response . context [ " files " ] [ 0 ] [ " code " ] == " FILE_CHANGED "
assert response . context [ " files " ] [ 0 ] [ " code " ] == " FILE_CHANGED "
assert not Transaction . objects . exists ( )
assert not Transaction . objects . exists ( )