feat: complete production workflow migration

This commit is contained in:
2026-08-06 14:29:57 +08:00
parent 7f215e79c4
commit 8df5266abb
448 changed files with 56937 additions and 14619 deletions
+142
View File
@@ -6,6 +6,10 @@ from pathlib import Path
import pytest
from gyxx_flow.adapters.acceptance_policy import (
WORKFLOW_ACCEPTANCE_RECIPIENT_OPEN_ID,
WorkflowAcceptancePolicyError,
)
from gyxx_flow.adapters.native import (
DeferredModuleCommandStep,
ModuleCommandAdapter,
@@ -14,6 +18,7 @@ from gyxx_flow.adapters.native import (
)
from gyxx_flow.catalog import WorkflowEntry
from gyxx_flow.core.context import RunContext
from gyxx_flow.workflow.steps import StepExecution
def _entry(**overrides: object) -> WorkflowEntry:
@@ -86,6 +91,31 @@ def test_deferred_module_step_dry_run_never_resolves_or_executes() -> None:
assert execution.reason == "dry-run"
def test_deferred_module_step_renders_business_date_in_catalog_args() -> None:
captured: list[WorkflowEntry] = []
class SuccessStep:
def execute(self, **_kwargs: object) -> StepExecution:
return StepExecution(exit_code=0)
def command_factory(entry: WorkflowEntry, _context: RunContext) -> SuccessStep:
captured.append(entry)
return SuccessStep()
execution = DeferredModuleCommandStep(
_entry(),
command_factory=command_factory,
command_args=("--start-date", "{business_date}", "run={run_id}"),
).execute(context=_context(), timeout_seconds=60, dry_run=False)
assert execution.exit_code == 0
assert captured[0].args == (
"--start-date",
"2026-07-27",
f"run={_context().run_id}",
)
def test_module_adapter_uses_explicit_windows_launchers_for_batch_and_powershell(
tmp_path: Path,
) -> None:
@@ -131,3 +161,115 @@ def test_product_config_uses_only_the_new_environment_name(tmp_path: Path) -> No
assert command.env["GYXX_PRODUCT_CONFIG"] == "D:/portable/product.json"
assert "AUTO_FLOW_CONFIG" not in command.env
def test_product_config_defaults_to_canonical_module_state(tmp_path: Path) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
data_root = tmp_path / "var"
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={},
project_root=tmp_path,
data_root=data_root,
)
command = adapter.build(_entry(entry="job.py", args=()), context=_context())
assert command.env["GYXX_PRODUCT_CONFIG"] == str(
(data_root / "state" / "product_commerce" / "auto-flow-config.json").resolve()
)
def test_product_config_maps_legacy_portable_alias(tmp_path: Path) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={"AUTOFLOW_CONFIG_PATH": "D:/portable/legacy-product.json"},
project_root=tmp_path,
data_root=tmp_path / "var",
)
command = adapter.build(_entry(entry="job.py", args=()), context=_context())
assert command.env["GYXX_PRODUCT_CONFIG"] == "D:/portable/legacy-product.json"
def test_product_config_maps_original_source_environment_name(tmp_path: Path) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={"AUTO_FLOW_CONFIG": "D:/original/product.json"},
project_root=tmp_path,
data_root=tmp_path / "var",
)
command = adapter.build(_entry(entry="job.py", args=()), context=_context())
assert command.env["GYXX_PRODUCT_CONFIG"] == "D:/original/product.json"
def test_product_config_uses_explicit_precedence_order(tmp_path: Path) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={
"GYXX_PRODUCT_CONFIG": "D:/canonical/product.json",
"AUTO_FLOW_CONFIG": "D:/original/product.json",
"AUTOFLOW_CONFIG_PATH": "D:/portable/product.json",
},
project_root=tmp_path,
data_root=tmp_path / "var",
)
command = adapter.build(_entry(entry="job.py", args=()), context=_context())
assert command.env["GYXX_PRODUCT_CONFIG"] == "D:/canonical/product.json"
def test_module_adapter_propagates_fail_closed_acceptance_policy(
tmp_path: Path,
) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={"GYXX_WORKFLOW_ACCEPTANCE": "1"},
project_root=tmp_path,
data_root=tmp_path / "var",
)
command = adapter.build(_entry(entry="job.py", args=()), context=_context())
assert command.env["GYXX_FEISHU_TABLE_WRITE_DISABLED"] == "1"
assert command.env["GYXX_COOKIE_INVALID_SKIP"] == "1"
assert (
command.env["GYXX_NOTIFICATION_RECIPIENT_OPEN_ID"]
== WORKFLOW_ACCEPTANCE_RECIPIENT_OPEN_ID
)
def test_module_adapter_rejects_unsafe_acceptance_recipient(tmp_path: Path) -> None:
root = tmp_path / "runtime"
root.mkdir()
(root / "job.py").write_text("pass", encoding="utf-8")
adapter = ModuleCommandAdapter(
ModuleSourceRoots({"product_commerce": root}),
base_env={
"GYXX_WORKFLOW_ACCEPTANCE": "1",
"GYXX_NOTIFICATION_RECIPIENT_OPEN_ID": "ou_not_wang",
},
project_root=tmp_path,
data_root=tmp_path / "var",
)
with pytest.raises(WorkflowAcceptancePolicyError, match="Wang Yunlong"):
adapter.build(_entry(entry="job.py", args=()), context=_context())