Publish curated Unraid-AppOps-Gateway source from 2e53f628d284213b90ca2f9c5e121671f5933574
This commit is contained in:
1 parent
4f9c5a1106
commit
2facb8de34
13 files changed
+3868
-1749
No files matched your search
@@ -5718,6 +5718,131 @@ def test_appops_request_is_idempotent_and_runs_only_fixed_deploy(status_module,
|
||||
assert calls == [["python3", str(status_module.ROOT / "deploy.py"), "deploy", "jens/repo-a", "a" * 40]]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("operation", ["apply", "rollback"])
|
||||
def test_generic_appops_hub_request_is_rejected_before_inbox_creation(
|
||||
status_module, tmp_path, monkeypatch, operation
|
||||
):
|
||||
inbox = tmp_path / "appops-inbox"
|
||||
monkeypatch.setattr(status_module, "APPOPS_DEPLOYMENT_INBOX_ROOT", inbox)
|
||||
|
||||
status, request_id = status_module._enqueue_appops_request(
|
||||
repository="Jens/ITWorx_MCP_Hub",
|
||||
revision="a" * 40,
|
||||
operation=operation,
|
||||
idempotency_key=f"appops-hub-{operation}-0001",
|
||||
plan={},
|
||||
)
|
||||
|
||||
assert (status, request_id) == ("invalid", "")
|
||||
assert not inbox.exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("operation", ["apply", "rollback"])
|
||||
@pytest.mark.parametrize(
|
||||
("prior_status", "expected_status"),
|
||||
[("pending", "failed"), ("running", "outcome_unknown")],
|
||||
)
|
||||
def test_direct_appops_hub_record_is_denied_before_project_or_deploy_access(
|
||||
status_module, tmp_path, monkeypatch, operation, prior_status, expected_status
|
||||
):
|
||||
inbox = tmp_path / "appops-inbox"
|
||||
inbox.mkdir()
|
||||
request_id = "a" * 64
|
||||
marker = inbox / f"{request_id}.json"
|
||||
marker.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema": "appops-deployment-request.v1",
|
||||
"request": request_id,
|
||||
"repository": "jens/itworx_mcp_hub",
|
||||
"revision": "b" * 40,
|
||||
"operation": operation,
|
||||
"plan_sha256": "c" * 64,
|
||||
"status": prior_status,
|
||||
"received_at": 1,
|
||||
"updated_at": 1,
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
status_module,
|
||||
"_load_projects",
|
||||
lambda: pytest.fail("generic Hub records must not inspect deployment projects"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
status_module.subprocess,
|
||||
"run",
|
||||
lambda *_args, **_kwargs: pytest.fail("generic Hub records must not launch deploy.py"),
|
||||
)
|
||||
|
||||
status_module._process_appops_record(marker)
|
||||
|
||||
record = json.loads(marker.read_text(encoding="utf-8"))
|
||||
assert record["status"] == expected_status
|
||||
assert record["reason"] == "canonical_hub_push_required"
|
||||
|
||||
|
||||
def test_hub_protected_push_worker_remains_the_fixed_deploy_path(
|
||||
status_module, tmp_path, monkeypatch
|
||||
):
|
||||
repository = "jens/itworx_mcp_hub"
|
||||
inbox = tmp_path / "webhook-inbox"
|
||||
projects = tmp_path / "projects.json"
|
||||
project = {
|
||||
**_webhook_project(repository),
|
||||
"slug": "itworx-mcp-hub",
|
||||
"containers": ["itworx-mcp-hub"],
|
||||
}
|
||||
projects.write_text(json.dumps({repository: project}), encoding="utf-8")
|
||||
monkeypatch.setattr(status_module, "WEBHOOK_INBOX_ROOT", inbox)
|
||||
monkeypatch.setattr(status_module, "PROJECTS_PATH", projects)
|
||||
|
||||
revision = "d" * 40
|
||||
raw_body = json.dumps(
|
||||
_push_payload(revision, repository=repository), separators=(",", ":")
|
||||
).encode()
|
||||
_, request_id = status_module._enqueue_webhook_delivery(
|
||||
raw_body,
|
||||
"145440ab-b7b1-4c74-ae84-996d8d48167e",
|
||||
77,
|
||||
"refs/heads/main",
|
||||
repository,
|
||||
revision,
|
||||
status_module._validation_policy(repository, project),
|
||||
)
|
||||
marker = inbox / f"{request_id}.json"
|
||||
calls = []
|
||||
|
||||
class Result:
|
||||
returncode = 0
|
||||
|
||||
monkeypatch.setattr(
|
||||
status_module.subprocess,
|
||||
"run",
|
||||
lambda arguments, **_kwargs: calls.append(arguments) or Result(),
|
||||
)
|
||||
monkeypatch.setattr(status_module, "_deployment_state", lambda _slug: {})
|
||||
monkeypatch.setattr(status_module, "_verified_receipt", lambda *_args: {"ok": True})
|
||||
monkeypatch.setattr(
|
||||
status_module,
|
||||
"_fetch_gitea_validation",
|
||||
lambda *_args: (
|
||||
"pass",
|
||||
[{
|
||||
"context": "Managed validation / full (push)",
|
||||
"status": "success",
|
||||
"updated_at": "2026-09-14T00:00:00Z",
|
||||
}],
|
||||
),
|
||||
)
|
||||
|
||||
status_module._process_webhook_record(marker)
|
||||
|
||||
assert json.loads(marker.read_text(encoding="utf-8"))["status"] == "deployed"
|
||||
assert calls == [["python3", str(status_module.ROOT / "deploy.py"), "deploy", repository, revision]]
|
||||
|
||||
|
||||
def test_durable_rollback_snapshot_is_receipt_bound_and_not_caller_selected(deploy_module, tmp_path, monkeypatch):
|
||||
previous_revision = "a" * 40
|
||||
current_revision = "b" * 40
|
||||
|
||||
Reference in new issue
Block a user