from datetime import datetime, timezone from unittest.mock import MagicMock, patch import pytest from click.testing import CliRunner from order_bak.cli import main from order_bak.csv_utils import write_csv from order_bak.delete import delete_orders def _make_entity(pk, rk): return { "PartitionKey": pk, "RowKey": rk, "OrderTime": datetime(2025, 1, 1, tzinfo=timezone.utc), "ReceiveTime": None, "revenue": 10.0, "platformOrder": "p", "prodCount": 1, "pfId": pk, "prodId": "prod", "prodPrice": 10.0, "state": 1, "ver": 3, "customData": None, } # --- delete_orders unit tests --- def test_delete_groups_by_partition_and_batches(tmp_path): entities = [] for i in range(3): entities.append(_make_entity("user1", f"order{i}")) for i in range(3, 5): entities.append(_make_entity("user2", f"order{i}")) csv_path = tmp_path / "2025-01-01.csv" write_csv(csv_path, entities) mock_table = MagicMock() deleted_count = delete_orders( table_client=mock_table, csv_path=csv_path, batch_size=2, delay_ms=0, ) assert deleted_count == 5 # user1: 3 entities, batch_size=2 -> 2 batches (2 + 1) # user2: 2 entities, batch_size=2 -> 1 batch assert mock_table.submit_transaction.call_count == 3 def test_delete_respects_batch_size(tmp_path): entities = [_make_entity("user1", f"order{i}") for i in range(5)] csv_path = tmp_path / "2025-01-01.csv" write_csv(csv_path, entities) mock_table = MagicMock() deleted_count = delete_orders( table_client=mock_table, csv_path=csv_path, batch_size=2, delay_ms=0, ) assert deleted_count == 5 assert mock_table.submit_transaction.call_count == 3 # 2+2+1 # --- delete CLI command tests --- @pytest.fixture def runner(): return CliRunner() @pytest.fixture def config_file(tmp_path): cfg = tmp_path / "config.toml" cfg.write_text('[azure]\nconnection_string = "UseDevelopmentStorage=true"\n') return cfg def test_delete_command_no_backups(runner, config_file, tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) (tmp_path / "backups").mkdir() result = runner.invoke(main, ["--config", str(config_file), "delete"]) assert result.exit_code == 1 assert "No backup files found" in result.output def test_delete_command_aborts(runner, config_file, tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) backup_dir = tmp_path / "backups" backup_dir.mkdir() write_csv(backup_dir / "2025-01-01.csv", [_make_entity("u1", "o1")]) mock_table = MagicMock() with patch("azure.data.tables.TableClient.from_connection_string", return_value=mock_table): result = runner.invoke(main, ["--config", str(config_file), "delete"], input="n\n") assert "Aborted" in result.output mock_table.submit_transaction.assert_not_called() def test_delete_command_single_file(runner, config_file, tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) backup_dir = tmp_path / "backups" backup_dir.mkdir() write_csv(backup_dir / "2025-01-01.csv", [_make_entity("u1", f"o{i}") for i in range(3)]) mock_table = MagicMock() with patch("azure.data.tables.TableClient.from_connection_string", return_value=mock_table): result = runner.invoke(main, ["--config", str(config_file), "delete"], input="y\n") assert result.exit_code == 0 assert "3 orders" in result.output assert "1 file" in result.output assert mock_table.submit_transaction.called def test_delete_command_multiple_files(runner, config_file, tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) backup_dir = tmp_path / "backups" backup_dir.mkdir() write_csv(backup_dir / "2025-01-01.csv", [_make_entity("u1", f"o{i}") for i in range(2)]) write_csv(backup_dir / "2025-01-02.csv", [_make_entity("u2", f"o{i}") for i in range(3)]) mock_table = MagicMock() with patch("azure.data.tables.TableClient.from_connection_string", return_value=mock_table): result = runner.invoke(main, ["--config", str(config_file), "delete"], input="y\n") assert result.exit_code == 0 assert "5 orders" in result.output assert "2 file" in result.output # u1 (2 entities, batch 100) → 1 call; u2 (3 entities, batch 100) → 1 call assert mock_table.submit_transaction.call_count == 2