1+ from types import SimpleNamespace
2+ from pathlib import Path
13import logging
24import tempfile
3- from pathlib import Path
4- from types import SimpleNamespace
5-
65import pytest
76
7+ from pygeodiff import GeoDiff
8+ from mergin .client_push import push_project_finalize , UploadJob
9+ from mergin .common import ClientError
810
911@pytest .mark .parametrize ("status_code" , [502 , 504 ])
10- def test_push_finalize_logs_on_5xx_real_diff (caplog , status_code ):
11- # geodiff is required – if missing, skip the test gracefully
12- pytest .importorskip ("pygeodiff" )
13- from pygeodiff import GeoDiff
14-
15- from mergin .client_push import push_project_finalize
16- from mergin .common import ClientError
17-
18- # --- exact paths according to your repo structure ---
12+ def test_push_finalize_logs_on_5xx_real_diff (caplog , status_code , tmp_path ):
13+ # --- data ---
1914 data_dir = Path (__file__ ).resolve ().parent / "test_data"
2015 base = data_dir / "base.gpkg"
2116 modified = data_dir / "inserted_1_A.gpkg"
22-
23- # if something is missing, print the directory contents and FAIL
24- if not base .exists () or not modified .exists ():
25- print ("[DEBUG] data_dir:" , data_dir )
26- print ("[DEBUG] data_dir.exists():" , data_dir .exists ())
27- if data_dir .exists ():
28- print ("[DEBUG] contents:" , [p .name for p in sorted (data_dir .iterdir ())])
29- pytest .fail (f"Expected files are not available: base={ base .exists ()} inserted_1_A={ modified .exists ()} " )
30-
31- # --- create a real .diff (changeset) ---
32- with tempfile .TemporaryDirectory (prefix = "geodiff-" ) as tmpdir :
33- tmpdir = Path (tmpdir )
34- diff_path = tmpdir / "base_to_inserted_1_A.diff"
35-
36- gd = GeoDiff ()
37- gd .create_changeset (str (base ), str (modified ), str (diff_path ))
38- diff_size = diff_path .stat ().st_size
39- file_size = modified .stat ().st_size
40-
41- # --- logger captured by caplog ---
42- logger = logging .getLogger ("mergin_test" )
43- logger .setLevel (logging .DEBUG )
44- logger .propagate = True
45-
46- # --- fake mc.post: /finish -> 5xx, /cancel -> ok ---
47- def fake_post (url , * args , ** kwargs ):
48- if "/finish/" in url :
49- err = ClientError ("Gateway error" )
50- setattr (err , "http_error" , status_code )
51- raise err
52- if "/cancel/" in url :
17+ assert base .exists ()
18+ assert modified .exists ()
19+
20+ diff_path = tmp_path / "base_to_inserted_1_A.diff"
21+ GeoDiff ().create_changeset (str (base ), str (modified ), str (diff_path ))
22+ diff_size = diff_path .stat ().st_size
23+ file_size = modified .stat ().st_size
24+
25+ # --- logger ---
26+ logger = logging .getLogger ("mergin_test" )
27+ logger .setLevel (logging .DEBUG )
28+ logger .propagate = True
29+ caplog .set_level (logging .ERROR , logger = "mergin_test" )
30+
31+ # --- fake MP/MC len tam, kde treba ---
32+ class MP :
33+ def __init__ (self , log ): self .log = log
34+ def update_metadata (self , * a , ** k ): pass
35+ def apply_push_changes (self , * a , ** k ): pass
36+ def fpath_meta (self , rel ): return str (diff_path ) if rel == diff_path .name else rel
37+
38+ tx = "tx-1"
39+
40+ class MC :
41+ def post (self , url , * args , ** kwargs ):
42+ if url .endswith (f"/v1/project/push/finish/{ tx } " ):
43+ err = ClientError ("Gateway error" ); setattr (err , "http_error" , status_code ); raise err
44+ if url .endswith (f"/v1/project/push/cancel/{ tx } " ):
5345 return SimpleNamespace (msg = "cancelled" )
5446 return SimpleNamespace (msg = "ok" )
5547
56- # --- fake future/executor (no exceptions) ---
57- fake_future = SimpleNamespace (done = lambda : True , exception = lambda : None , running = lambda : False )
58- fake_executor = SimpleNamespace (shutdown = lambda wait = True : None )
59-
60- # mp.fpath_meta should resolve the relative diff path to an absolute path
61- def fpath_meta (rel ):
62- return str (diff_path ) if rel == diff_path .name else rel
63-
64- # --- minimal job – only what finalize() uses ---
65- job = SimpleNamespace (
66- executor = fake_executor ,
67- futures = [fake_future ],
68- transferred_size = 1234 ,
69- total_size = 1234 ,
70- upload_queue_items = [1 ],
71- transaction_id = "tx-1" ,
72- mp = SimpleNamespace (
73- log = logger ,
74- update_metadata = lambda * a , ** k : None ,
75- apply_push_changes = lambda * a , ** k : None ,
76- fpath_meta = fpath_meta ,
77- ),
78- mc = SimpleNamespace (post = fake_post ),
79- changes = {
80- "added" : [],
81- "updated" : [
82- {
83- "path" : modified .name ,
84- "size" : file_size ,
85- "diff" : {"path" : diff_path .name , "size" : diff_size },
86- "chunks" : [1 ],
87- }
88- ],
89- "removed" : [],
90- },
91- tmp_dir = SimpleNamespace (cleanup = lambda : None ),
92- )
93-
94- # capture ERROR-level logs from "mergin_test"
95- caplog .set_level (logging .ERROR , logger = "mergin_test" )
96-
97- # --- call production logic (expecting 5xx) ---
98- with pytest .raises (ClientError ):
99- push_project_finalize (job )
100-
101- text = caplog .text
102-
103- assert f"Push failed with HTTP error { status_code } " in text
104- assert "Upload details:" in text
105- assert "Files:" in text
106- assert modified .name in text
107- assert f"size={ file_size } " in text
108- assert f"diff_size={ diff_size } " in text
109- # helper may compute changes, or log changes=n/a – accept both
110- assert ("changes=" in text ) or ("changes=n/a" in text )
48+ tmp_dir = tempfile .TemporaryDirectory (prefix = "python-api-client-test-" )
49+
50+ # --- real UploadJob objekt ---
51+ job = UploadJob (
52+ project_path = "u/p" ,
53+ changes = {
54+ "added" : [],
55+ "updated" : [{
56+ "path" : modified .name ,
57+ "size" : file_size ,
58+ "diff" : {"path" : diff_path .name , "size" : diff_size },
59+ "chunks" : [1 ],
60+ }],
61+ "removed" : [],
62+ },
63+ transaction_id = tx ,
64+ mp = MP (logger ),
65+ mc = MC (),
66+ tmp_dir = tmp_dir ,
67+ )
68+
69+ # nastav to, čo finalize() očakáva
70+ job .total_size = 1234
71+ job .transferred_size = 1234
72+ job .upload_queue_items = [1 ] # len pre log „Upload details“
73+ job .executor = SimpleNamespace (shutdown = lambda wait = True : None )
74+ job .futures = [SimpleNamespace (done = lambda : True , exception = lambda : None , running = lambda : False )]
75+ job .server_resp = {"version" : "n/a" } # aj tak sa 5xx vyhodí skôr
76+
77+ with pytest .raises (ClientError ):
78+ push_project_finalize (job )
79+
80+ text = caplog .text
81+ assert f"Push failed with HTTP error { status_code } " in text
82+ assert "Upload details:" in text
83+ assert "Files:" in text
84+ assert modified .name in text
85+ assert f"size={ file_size } " in text
86+ assert f"diff_size={ diff_size } " in text
87+ assert ("changes=" in text ) or ("changes=n/a" in text )
0 commit comments