Skip to content

Commit 99f89ce

Browse files
author
bosd
committed
Improve test coverage for import flows
- Add test_run_import_fail_mode_no_records: Test fail mode when fail file has no records - Add test_run_import_sort_strategy_already_sorted: Test sort strategy with already sorted file - Add test_run_import_invalid_json_type_context: Test handling of non-dict JSON context - Add test_run_import_with_relational_strategy: Test relational import strategies in Pass 2 - Add test_run_import_fails_without_creating_fail_file: Test failure path without fail file creation These new tests improve coverage for edge cases and error handling in the import flows.
1 parent 9a573d6 commit 99f89ce

File tree

1 file changed

+210
-1
lines changed

1 file changed

+210
-1
lines changed

tests/test_importer.py

Lines changed: 210 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,10 @@ def preflight_side_effect(*_args: Any, **kwargs: Any) -> bool:
357357
return True
358358

359359
mock_preflight.side_effect = preflight_side_effect
360-
mock_import_data.return_value = (True, {"total_records": 1, "id_map": {"1": 1}})
360+
mock_import_data.return_value = (
361+
True,
362+
{"total_records": 1, "id_map": {"1": 1}},
363+
)
361364

362365
run_import(
363366
config="dummy.conf",
@@ -380,3 +383,209 @@ def preflight_side_effect(*_args: Any, **kwargs: Any) -> bool:
380383
)
381384
mock_import_data.assert_called_once()
382385
mock_relational_import.assert_not_called()
386+
387+
388+
@patch("odoo_data_flow.importer.import_threaded.import_data")
389+
@patch("odoo_data_flow.importer.Console")
390+
def test_run_import_fail_mode_no_records(
391+
mock_console: MagicMock, mock_import_data: MagicMock, tmp_path: Path
392+
) -> None:
393+
"""Test fail mode when the fail file has no records to retry."""
394+
source_file = tmp_path / "source.csv"
395+
source_file.touch()
396+
fail_file = tmp_path / "res_partner_fail.csv"
397+
fail_file.write_text("id,name\n") # Only a header
398+
399+
run_import(
400+
config="dummy.conf",
401+
filename=str(source_file),
402+
model="res.partner",
403+
fail=True,
404+
deferred_fields=None,
405+
unique_id_field=None,
406+
no_preflight_checks=True,
407+
headless=True,
408+
worker=1,
409+
batch_size=100,
410+
skip=0,
411+
separator=";",
412+
ignore=None,
413+
context={},
414+
encoding="utf-8",
415+
o2m=False,
416+
groupby=None,
417+
)
418+
mock_import_data.assert_not_called()
419+
mock_console.return_value.print.assert_called_once()
420+
assert (
421+
"No records to retry"
422+
in mock_console.return_value.print.call_args[0][0].renderable
423+
)
424+
425+
426+
@patch("odoo_data_flow.importer.sort.sort_for_self_referencing")
427+
@patch("odoo_data_flow.importer.import_threaded.import_data")
428+
@patch("odoo_data_flow.importer._run_preflight_checks")
429+
def test_run_import_sort_strategy_already_sorted(
430+
mock_preflight: MagicMock,
431+
mock_import_data: MagicMock,
432+
mock_sort: MagicMock,
433+
tmp_path: Path,
434+
) -> None:
435+
"""Test the sort strategy when the file is already sorted."""
436+
source_file = tmp_path / "source.csv"
437+
source_file.touch()
438+
mock_sort.return_value = True # Indicates file is already sorted
439+
440+
def preflight_side_effect(*args: Any, **kwargs: Any) -> bool:
441+
kwargs["import_plan"]["strategy"] = "sort_and_one_pass_load"
442+
kwargs["import_plan"]["id_column"] = "id"
443+
kwargs["import_plan"]["parent_column"] = "parent_id"
444+
return True
445+
446+
mock_preflight.side_effect = preflight_side_effect
447+
mock_import_data.return_value = (True, {"total_records": 1})
448+
449+
run_import(
450+
config="dummy.conf",
451+
filename=str(source_file),
452+
model="res.partner",
453+
deferred_fields=None,
454+
unique_id_field=None,
455+
no_preflight_checks=False,
456+
headless=True,
457+
worker=1,
458+
batch_size=100,
459+
skip=0,
460+
fail=False,
461+
separator=";",
462+
ignore=None,
463+
context={},
464+
encoding="utf-8",
465+
o2m=False,
466+
groupby=None,
467+
)
468+
mock_sort.assert_called_once()
469+
assert mock_import_data.call_args.kwargs["file_csv"] == str(source_file)
470+
471+
472+
@patch("odoo_data_flow.importer._show_error_panel")
473+
def test_run_import_invalid_json_type_context(
474+
mock_show_error: MagicMock,
475+
) -> None:
476+
"""Test that run_import handles context that is not a JSON dict."""
477+
run_import(
478+
config="dummy.conf",
479+
filename="dummy.csv",
480+
model="res.partner",
481+
context='["not", "a", "dict"]', # Valid JSON, but not a dict
482+
deferred_fields=None,
483+
unique_id_field=None,
484+
no_preflight_checks=True,
485+
headless=True,
486+
worker=1,
487+
batch_size=100,
488+
skip=0,
489+
fail=False,
490+
separator=";",
491+
ignore=None,
492+
encoding="utf-8",
493+
o2m=False,
494+
groupby=None,
495+
)
496+
mock_show_error.assert_called_once()
497+
assert "must be a valid JSON dictionary" in mock_show_error.call_args[0][1]
498+
499+
500+
@patch("odoo_data_flow.importer.cache.save_id_map")
501+
@patch("odoo_data_flow.importer.relational_import.run_direct_relational_import")
502+
@patch("odoo_data_flow.importer.import_threaded.import_data")
503+
@patch("odoo_data_flow.importer._run_preflight_checks")
504+
def test_run_import_with_relational_strategy(
505+
mock_preflight: MagicMock,
506+
mock_import_data: MagicMock,
507+
mock_run_direct_relational: MagicMock,
508+
mock_save_cache: MagicMock,
509+
tmp_path: Path,
510+
) -> None:
511+
"""Test that relational import strategies are called in Pass 2."""
512+
source_file = tmp_path / "source.csv"
513+
source_file.write_text("id,name,tags\np1,Partner 1,tag1,tag2")
514+
515+
def preflight_side_effect(*args: Any, **kwargs: Any) -> bool:
516+
kwargs["import_plan"]["strategies"] = {
517+
"tags": {"strategy": "direct_relational_import"}
518+
}
519+
return True
520+
521+
mock_preflight.side_effect = preflight_side_effect
522+
# Pass 1 successful, returns an id_map
523+
mock_import_data.return_value = (True, {"id_map": {"p1": 1}})
524+
# Pass 2 (from relational) returns None, so no third import call
525+
mock_run_direct_relational.return_value = None
526+
527+
run_import(
528+
config=str(tmp_path / "dummy.conf"),
529+
filename=str(source_file),
530+
model="res.partner",
531+
deferred_fields=None,
532+
unique_id_field=None,
533+
no_preflight_checks=False,
534+
headless=True,
535+
worker=1,
536+
batch_size=100,
537+
skip=0,
538+
fail=False,
539+
separator=",",
540+
ignore=None,
541+
context={},
542+
encoding="utf-8",
543+
o2m=False,
544+
groupby=None,
545+
)
546+
547+
assert mock_import_data.call_count == 1 # Only the first pass
548+
mock_run_direct_relational.assert_called_once()
549+
mock_save_cache.assert_called_once()
550+
551+
552+
@patch("odoo_data_flow.importer._show_error_panel")
553+
@patch("odoo_data_flow.importer._count_lines", return_value=0)
554+
@patch("odoo_data_flow.importer.import_threaded.import_data")
555+
@patch("odoo_data_flow.importer._run_preflight_checks", return_value=True)
556+
def test_run_import_fails_without_creating_fail_file(
557+
mock_preflight: MagicMock,
558+
mock_import_data: MagicMock,
559+
mock_count_lines: MagicMock,
560+
mock_show_error: MagicMock,
561+
tmp_path: Path,
562+
) -> None:
563+
"""Test the failure path where import fails but no fail file is created."""
564+
source_file = tmp_path / "source.csv"
565+
source_file.touch()
566+
# Simulate import_data returning success=False
567+
mock_import_data.return_value = (False, {})
568+
569+
run_import(
570+
config="dummy.conf",
571+
filename=str(source_file),
572+
model="res.partner",
573+
deferred_fields=None,
574+
unique_id_field=None,
575+
no_preflight_checks=False,
576+
headless=True,
577+
worker=1,
578+
batch_size=100,
579+
skip=0,
580+
fail=False,
581+
separator=";",
582+
ignore=None,
583+
context={},
584+
encoding="utf-8",
585+
o2m=False,
586+
groupby=None,
587+
)
588+
589+
mock_import_data.assert_called_once()
590+
mock_show_error.assert_called_once()
591+
assert "Import Failed" in mock_show_error.call_args[0]

0 commit comments

Comments
 (0)