test(e2e): bind finance release to exact validation

This commit is contained in:
2026-10-13 16:09:23 +03:00
parent 6d5dccfcd4
commit 53950556a4
2 changed files with 39 additions and 9 deletions

View File

@@ -30,6 +30,7 @@ services:
ALLOWED_ORIGINS: http://frontend
SOURCE_REPOSITORY_IDENTITY_FILE: /run/source-identity/identity.json
GITEA_API_BASE_URL: http://gitea:3000/api/v1
E2E_BACKEND_URL: http://127.0.0.1:8000
FULL_FLOW_ENV_FILE_UID: ${E2E_PRIVATE_ENV_UID:?Set E2E_PRIVATE_ENV_UID}
FULL_FLOW_ENV_FILE_GID: ${E2E_PRIVATE_ENV_GID:?Set E2E_PRIVATE_ENV_GID}
volumes:

View File

@@ -123,30 +123,52 @@ def export(driver, repo, metadata):
driver.call("git-author-profile", "PATCH", "/api/profile/preferences", {
"git_username": "full-flow-admin", "git_email": "full-flow@example.test",
"git_personal_access_token": driver.git_token})
driver.call("export-finance-dev", "POST", repo+"/sync"+suffix+"&source_env_id=full-flow-dev")
synced = driver.call("export-finance-dev", "POST", repo+"/sync"+suffix+"&source_env_id=full-flow-dev")
driver.require("finance-dev-sync-success", synced.get("status") == "success")
driver.call("commit-finance-dev", "POST", repo+"/commit"+suffix,
{"message": "Synthetic finance million-row fixture", "files": []})
driver.call("push-finance-dev", "POST", repo+"/push"+suffix)
driver.call("reject-direct-finance-prod", "POST", repo+"/deploy"+suffix,
direct_prod = driver.call("reject-direct-finance-prod", "POST", repo+"/deploy"+suffix,
{"stage": "prod"}, expected=(409,))
driver.require("finance-direct-prod-refusal-exact",
direct_prod.get("detail") == "Create and publish a named dashboard release before deploying to PROD")
driver.call("deploy-finance-preprod", "POST", repo+"/deploy"+suffix, {"stage": "preprod"})
driver.call("reject-unvalidated-finance-release", "POST", repo+"/releases"+suffix,
metadata, expected=(409,))
deployment_status = driver.call("finance-preprod-deployment-status", "GET",
repo+"/deployment-status"+suffix)
preprod_rows = [row for row in deployment_status.get("environments", []) if row.get("stage") == "preprod"]
driver.require("finance-preprod-deployment-identity-present", len(preprod_rows) == 1
and isinstance(preprod_rows[0].get("commit_hash"), str)
and bool(preprod_rows[0]["commit_hash"])
and isinstance(preprod_rows[0].get("content_hash"), str)
and bool(preprod_rows[0]["content_hash"]))
preprod_identity = {key: preprod_rows[0][key] for key in ("commit_hash", "content_hash")}
unvalidated = driver.call("reject-unvalidated-finance-release", "POST", repo+"/releases"+suffix,
metadata, expected=(409,))
driver.require("finance-unvalidated-release-refusal-exact",
unvalidated.get("detail") == "Validate the current PREPROD deployment before creating a release")
validation = driver.call("validate-finance-preprod", "POST", repo+"/deployment-validation"+suffix,
{"stage": "preprod", "comment": "Authorized synthetic finance review"})
stage = next((s for s in validation["environments"] if s["stage"] == "preprod"), {})
driver.require("finance-preprod-validated-exact", stage.get("validation_status") == "validated"
and stage.get("drift_status") == "in_sync", deployment=stage)
driver.require("finance-preprod-validation-preserves-identity",
all(stage.get(key) == value for key, value in preprod_identity.items()))
return preprod_identity
# #endregion FullFlow.FinanceRelease.Export
# #region FullFlow.FinanceRelease.Publish [C:4] [TYPE Function]
# @BRIEF Require approval and publish the same immutable PREPROD-tested release to PROD.
def publish(driver, repo, metadata):
def publish(driver, repo, metadata, preprod_identity):
suffix = "?env_id=full-flow-dev"
release = driver.call("create-finance-release", "POST", repo+"/releases"+suffix,
metadata, expected=(201,))
driver.ids["release_id"] = release["id"]
identity_valid = all(isinstance(preprod_identity.get(key), str) and preprod_identity[key]
and release.get(key) == preprod_identity[key]
for key in ("commit_hash", "content_hash"))
driver.require("finance-release-preserves-preprod-identity", identity_valid,
commit_hash=release.get("commit_hash"), content_hash=release.get("content_hash"))
driver.require("finance-release-awaits-human", release["status"] == "awaiting_approval")
approved = driver.call("approve-finance-release", "POST",
repo+f"/releases/{release['id']}/approve"+suffix,
@@ -158,7 +180,11 @@ def publish(driver, repo, metadata):
ledger = driver.call("finance-release-ledger", "GET", repo+"/releases"+suffix)
current = next(r for r in ledger if r["id"] == release["id"])
driver.require("finance-published-exact-commit", current["status"] == "published"
and current["commit_hash"] == release["commit_hash"])
and current["id"] == release["id"]
and current["commit_hash"] == release["commit_hash"]
and current["content_hash"] == release["content_hash"]
and current["commit_hash"] == preprod_identity["commit_hash"]
and current["content_hash"] == preprod_identity["content_hash"])
driver.output.joinpath("published-release.json").write_text(json.dumps(current, indent=2)+"\n")
# #endregion FullFlow.FinanceRelease.Publish
@@ -174,7 +200,10 @@ def main():
args = parser.parse_args()
private_environment(args.env_file)
asset = assets(args.assets)
asset["backend_url"] = os.environ.get("E2E_BACKEND_URL", "http://127.0.0.1:8000")
backend_url = os.environ.get("E2E_BACKEND_URL")
if backend_url != "http://127.0.0.1:8000":
raise ValueError("E2E_BACKEND_URL_REQUIRED")
asset["backend_url"] = backend_url
driver = Driver(asset, args.output)
driver.secrets.append(os.environ["CLICKHOUSE_PASSWORD"])
driver.phase = "finance-release"
@@ -186,8 +215,8 @@ def main():
driver.login()
driver.setup()
repository(driver, repo)
export(driver, repo, metadata)
publish(driver, repo, metadata)
preprod_identity = export(driver, repo, metadata)
publish(driver, repo, metadata, preprod_identity)
except (Blocked, KeyError, StopIteration, ValueError, httpx.HTTPError) as exc:
driver.record("finance-release-stopped", "BLOCKED", reason=str(exc))
finally: