import asyncio import hashlib from pathlib import Path import httpx import yaml from gewu.adapters.git_repository import GitCanonicalRepository from gewu.adapters.sqlite_identity import SQLiteIdentityStore from gewu.adapters.text_extractor import TextMarkdownExtractor from gewu.application.reading import CanonicalNodeReader from gewu.application.uploads import LocalArtifactStore from gewu.bootstrap.app import create_app from tests.domain.proposal_fixtures import NODE, SOURCE_REVISION def test_authenticated_scoped_canonical_node_read(tmp_path: Path) -> None: async def exercise() -> None: repository = GitCanonicalRepository(tmp_path / "knowledge.git") project_key = hashlib.sha256(NODE.project_id.encode()).hexdigest() node_key = hashlib.sha256(NODE.node_id.encode()).hexdigest() node_path = f"projects/{project_key}/nodes/{node_key}.yaml" source_path = ( f"sources/{hashlib.sha256(SOURCE_REVISION.source_id.encode()).hexdigest()}/" f"{hashlib.sha256(SOURCE_REVISION.revision_id.encode()).hexdigest()}.yaml" ) artifact_store = LocalArtifactStore(tmp_path / "artifacts") source_bytes = b"# Archived source\nA precise citation.\n" source_digest = hashlib.sha256(source_bytes).hexdigest() upload_id = artifact_store.begin( filename="source.md", media_type="text/markdown", byte_length=len(source_bytes), expected_sha256=source_digest, owner_id="agent-pilot", project_id=NODE.project_id, ).upload_id artifact_store.put_chunk( upload_id, offset=0, content=source_bytes, chunk_sha256=source_digest, owner_id="agent-pilot", ) artifact_store.finalize(upload_id, owner_id="agent-pilot") extraction = TextMarkdownExtractor().extract( SOURCE_REVISION.revision_id, source_bytes, "text/markdown" ) artifact_store.save_extraction( extraction, owner_id="agent-pilot", project_id=NODE.project_id ) archived_source = SOURCE_REVISION.model_copy( update={"artifact_sha256": source_digest, "byte_length": len(source_bytes)} ) repository.publish_files( expected_head=repository.head(), files={node_path: yaml.safe_dump(NODE.model_dump(mode="json")).encode()}, message="Publish test node", index_directory=tmp_path / "publishing", ) updated_node = NODE.model_copy(update={"summary": "A revised accepted summary."}) restricted_source = SOURCE_REVISION.model_copy( update={ "source_id": "video:private", "revision_id": "video:private:r1", "ingested_for": ("interactive-video",), } ) restricted_source_path = ( f"sources/{hashlib.sha256(restricted_source.source_id.encode()).hexdigest()}/" f"{hashlib.sha256(restricted_source.revision_id.encode()).hexdigest()}.yaml" ) reference_source = SOURCE_REVISION.model_copy( update={ "source_id": "dome:reference-only", "revision_id": "dome:reference-only:r1", "artifact_sha256": None, "media_type": "application/octet-stream", "byte_length": 0, "original_url": "https://research.example/unavailable-paper", "ingested_for": (NODE.project_id,), "archive_status": "reference-only", } ) reference_source_path = ( f"sources/{hashlib.sha256(reference_source.source_id.encode()).hexdigest()}/" f"{hashlib.sha256(reference_source.revision_id.encode()).hexdigest()}.yaml" ) repository.publish_files( expected_head=repository.head(), files={ node_path: yaml.safe_dump(updated_node.model_dump(mode="json")).encode(), source_path: yaml.safe_dump(archived_source.model_dump(mode="json")).encode(), restricted_source_path: yaml.safe_dump( restricted_source.model_dump(mode="json") ).encode(), reference_source_path: yaml.safe_dump( reference_source.model_dump(mode="json") ).encode(), }, message="Revise node and register source", index_directory=tmp_path / "publishing", ) repository.publish_files( expected_head=repository.head(), files={"metadata/unrelated.yaml": b"accepted but unrelated\n"}, message="Advance repository without changing the node", index_directory=tmp_path / "publishing", ) identities = SQLiteIdentityStore(tmp_path / "identity.sqlite3") token = identities.provision( credential_id="agent-key", principal_id="agent-pilot", roles=("agent",), project_ids=(NODE.project_id,), ) app = create_app( identity_provider=identities, node_reader=CanonicalNodeReader(repository), artifact_store=artifact_store, ) async with httpx.AsyncClient( transport=httpx.ASGITransport(app=app), base_url="http://gewu.test" ) as client: path = f"/v1/projects/{NODE.project_id}/nodes/{NODE.node_id}" assert (await client.get(path)).status_code == 401 malformed_id = await client.get( f"/v1/projects/{NODE.project_id}/nodes/not%20a%20record-id", headers={"Authorization": f"Bearer {token}"}, ) assert malformed_id.status_code == 422 denied = await client.get( f"/v1/projects/interactive-video/nodes/{NODE.node_id}", headers={"Authorization": f"Bearer {token}"}, ) assert denied.status_code == 403 accepted = await client.get(path, headers={"Authorization": f"Bearer {token}"}) assert accepted.status_code == 200 envelope = accepted.json() assert envelope["retrieval_mode"] == "canonical" assert envelope["canonical_revision"] == repository.head() assert envelope["data"][0]["node_id"] == NODE.node_id assert envelope["citations"][0]["quote"] == NODE.claims[0].evidence[0].quote history = await client.get( f"{path}/history", headers={"Authorization": f"Bearer {token}"} ) assert history.status_code == 200 history_data = history.json()["data"] assert len(history_data) == 2 assert history_data[0]["node"]["summary"] == updated_node.summary latest_node_revision = history_data[0]["revision"] assert latest_node_revision != repository.head() latest_file_version = await client.get( f"{path}?revision={latest_node_revision}", headers={"Authorization": f"Bearer {token}"}, ) assert latest_file_version.status_code == 200 assert latest_file_version.json()["canonical_revision"] == latest_node_revision assert latest_file_version.json()["data"][0]["summary"] == updated_node.summary historical_revision = history_data[-1]["revision"] historical = await client.get( f"{path}?revision={historical_revision}", headers={"Authorization": f"Bearer {token}"}, ) assert historical.status_code == 200, historical.text assert historical.json()["canonical_revision"] == historical_revision assert historical.json()["data"][0]["summary"] == NODE.summary assert historical.json()["citations"][0]["quote"] == NODE.claims[0].evidence[0].quote malformed_revision = await client.get( f"{path}?revision=not-a-commit", headers={"Authorization": f"Bearer {token}"}, ) assert malformed_revision.status_code == 422 unknown_revision = await client.get( f"{path}?revision={'a' * 40}", headers={"Authorization": f"Bearer {token}"}, ) assert unknown_revision.status_code == 404 source = await client.get( f"/v1/projects/{NODE.project_id}/sources/{SOURCE_REVISION.revision_id}", headers={"Authorization": f"Bearer {token}"}, ) assert source.status_code == 200 assert source.json()["data"][0]["revision_id"] == SOURCE_REVISION.revision_id reference_path = ( f"/v1/projects/{NODE.project_id}/sources/{reference_source.revision_id}" ) reference = await client.get( reference_path, headers={"Authorization": f"Bearer {token}"} ) assert reference.status_code == 200 assert reference.json()["data"][0]["artifact_sha256"] is None diagnostics_path = "/v1/diagnostics?projects=dome-performance" assert (await client.get("/v1/diagnostics")).status_code == 401 diagnostic_response = await client.get( diagnostics_path, headers={"Authorization": f"Bearer {token}"} ) assert diagnostic_response.status_code == 200 diagnostic_body = diagnostic_response.json() catalog_path = Path(__file__).resolve().parents[2] / "schema/v1/catalog.json" assert ( diagnostic_body["catalog_sha256"] == hashlib.sha256(catalog_path.read_bytes()).hexdigest() ) assert diagnostic_body["selected_scope"]["projects"] == [NODE.project_id] assert diagnostic_body["index_state"] == "not_configured" assert diagnostic_body["max_upload_bytes"] == 50 * 1024 * 1024 assert "get_source_artifact" in diagnostic_body["authorized_operations"] assert token not in diagnostic_response.text assert "principal_id" not in diagnostic_body forbidden_diagnostic_scope = await client.get( "/v1/diagnostics?projects=interactive-video", headers={"Authorization": f"Bearer {token}"}, ) assert forbidden_diagnostic_scope.status_code == 403 forbidden_shared_scope = await client.get( diagnostics_path + "&include_shared=true", headers={"Authorization": f"Bearer {token}"}, ) assert forbidden_shared_scope.status_code == 403 artifact_path = ( f"/v1/projects/{NODE.project_id}/sources/{SOURCE_REVISION.revision_id}/artifact" ) assert (await client.get(artifact_path)).status_code == 401 archived = await client.get(artifact_path, headers={"Authorization": f"Bearer {token}"}) assert archived.status_code == 200 assert archived.content == source_bytes assert archived.headers["etag"] == f'"{source_digest}"' assert archived.headers["x-content-type-options"] == "nosniff" assert archived.headers["cache-control"] == "private, no-store" assert archived.headers["content-security-policy"] == "default-src 'none'; sandbox" missing_artifact = await client.get( f"{reference_path}/artifact", headers={"Authorization": f"Bearer {token}"} ) assert missing_artifact.status_code == 404 block = extraction.blocks[0] offset = block.normalized_text.index("precise") location_path = ( f"/v1/projects/{NODE.project_id}/sources/{SOURCE_REVISION.revision_id}" f"/extractions/{extraction.extraction_id}/blocks/{block.block_id}" ) location = await client.get( location_path, params={"offset": offset, "length": len("precise")}, headers={"Authorization": f"Bearer {token}"}, ) assert location.status_code == 200 assert location.json()["normalized_text"] == "precise" assert location.json()["original_text"] == "precise" denied_location = await client.get( location_path, params={"offset": offset, "length": len("precise")}, ) assert denied_location.status_code == 401 restricted = await client.get( f"/v1/projects/{NODE.project_id}/sources/{restricted_source.revision_id}", headers={"Authorization": f"Bearer {token}"}, ) assert restricted.status_code == 404 restricted_artifact = await client.get( f"/v1/projects/{NODE.project_id}/sources/{restricted_source.revision_id}/artifact", headers={"Authorization": f"Bearer {token}"}, ) assert restricted_artifact.status_code == 404 assert identities.revoke("agent-key") assert ( await client.get(path, headers={"Authorization": f"Bearer {token}"}) ).status_code == 401 assert ( await client.get(artifact_path, headers={"Authorization": f"Bearer {token}"}) ).status_code == 401 asyncio.run(exercise())