fix(e2e): resume published chart response exactly

This commit is contained in:
2026-10-14 09:52:45 +03:00
parent cbe372a1ce
commit 825203cdca
3 changed files with 200 additions and 11 deletions

View File

@@ -71,7 +71,13 @@ e2e-run: ## Run chart-8 E2E for an explicit lab target (requires a mode-0600 LAB
@test "$(TARGET)" = "lab" || { echo "TARGET=lab is required"; exit 2; }
@test -n "$(LAB_PROFILE)" -a -f "$(LAB_PROFILE)" || { echo "LAB_PROFILE must name a mode-0600 explicit target/credentials-path profile"; exit 2; }
@test -x "$(E2E_PYTHON)" || { echo "E2E_PYTHON must name an executable Python with backend dependencies (default: $(BACKEND)/.venv/bin/python)"; exit 2; }
@cd $(BACKEND) && "$(E2E_PYTHON)" ../scripts/e2e-scripts/native_chart8_prepare.py --profile "$(LAB_PROFILE)" $(if $(filter 1,$(E2E_VALIDATE_ONLY)),--validate-only,--run-lifecycle)
@case "$(E2E_MODE)" in \
resume-published) mode=--resume-published ;; \
fresh|'') mode=--run-lifecycle ;; \
*) echo "E2E_MODE must be fresh or resume-published"; exit 2 ;; \
esac; \
if [ "$(E2E_VALIDATE_ONLY)" = "1" ]; then mode=--validate-only; fi; \
cd $(BACKEND) && "$(E2E_PYTHON)" ../scripts/e2e-scripts/native_chart8_prepare.py --profile "$(LAB_PROFILE)" "$$mode"
lab-reset: ## Prepare/archive or explicitly apply the exact approved chart-8 row reset (PHASE=prepare|apply|verify)
@test -n "$(SCOPE)" || { echo "SCOPE must name the mode-0600 exact lab reset scope JSON"; exit 2; }

View File

@@ -161,6 +161,7 @@ def validate_target_descriptor(target: dict, seed: dict, release: dict) -> None:
raise Blocked("NATIVE_CAPTURE_SCENARIO_CASES_REQUIRED")
elif capture_scenario.get("mode") == "existing_mcp_revision":
validate_existing_mcp_revision(capture_scenario)
validate_published_resume_target(target)
else:
raise Blocked("NATIVE_CAPTURE_SCENARIO_MODE_UNSUPPORTED")
numeric = target.get("numeric_verification")
@@ -286,6 +287,20 @@ def validate_existing_mcp_revision(selection: dict) -> None:
raise Blocked("NATIVE_CAPTURE_SCENARIO_REVISION_REQUIRED")
def validate_published_resume_target(target: dict) -> None:
resume = target.get("published_resume") if isinstance(target, dict) else None
selection = target.get("catalog_selection") if isinstance(target, dict) else None
if (target.get("catalog_mode") != "exact_revision"
or not isinstance(selection, dict) or selection.get("kind") != "revision"
or not _is_uuid(selection.get("catalog_id"))
or not _is_uuid(selection.get("catalog_revision_id"))
or not isinstance(resume, dict)
or set(resume) != {"capture_artifact_id", "publication_operation_id"}
or not _is_uuid(resume.get("capture_artifact_id"))
or not _is_uuid(resume.get("publication_operation_id"))):
raise Blocked("NATIVE_PUBLISHED_RESUME_INPUT_INVALID")
def _is_uuid(value: object) -> bool:
if type(value) is not str:
return False
@@ -489,8 +504,6 @@ def register_mcp_owned_scenario(driver: Driver, dashboard_id: int, model: dict,
def select_m01_profile_coordinate(profile: dict, target: dict, dashboard_id: int, model: dict) -> dict:
if not isinstance(profile, dict) or not isinstance(profile.get("coordinates"), list):
raise Blocked("NATIVE_M01_PROFILE_COORDINATES_UNAVAILABLE")
selection = target["capture_scenario"]
metric_name = selection["metric_name"]
charts = [chart for chart in model.get("charts", [])
if chart.get("chart_uuid") == target["chart_uuid"]]
if len(charts) != 1:
@@ -501,15 +514,103 @@ def select_m01_profile_coordinate(profile: dict, target: dict, dashboard_id: int
if isinstance(item, dict)
and item.get("chart_id") == expected_chart_id
and item.get("dataset_id") == expected_dataset_id
and item.get("metric_name") == metric_name
and item.get("metric_name") is None
and item.get("reference_coordinate") == {
"kind": "metric", "chart_uuid": target["chart_uuid"], "metric": metric_name,
"kind": "chart", "chart_uuid": target["chart_uuid"], "metric": None,
}]
if type(expected_chart_id) is not int or len(coordinates) != 1:
raise Blocked("NATIVE_M01_PROFILE_COORDINATE_AMBIGUOUS")
return coordinates[0]
def read_published_capture(driver: Driver, target: dict, model: dict) -> dict:
"""Resume M01 only from an explicitly named, publicly read-back publication."""
resume = target.get("published_resume")
selection = target.get("catalog_selection")
expected_capture = resume.get("capture_artifact_id") if isinstance(resume, dict) else None
operation_id = resume.get("publication_operation_id") if isinstance(resume, dict) else None
if (not isinstance(resume, dict) or set(resume) != {"capture_artifact_id", "publication_operation_id"}
or not _is_uuid(expected_capture) or not _is_uuid(operation_id)
or target.get("catalog_mode") != "exact_revision"
or not isinstance(selection, dict) or selection.get("kind") != "revision"):
raise Blocked("NATIVE_PUBLISHED_RESUME_INPUT_INVALID")
operation = driver.call("published-resume-operation-readback", "GET",
f"/api/catalog-publications/{operation_id}")
revision_response = driver.call("published-resume-revision-readback", "GET",
"/api/dashboard-testing/baselines?" + urlencode({
"catalog_id": selection["catalog_id"],
"catalog_revision_id": selection["catalog_revision_id"],
}))
revision = revision_response.get("revision", {})
if (operation.get("operation_id") != operation_id or operation.get("state") != "published"
or operation.get("catalog_revision_id") != selection["catalog_revision_id"]
or operation.get("catalog_digest") != revision.get("catalog_digest")
or not operation.get("published_receipt")
or operation.get("repository") != target["catalog_repository"]
or operation.get("branch") != target["catalog_branch"]
or revision.get("catalog_id") != selection["catalog_id"]
or revision.get("catalog_revision_id") != selection["catalog_revision_id"]
or revision.get("release_id") != target["release_identity"].get("id")):
raise Blocked("NATIVE_PUBLISHED_RESUME_IDENTITY_MISMATCH")
wrappers = revision.get("entry_revisions")
matches = [wrapper for wrapper in wrappers if isinstance(wrapper, dict)
and wrapper.get("capture_artifact_id") == expected_capture
and isinstance(wrapper.get("entry"), dict)]
if len(matches) != 1:
raise Blocked("NATIVE_PUBLISHED_RESUME_CAPTURE_MISSING")
wrapper = matches[0]
entry = wrapper["entry"]
expected = entry.get("expected") or {}
source = wrapper.get("reference_source") or {}
native = entry.get("native_result") or {}
authority = native.get("authority_record") or {}
descriptor = authority.get("descriptor") or {}
reference = descriptor.get("reference_coordinate") or {}
chart = next((item for item in model.get("charts", [])
if item.get("chart_uuid") == target["chart_uuid"]), None)
if (wrapper.get("status") != "approved" or entry.get("status") != "approved"
or expected.get("kind") != "chart_response"
or entry.get("result_key", "missing") is not None
or entry.get("chart_id") != descriptor.get("chart_id")
or entry.get("dataset_id") != descriptor.get("dataset_id")
or not isinstance(chart, dict)
or descriptor.get("chart_id") != chart.get("chart_id")
or descriptor.get("dataset_id") != chart.get("dataset_id")
or descriptor.get("kind") != "chart_response"
or authority.get("capture_artifact_id") != expected_capture
or descriptor.get("selected_metric", "missing") is not None
or reference != {"kind": "chart", "chart_uuid": target["chart_uuid"], "metric": None}
or source.get("environment_id") != target["target_environment_id"]
or source.get("dashboard_id") != target["resolved_target_dashboard_id"]
or source.get("filters_hash") != target["expected_filters_hash"]
or source.get("query_model_fingerprint") != model.get("query_model_fingerprint")
or entry.get("release_version") != target["release_identity"].get("version")
or entry.get("release_commit_hash") != target["release_identity"].get("commit_hash")):
raise Blocked("NATIVE_PUBLISHED_RESUME_CAPTURE_IDENTITY_MISMATCH")
driver.ids.update(capture_artifact_id=expected_capture,
published_catalog_revision_id=selection["catalog_revision_id"],
publication_operation_id=operation_id)
driver.record("published-capture-exact-resume", "PASS",
catalog_id=selection["catalog_id"],
catalog_revision_id=selection["catalog_revision_id"],
operation_id=operation_id,
capture_count=1, publication_state="published")
return {"capture": {"release_id": target["release_identity"]["id"],
"capture_artifact_ids": [expected_capture]},
"catalog_revision": revision,
"publication": operation,
"catalog_selection": selection,
"coordinate": {"chart_id": descriptor["chart_id"],
"dataset_id": descriptor["dataset_id"],
"chart_uuid": target["chart_uuid"],
"dataset_uuid": target["chart_contract"]["dataset_uuid"]},
"query_model_fingerprint": source["query_model_fingerprint"],
"filters_hash": source["filters_hash"]}
def register_m01_capture_scenario(driver: Driver, dashboard_id: int, model: dict,
environment_id: str, target: dict) -> dict:
"""Resolve the published chart-response source through the owner-scoped M01 MCP workflow."""
@@ -1330,8 +1431,11 @@ def main() -> int:
parser.add_argument("--output", type=Path)
parser.add_argument("--validate-only", action="store_true",
help="Validate explicit inputs without making API calls")
parser.add_argument("--run-lifecycle", action="store_true",
help="Run chart-8 capture, publication, pin, and three-run acceptance")
lifecycle = parser.add_mutually_exclusive_group()
lifecycle.add_argument("--run-lifecycle", action="store_true",
help="Run chart-8 capture, publication, pin, and three-run acceptance")
lifecycle.add_argument("--resume-published", action="store_true",
help="Resume only M01 and numeric acceptance from one exact published capture")
args = parser.parse_args()
try:
env_file, seed_path, release_path, target_path, output = explicit_inputs(args)
@@ -1414,8 +1518,12 @@ def main() -> int:
"target_dashboard_uuid": target["target_dashboard_uuid"],
"chart_uuid": target["chart_uuid"]}},
indent=2, ensure_ascii=False) + "\n")
if args.run_lifecycle:
captured = capture_approve_publish(driver, target, scenario, catalog, model)
if args.run_lifecycle or args.resume_published:
if args.resume_published:
captured = read_published_capture(driver, target, model)
pin_target = target
else:
captured = capture_approve_publish(driver, target, scenario, catalog, model)
driver.ids.update(
capture_artifact_id=(captured["capture"].get("capture_artifact_ids") or [None])[0],
published_catalog_revision_id=captured["catalog_revision"].get("catalog_revision_id"),
@@ -1426,7 +1534,8 @@ def main() -> int:
dataset_id=captured["coordinate"]["dataset_id"],
query_model_fingerprint=captured["query_model_fingerprint"],
filters_hash=captured["filters_hash"], capture_count=1, publication_state="published")
pin_target = target_for_published_revision(target, captured)
if not args.resume_published:
pin_target = target_for_published_revision(target, captured)
pin_scenario = register_m01_capture_scenario(driver, dashboard_id, model,
target["target_environment_id"], pin_target)
driver.ids.update(scenario_id=pin_scenario["scenario_id"],
@@ -1467,7 +1576,7 @@ def main() -> int:
driver.flush()
driver.client.close()
stopped = any(step["status"] in {"BLOCKED", "NEEDS_REVIEW"} for step in driver.steps)
if args.run_lifecycle:
if args.run_lifecycle or args.resume_published:
if not stopped and acceptance_completed:
driver.flush()
return 0

View File

@@ -14,8 +14,10 @@ from common import Blocked # noqa: E402
from native_chart8_prepare import ( # noqa: E402
bind_seeded_catalog_selection,
target_for_published_revision,
select_m01_profile_coordinate,
validate_lab_bootstrap_mode,
validate_existing_mcp_revision,
validate_published_resume_target,
)
@@ -42,6 +44,78 @@ def test_existing_mcp_revision_identity_requires_exact_uuid_values() -> None:
validate_existing_mcp_revision({"scenario_id": "first", "revision_id": "latest"})
def test_published_resume_requires_explicit_exact_revision_and_producer_ids() -> None:
valid = {
"catalog_mode": "exact_revision",
"catalog_selection": {
"kind": "revision",
"catalog_id": "67ec18a8-3d66-443e-8324-cdb8476d8f16",
"catalog_revision_id": "9ffd1709-6aa8-46ad-b997-447338116df1",
},
"published_resume": {
"capture_artifact_id": "306a684b-f8dd-4bec-b90d-48ff75ac4f5d",
"publication_operation_id": "58bd49d1-7845-405b-a43c-bcc44f7fdcf4",
},
}
validate_published_resume_target(valid)
invalid = {**valid, "catalog_selection": {**valid["catalog_selection"], "kind": "current"}}
with pytest.raises(Blocked, match="NATIVE_PUBLISHED_RESUME_INPUT_INVALID"):
validate_published_resume_target(invalid)
def _m01_profile_coordinate(*, chart_id=8, dataset_id=4, kind="chart", chart_uuid="chart-uuid",
metric=None) -> dict:
return {
"coordinate_id": f"coordinate-{chart_id}-{dataset_id}-{kind}-{metric}",
"chart_id": chart_id,
"dataset_id": dataset_id,
"metric_name": metric,
"reference_coordinate": {"kind": kind, "chart_uuid": chart_uuid, "metric": metric},
}
def _m01_profile(coordinates: list[dict]) -> tuple[dict, dict]:
target = {"chart_uuid": "chart-uuid", "capture_scenario": {"metric_name": "selected_value_cents"}}
return {"coordinates": coordinates}, target
def test_m01_selects_exact_published_whole_chart_coordinate() -> None:
metric = _m01_profile_coordinate(kind="metric", metric="selected_value_cents")
chart = _m01_profile_coordinate()
profile, target = _m01_profile([metric, chart])
selected = select_m01_profile_coordinate(profile, target, 2, {
"charts": [{"chart_id": 8, "dataset_id": 4, "chart_uuid": "chart-uuid"}],
})
assert selected == chart
@pytest.mark.parametrize("coordinate", [
_m01_profile_coordinate(chart_id=9),
_m01_profile_coordinate(dataset_id=5),
_m01_profile_coordinate(kind="metric", metric="selected_value_cents"),
_m01_profile_coordinate(chart_uuid="other-chart"),
])
def test_m01_refuses_wrong_published_chart_coordinate(coordinate: dict) -> None:
profile, target = _m01_profile([coordinate])
with pytest.raises(Blocked, match="NATIVE_M01_PROFILE_COORDINATE_AMBIGUOUS"):
select_m01_profile_coordinate(profile, target, 2, {
"charts": [{"chart_id": 8, "dataset_id": 4, "chart_uuid": "chart-uuid"}],
})
def test_m01_refuses_ambiguous_whole_chart_coordinates() -> None:
chart = _m01_profile_coordinate()
profile, target = _m01_profile([chart, chart.copy()])
with pytest.raises(Blocked, match="NATIVE_M01_PROFILE_COORDINATE_AMBIGUOUS"):
select_m01_profile_coordinate(profile, target, 2, {
"charts": [{"chart_id": 8, "dataset_id": 4, "chart_uuid": "chart-uuid"}],
})
def test_seeded_catalog_selection_is_bound_from_exact_official_result() -> None:
target = {"catalog_mode": "seed_empty", "source_dashboard_id": 2}
catalog = {"source_dashboard_id": 2, "selection": {