Files
gyxx-flow/tests/test_supply_acceptance_isolation.py

187 lines
5.5 KiB
Python

from __future__ import annotations
import importlib
import json
from pathlib import Path
import pytest
from gyxx_flow.adapters import WORKFLOW_ACCEPTANCE_RECIPIENT_OPEN_ID
from gyxx_flow.notification_routing import ANALYZER_ACCEPTANCE_RECIPIENT_OPEN_ID
@pytest.fixture
def acceptance_environment(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> Path:
evidence = tmp_path / "evidence.jsonl"
monkeypatch.setenv("GYXX_WORKFLOW_ACCEPTANCE", "1")
monkeypatch.setenv("GYXX_FEISHU_TABLE_WRITE_DISABLED", "1")
monkeypatch.setenv("GYXX_COOKIE_INVALID_SKIP", "1")
monkeypatch.setenv(
"GYXX_NOTIFICATION_RECIPIENT_OPEN_ID",
WORKFLOW_ACCEPTANCE_RECIPIENT_OPEN_ID,
)
monkeypatch.setenv("GYXX_ACCEPTANCE_EVIDENCE_FILE", str(evidence))
return evidence
def test_replenishment_bitable_run_is_physically_skipped(
tmp_path: Path,
acceptance_environment: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
module = importlib.import_module(
"gyxx_flow.modules.supply_chain.orchestrator.scripts."
"insert_replenishment_bitable"
)
pending = tmp_path / "pending_insert.json"
pending.write_text(
json.dumps(
{
"pending_count": 2,
"records": [
{"商品编号": "SKU1"},
{"商品编号": "SKU2"},
],
},
ensure_ascii=False,
),
encoding="utf-8",
)
monkeypatch.setattr(
module,
"_search_record_id_by_sku",
lambda *_args, **_kwargs: pytest.fail("must not access Feishu"),
)
result = module.run(pending)
assert result == {
"inserted": 0,
"updated": 0,
"skipped": 2,
"record_ids": [],
"errors": [],
"skipped_by_policy": True,
}
assert "feishu_write_skipped" in acceptance_environment.read_text(
encoding="utf-8"
)
def test_supply_card_sender_forces_only_wang_yunlong(
acceptance_environment: Path,
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
module = importlib.import_module(
"gyxx_flow.modules.supply_chain.orchestrator.scripts."
"send_card_notification"
)
monkeypatch.setattr(module, "STATE_ROOT", tmp_path)
run_id = "acceptance-run"
snapshot_path = module._notification_snapshot_path("replenishment", run_id)
snapshot_path.parent.mkdir(parents=True)
snapshot_path.write_text(
json.dumps(
{
"schema_version": 1,
"workflow": "replenishment",
"run_id": run_id,
"enabled": True,
"recipients": [ANALYZER_ACCEPTANCE_RECIPIENT_OPEN_ID],
"acceptance": True,
}
),
encoding="utf-8",
)
sent: list[str] = []
monkeypatch.setattr(
module,
"_send_card",
lambda open_id, _card, _key: sent.append(open_id) or "om_acceptance1",
)
monkeypatch.setattr(
module.pg_writer,
"mark_notification_sent",
lambda *_args, **_kwargs: None,
)
monkeypatch.setattr(
module.pg_writer,
"notification_already_sent",
lambda *_args, **_kwargs: True,
)
with pytest.raises(RuntimeError, match="snapshot mismatch"):
module.run(
["ou_other123"],
{"elements": []},
workflow="replenishment",
run_id=run_id,
idempotency_key="replenishment:acceptance-run",
)
result = module.run(
[ANALYZER_ACCEPTANCE_RECIPIENT_OPEN_ID],
{"elements": []},
workflow="replenishment",
run_id=run_id,
idempotency_key="replenishment:acceptance-run",
)
assert sent == [ANALYZER_ACCEPTANCE_RECIPIENT_OPEN_ID]
assert result == {
"message_ids": ["om_acceptance1"],
"recipient_message_ids": {
ANALYZER_ACCEPTANCE_RECIPIENT_OPEN_ID: "om_acceptance1",
},
"errors": [],
}
def test_supply_config_uses_the_two_local_hermes_api_servers(
acceptance_environment: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv(
"COLLECTOR_API_SERVER_URL",
"http://127.0.0.1:8643/v1/chat/completions",
)
monkeypatch.setenv(
"ANALYZER_API_SERVER_URL",
"http://127.0.0.1:8642/v1/chat/completions",
)
module = importlib.import_module(
"gyxx_flow.modules.supply_chain.orchestrator.config"
)
module = importlib.reload(module)
assert module.HERMES_GATEWAYS["collector"]["url"] == "http://127.0.0.1:8643/v1"
assert module.HERMES_GATEWAYS["collector"]["model"] == "data-collector"
assert module.HERMES_GATEWAYS["analyzer"]["url"] == "http://127.0.0.1:8642/v1"
assert module.HERMES_GATEWAYS["analyzer"]["model"] == "data-analyzer"
for targets in module.FEISHU_CONFIG["notification_targets"].values():
assert targets == [WORKFLOW_ACCEPTANCE_RECIPIENT_OPEN_ID]
def test_purchase_order_update_is_blocked_before_erp_mutation(
acceptance_environment: Path,
) -> None:
module = importlib.import_module(
"gyxx_flow.modules.supply_chain.orchestrator.mcp_workflow"
)
module = importlib.reload(module)
result = module.run_mcp_workflow(
"purchase-order-update",
"acceptance-purchase-order-update",
)
assert result["status"] == "blocked_business_mutation"
assert "approved test task/SKU fixture" in result["error"]
assert "business_mutation_blocked" in acceptance_environment.read_text(
encoding="utf-8"
)