Files
2026-10-14 19:21:35 +08:00

396 lines
14 KiB
Python

import hashlib
from pathlib import Path
import pytest
from gewu.application.uploads import CHUNK_BYTES, LocalArtifactStore, UploadError
from tests.domain.proposal_fixtures import EXTRACTION
def _begin(store: LocalArtifactStore, content: bytes, *, now: int = 1_000) -> str:
session = store.begin(
filename="selected.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256=hashlib.sha256(content).hexdigest(),
now=now,
)
return session.upload_id
def test_matching_chunk_retry_and_restart_are_idempotent(tmp_path: Path) -> None:
content = b"# Evidence\nA source block.\n"
first_store = LocalArtifactStore(tmp_path)
upload_id = _begin(first_store, content)
digest = hashlib.sha256(content).hexdigest()
assert first_store.put_chunk(
upload_id, offset=0, content=content, chunk_sha256=digest, now=1_001
) == (0,)
restarted = LocalArtifactStore(tmp_path)
assert restarted.verified_offsets(upload_id, now=1_002) == (0,)
assert restarted.put_chunk(
upload_id, offset=0, content=content, chunk_sha256=digest, now=1_002
) == (0,)
receipt = restarted.finalize(upload_id, now=1_003)
assert receipt.sha256 == digest
assert receipt.status == "staged"
with restarted.open_artifact(receipt.sha256) as artifact:
assert artifact.read() == content
def test_pasted_capture_receipt_survives_restart_and_conflicts_on_changed_input(
tmp_path: Path,
) -> None:
first = LocalArtifactStore(tmp_path)
result = {"sha256": "a" * 64, "source_candidate": {"revision_id": "paste:r1"}}
with first.capture_receipt_guard():
first.save_capture_receipt(
"capture-1",
owner_id="agent-a",
project_id="project-a",
request_sha256="b" * 64,
result=result,
)
restarted = LocalArtifactStore(tmp_path)
with restarted.capture_receipt_guard():
assert (
restarted.get_capture_receipt(
"capture-1",
owner_id="agent-a",
project_id="project-a",
request_sha256="b" * 64,
)
== result
)
with pytest.raises(UploadError) as error:
restarted.get_capture_receipt(
"capture-1",
owner_id="agent-a",
project_id="project-a",
request_sha256="c" * 64,
)
assert error.value.code == "CAPTURE_CONFLICT"
with pytest.raises(UploadError) as error:
restarted.get_capture_receipt(
"capture-1",
owner_id="agent-a",
project_id="project-b",
request_sha256="b" * 64,
)
assert error.value.code == "CAPTURE_CONFLICT"
assert (
restarted.get_capture_receipt(
"capture-1",
owner_id="agent-b",
project_id="project-a",
request_sha256="b" * 64,
)
is None
)
def test_upload_session_is_private_to_its_principal(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
content = b"# Scoped upload\n"
upload_id = store.begin(
filename="scoped.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256=hashlib.sha256(content).hexdigest(),
owner_id="agent-a",
).upload_id
with pytest.raises(UploadError) as error:
store.verified_offsets(upload_id, owner_id="agent-b")
assert error.value.code == "UPLOAD_NOT_FOUND"
with pytest.raises(UploadError) as error:
store.put_chunk(
upload_id,
offset=0,
content=content,
chunk_sha256=hashlib.sha256(content).hexdigest(),
owner_id="agent-b",
)
assert error.value.code == "UPLOAD_NOT_FOUND"
def test_staged_extraction_is_immutable_and_principal_scoped(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
store.save_extraction(EXTRACTION, owner_id="agent-a")
assert store.get_extraction(EXTRACTION.extraction_id, owner_id="agent-a") == EXTRACTION
assert store.get_extraction(EXTRACTION.extraction_id, owner_id="agent-b") is None
with pytest.raises(UploadError) as error:
store.save_extraction(EXTRACTION, owner_id="agent-b")
assert error.value.code == "EXTRACTION_STORE_CONFLICT"
def test_artifact_access_is_scoped_to_principal_and_project(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
content = b"private project source"
digest = hashlib.sha256(content).hexdigest()
upload_id = store.begin(
filename="source.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256=digest,
owner_id="agent-a",
project_id="project-a",
).upload_id
store.put_chunk(
upload_id,
offset=0,
content=content,
chunk_sha256=digest,
owner_id="agent-a",
)
store.finalize(upload_id, owner_id="agent-a")
assert (
store.artifact_media_type(digest, owner_id="agent-a", project_id="project-a")
== "text/markdown"
)
with store.open_artifact(digest, owner_id="agent-a", project_id="project-a") as stream:
assert stream.read() == content
with pytest.raises(UploadError):
store.open_artifact(digest, owner_id="agent-b", project_id="project-a")
with pytest.raises(UploadError):
store.open_artifact(digest, owner_id="agent-a", project_id="project-b")
def test_changed_retry_at_same_offset_is_rejected(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
upload_id = _begin(store, b"abc")
store.put_chunk(
upload_id,
offset=0,
content=b"abc",
chunk_sha256=hashlib.sha256(b"abc").hexdigest(),
now=1_001,
)
with pytest.raises(UploadError, match="different bytes") as error:
store.put_chunk(
upload_id,
offset=0,
content=b"xyz",
chunk_sha256=hashlib.sha256(b"xyz").hexdigest(),
now=1_002,
)
assert error.value.code == "IDEMPOTENCY_CONFLICT"
def test_chunk_and_complete_artifact_digests_are_verified(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
content = b"verified bytes"
digest = hashlib.sha256(content).hexdigest()
upload_id = store.begin(
filename="verified.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256="f" * 64,
).upload_id
with pytest.raises(UploadError) as error:
store.put_chunk(
upload_id,
offset=0,
content=content,
chunk_sha256="0" * 64,
)
assert error.value.code == "CHUNK_DIGEST_MISMATCH"
store.put_chunk(upload_id, offset=0, content=content, chunk_sha256=digest)
with pytest.raises(UploadError) as error:
store.finalize(upload_id)
assert error.value.code == "ARTIFACT_DIGEST_MISMATCH"
with pytest.raises(UploadError) as error:
store.open_artifact("f" * 64)
assert error.value.code == "ARTIFACT_NOT_FOUND"
def test_interrupted_upload_does_not_publish_an_artifact(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
content = b"a" * (CHUNK_BYTES + 1)
digest = hashlib.sha256(content).hexdigest()
upload_id = store.begin(
filename="partial.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256=digest,
owner_id="agent-a",
project_id="project-a",
).upload_id
first_chunk = content[:CHUNK_BYTES]
store.put_chunk(
upload_id,
offset=0,
content=first_chunk,
chunk_sha256=hashlib.sha256(first_chunk).hexdigest(),
owner_id="agent-a",
)
with pytest.raises(UploadError) as error:
store.finalize(upload_id, owner_id="agent-a")
assert error.value.code == "UPLOAD_INCOMPLETE"
with pytest.raises(UploadError) as error:
store.open_artifact(digest, owner_id="agent-a", project_id="project-a")
assert error.value.code == "ARTIFACT_NOT_FOUND"
def test_symlinked_upload_paths_are_not_followed(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
outside = tmp_path / "outside"
outside.mkdir()
sensitive = outside / "chunk"
sensitive.write_bytes(b"data")
forged_id = "A" * 24
try:
(store.upload_root / forged_id).symlink_to(outside, target_is_directory=True)
except OSError as error:
pytest.skip(f"directory symlinks are unavailable: {error}")
with pytest.raises(UploadError) as error:
store.verified_offsets(forged_id)
assert error.value.code == "UPLOAD_NOT_FOUND"
upload_id = _begin(store, b"data")
chunk_link = store.upload_root / upload_id / f"{0:020d}.chunk"
chunk_link.symlink_to(sensitive)
assert store.verified_offsets(upload_id, now=1_001) == ()
with pytest.raises(UploadError) as error:
store.finalize(upload_id, now=1_001)
assert error.value.code == "UPLOAD_INCOMPLETE"
assert sensitive.read_bytes() == b"data"
def test_upload_requires_order_complete_digest_and_utf8(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
content = b"a" * (CHUNK_BYTES + 1)
upload_id = _begin(store, content)
final_chunk = content[CHUNK_BYTES:]
with pytest.raises(UploadError, match="in order"):
store.put_chunk(
upload_id,
offset=CHUNK_BYTES,
content=final_chunk,
chunk_sha256=hashlib.sha256(final_chunk).hexdigest(),
now=1_001,
)
with pytest.raises(UploadError) as error:
store.finalize(upload_id, now=1_002)
assert error.value.code == "UPLOAD_INCOMPLETE"
invalid_bytes = b"\xff"
invalid_id = _begin(store, invalid_bytes)
store.put_chunk(
invalid_id,
offset=0,
content=invalid_bytes,
chunk_sha256=hashlib.sha256(invalid_bytes).hexdigest(),
now=1_001,
)
with pytest.raises(UploadError, match="UTF-8") as error:
store.finalize(invalid_id, now=1_002)
assert error.value.code == "TEXT_ENCODING_INVALID"
def test_rejects_unsupported_types_expiry_and_malicious_ids(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
with pytest.raises(UploadError) as error:
store.begin(
filename="payload.exe",
media_type="application/octet-stream",
byte_length=4,
expected_sha256=hashlib.sha256(b"data").hexdigest(),
now=1_000,
)
assert error.value.code == "UNSUPPORTED_MEDIA_TYPE"
upload_id = _begin(store, b"data")
with pytest.raises(UploadError) as error:
store.verified_offsets(upload_id, now=1_601)
assert error.value.code == "UPLOAD_EXPIRED"
with pytest.raises(UploadError) as error:
store.verified_offsets("../../outside", now=1_001)
assert error.value.code == "UPLOAD_NOT_FOUND"
def test_pdf_upload_requires_matching_suffix_and_pdf_signature(tmp_path: Path) -> None:
store = LocalArtifactStore(tmp_path)
payload = b"%PDF-fixture"
digest = hashlib.sha256(payload).hexdigest()
with pytest.raises(UploadError) as error:
store.begin(
filename="source.txt",
media_type="application/pdf",
byte_length=len(payload),
expected_sha256=digest,
)
assert error.value.code == "UNSUPPORTED_MEDIA_TYPE"
upload_id = store.begin(
filename="source.pdf",
media_type="application/pdf",
byte_length=len(payload),
expected_sha256=digest,
).upload_id
store.put_chunk(upload_id, offset=0, content=payload, chunk_sha256=digest)
assert store.finalize(upload_id).media_type == "application/pdf"
invalid = b"not pdf"
invalid_digest = hashlib.sha256(invalid).hexdigest()
invalid_id = store.begin(
filename="invalid.pdf",
media_type="application/pdf",
byte_length=len(invalid),
expected_sha256=invalid_digest,
).upload_id
store.put_chunk(invalid_id, offset=0, content=invalid, chunk_sha256=invalid_digest)
with pytest.raises(UploadError) as error:
store.finalize(invalid_id)
assert error.value.code == "PDF_INVALID"
def test_identical_artifact_bytes_dedupe_without_collapsing_upload_receipts(
tmp_path: Path,
) -> None:
store = LocalArtifactStore(tmp_path)
content = b"Shared source content."
digest = hashlib.sha256(content).hexdigest()
first_id = store.begin(
filename="shared.md",
media_type="text/markdown",
byte_length=len(content),
expected_sha256=digest,
owner_id="agent-shared",
project_id="project-a",
).upload_id
second_session = store.begin(
filename="second-copy.txt",
media_type="text/plain",
byte_length=len(content),
expected_sha256=digest,
owner_id="agent-shared",
project_id="project-b",
now=1_000,
)
for upload_id in (first_id, second_session.upload_id):
store.put_chunk(
upload_id,
offset=0,
content=content,
chunk_sha256=digest,
owner_id="agent-shared",
now=1_001,
)
first = store.finalize(first_id, owner_id="agent-shared", now=1_002)
second = store.finalize(second_session.upload_id, owner_id="agent-shared", now=1_002)
assert first.sha256 == second.sha256
assert first.byte_length == second.byte_length == len(content)
for project_id in ("project-a", "project-b"):
with store.open_artifact(
digest, owner_id="agent-shared", project_id=project_id
) as artifact:
assert artifact.read() == content
with pytest.raises(UploadError) as error:
store.open_artifact(digest, owner_id="agent-shared", project_id="project-c")
assert error.value.code == "ARTIFACT_NOT_FOUND"