This commit is contained in:
1 parent
4db3f45164
commit
d25c04cdd2
175 files changed
+56243
-15
No files matched your search
@@ -0,0 +1,273 @@
|
||||
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())
|
||||
Reference in new issue
Block a user