This commit is contained in:
1 parent
4db3f45164
commit
d25c04cdd2
175 files changed
+56243
-15
No files matched your search
@@ -0,0 +1,153 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import httpx2
|
||||
|
||||
from gewu.adapters.git_repository import GitCanonicalRepository
|
||||
from gewu.adapters.sqlite_identity import SQLiteIdentityStore
|
||||
from gewu.application.reading import CanonicalNodeReader
|
||||
from gewu.application.registry import LocalProjectRegistry
|
||||
from gewu.bootstrap.app import create_app
|
||||
from gewu.domain.contracts import AutoAcceptPolicy, ProjectRecord, ProjectStatus
|
||||
|
||||
|
||||
def test_project_registry_lifecycle_is_admin_only_and_durable(tmp_path: Path) -> None:
|
||||
async def exercise() -> None:
|
||||
identities = SQLiteIdentityStore(tmp_path / "identity.sqlite3")
|
||||
admin_token = identities.provision(
|
||||
credential_id="project-admin",
|
||||
principal_id="owner-operator",
|
||||
roles=("admin",),
|
||||
)
|
||||
viewer_token = identities.provision(
|
||||
credential_id="project-viewer",
|
||||
principal_id="research-reader",
|
||||
roles=("viewer",),
|
||||
)
|
||||
mixed_agent_admin_token = identities.provision(
|
||||
credential_id="mixed-agent-admin",
|
||||
principal_id="agent-with-admin-role",
|
||||
roles=("admin", "agent"),
|
||||
)
|
||||
repository = GitCanonicalRepository(tmp_path / "knowledge.git")
|
||||
registry = LocalProjectRegistry(
|
||||
tmp_path / "control" / "registry.json",
|
||||
repository=repository,
|
||||
index_directory=tmp_path / "publishing",
|
||||
authority_lock_path=tmp_path / "workflow" / "locks" / "canonical-writer.lock",
|
||||
)
|
||||
app = create_app(
|
||||
identity_provider=identities,
|
||||
node_reader=CanonicalNodeReader(repository),
|
||||
project_registry=registry,
|
||||
)
|
||||
project = ProjectRecord(
|
||||
project_id="new-research",
|
||||
title="New research",
|
||||
status=ProjectStatus.ACTIVE,
|
||||
description="An administrator-owned research lens.",
|
||||
owners=("owner-operator",),
|
||||
north_star="Investigate a new research area.",
|
||||
key_questions=("What is known?",),
|
||||
autonomy_policy_id="new-policy",
|
||||
)
|
||||
policy = AutoAcceptPolicy(
|
||||
policy_id="new-policy",
|
||||
policy_version=1,
|
||||
project_id="new-research",
|
||||
require_supported_assessment=True,
|
||||
)
|
||||
body = {
|
||||
"project": project.model_dump(mode="json"),
|
||||
"policy": policy.model_dump(mode="json"),
|
||||
}
|
||||
async with httpx2.AsyncClient(
|
||||
transport=httpx2.ASGITransport(app=app), base_url="http://test"
|
||||
) as http:
|
||||
assert (await http.get("/v1/admin/projects")).status_code == 401
|
||||
denied = await http.post(
|
||||
"/v1/admin/projects",
|
||||
json=body,
|
||||
headers={"Authorization": f"Bearer {viewer_token}"},
|
||||
)
|
||||
assert denied.status_code == 403
|
||||
mixed_agent_denied = await http.post(
|
||||
"/v1/admin/projects",
|
||||
json=body,
|
||||
headers={"Authorization": f"Bearer {mixed_agent_admin_token}"},
|
||||
)
|
||||
assert mixed_agent_denied.status_code == 403
|
||||
oversized = await http.post(
|
||||
"/v1/admin/projects",
|
||||
content=b" " * (64 * 1024 + 1),
|
||||
headers={
|
||||
"Authorization": f"Bearer {admin_token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
assert oversized.status_code == 413
|
||||
|
||||
headers = {"Authorization": f"Bearer {admin_token}"}
|
||||
diagnostics = await http.get("/v1/diagnostics", headers=headers)
|
||||
assert diagnostics.status_code == 200
|
||||
assert {
|
||||
"admin_list_projects",
|
||||
"project_create",
|
||||
"project_archive",
|
||||
}.issubset(diagnostics.json()["authorized_operations"])
|
||||
created = await http.post("/v1/admin/projects", json=body, headers=headers)
|
||||
assert created.status_code == 201, created.text
|
||||
assert created.json()["project_id"] == "new-research"
|
||||
listing = await http.get("/v1/admin/projects", headers=headers)
|
||||
assert [item["project_id"] for item in listing.json()] == ["new-research"]
|
||||
duplicate = await http.post("/v1/admin/projects", json=body, headers=headers)
|
||||
assert duplicate.status_code == 409
|
||||
archived = await http.post("/v1/admin/projects/new-research/archive", headers=headers)
|
||||
assert archived.status_code == 200
|
||||
assert archived.json()["status"] == "archived"
|
||||
assert registry.records()[0].status == ProjectStatus.ARCHIVED
|
||||
|
||||
session = identities.create_browser_session(admin_token)
|
||||
browser_project = project.model_copy(
|
||||
update={
|
||||
"project_id": "browser-created",
|
||||
"title": "Browser created",
|
||||
"autonomy_policy_id": "browser-policy",
|
||||
}
|
||||
)
|
||||
browser_policy = policy.model_copy(
|
||||
update={"policy_id": "browser-policy", "project_id": "browser-created"}
|
||||
)
|
||||
browser_body = {
|
||||
"project": browser_project.model_dump(mode="json"),
|
||||
"policy": browser_policy.model_dump(mode="json"),
|
||||
}
|
||||
cookie = {"Cookie": f"gewu_session={session.session_token}"}
|
||||
without_csrf = await http.post(
|
||||
"/v1/admin/projects",
|
||||
json=browser_body,
|
||||
headers={**cookie, "Origin": "http://test"},
|
||||
)
|
||||
assert without_csrf.status_code == 403
|
||||
wrong_origin = await http.post(
|
||||
"/v1/admin/projects",
|
||||
json=browser_body,
|
||||
headers={
|
||||
**cookie,
|
||||
"Origin": "https://attacker.example",
|
||||
"X-GEWU-CSRF": session.csrf_token,
|
||||
},
|
||||
)
|
||||
assert wrong_origin.status_code == 403
|
||||
browser_created = await http.post(
|
||||
"/v1/admin/projects",
|
||||
json=browser_body,
|
||||
headers={
|
||||
**cookie,
|
||||
"Origin": "http://test",
|
||||
"X-GEWU-CSRF": session.csrf_token,
|
||||
},
|
||||
)
|
||||
assert browser_created.status_code == 201, browser_created.text
|
||||
|
||||
asyncio.run(exercise())
|
||||
Reference in new issue
Block a user