Files
devplacepy/tests/api/sync/attachments.py
T
retoorandClaude Sonnet 5.5 02ab747cd0
DevPlace CI / test (push) Failing after 44m45s
Code Farm: per-plot auto-replant pause and crop-lock check; community sync and node work
Continuous Delivery could never be turned off once a crop was planted. Add a
one-shot per-plot "stop after harvest" flag (POST /game/plot/stop, Devii tool
game_plot_stop, game_plots.stop_after_harvest), cleared by the next plant,
autoreplant claim or Refactor. Autoreplant now also respects crop locks through
the shared _crop_lock predicate. Plan and measurements in happyplanting.md.

Also includes the pending community sync, node network and related work in the
tree; the full suite passed (4999) with all of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 02:23:43 +02:00

81 lines
4.1 KiB
Python

# retoor <retoor@molodetz.nl>
import base64
from pathlib import Path
from devplacepy.config import ATTACHMENTS_DIR
from devplacepy.database import get_table
from tests.api.sync.conftest import PNG_1X1, attachment_record, seed_world, sync_ok
def local(origin, kind, origin_uid):
return get_table("sync_links").find_one(origin=origin, kind=kind, origin_uid=origin_uid)["local_uid"]
def test_an_attachment_is_stored_and_linked_to_its_target(client, admin, origin):
seed_world(client, admin, origin)
result = sync_ok(client, admin, "attachments", origin, [attachment_record()])["results"][0]
assert result["status"] == "created"
row = get_table("attachments").find_one(uid=result["local_uid"])
assert (row["target_type"], row["target_uid"]) == ("post", local(origin, "posts", "p1"))
assert row["user_uid"] == local(origin, "users", "u1") and row["mime_type"] == "image/png"
assert (Path(ATTACHMENTS_DIR) / row["directory"] / row["stored_name"]).read_bytes() == base64.b64decode(PNG_1X1)
assert row["created_at"].startswith("2025-01-05")
def test_replay_is_unchanged_and_a_content_change_is_refused(client, admin, origin):
seed_world(client, admin, origin)
sync_ok(client, admin, "attachments", origin, [attachment_record()])
assert sync_ok(client, admin, "attachments", origin, [attachment_record()])["results"][0]["status"] == "unchanged"
other = base64.b64encode(b"GIF89a" + b"x" * 40).decode()
changed = sync_ok(client, admin, "attachments", origin, [attachment_record(content=other, filename="b.gif", updated_at="2025-02-01T00:00:00Z")])["results"][0]
assert changed["status"] == "rejected" and changed["reason"] == "immutable"
def test_tombstone_soft_deletes_and_restores(client, admin, origin):
seed_world(client, admin, origin)
created = sync_ok(client, admin, "attachments", origin, [attachment_record()])["results"][0]
gone = attachment_record(updated_at="2025-02-01T00:00:00Z", deleted=True)
assert sync_ok(client, admin, "attachments", origin, [gone])["results"][0]["status"] == "updated"
assert get_table("attachments").find_one(uid=created["local_uid"])["deleted_by"] == "sync"
back = attachment_record(updated_at="2025-03-01T00:00:00Z")
sync_ok(client, admin, "attachments", origin, [back])
assert get_table("attachments").find_one(uid=created["local_uid"])["deleted_at"] is None
def test_validation_and_type_rules(client, admin, origin):
seed_world(client, admin, origin)
body = sync_ok(
client, admin, "attachments", origin,
[
attachment_record("a1", content="not base64!!"),
attachment_record("a2", content=""),
attachment_record("a3", filename="run.exe", content=base64.b64encode(b"MZ" + b"0" * 10).decode()),
attachment_record("a4", filename="../../etc/passwd.png"),
],
)
statuses = [item["status"] for item in body["results"]]
assert statuses[:3] == ["rejected", "rejected", "rejected"]
stored = get_table("attachments").find_one(uid=body["results"][3].get("local_uid") or "x")
assert statuses[3] == "created" and stored["original_filename"] == "passwd.png"
def test_an_oversize_file_is_refused(client, admin, origin):
from devplacepy.services.sync.spec import LIMITS
seed_world(client, admin, origin)
big = base64.b64encode(b"\x00" * (LIMITS.max_attachment_bytes + 1)).decode()
result = sync_ok(client, admin, "attachments", origin, [attachment_record(content=big)])["results"][0]
assert result["status"] == "rejected" and "exceeds" in result["errors"][0]
def test_an_unknown_target_waits_and_is_released_when_the_target_arrives(client, admin, origin):
from tests.api.sync.conftest import post_record, user_record
sync_ok(client, admin, "users", origin, [user_record("u1", "attuser1")])
waiting = sync_ok(client, admin, "attachments", origin, [attachment_record(target="p1")])["results"][0]
assert waiting["status"] == "deferred"
body = sync_ok(client, admin, "posts", origin, [post_record("p1", "u1")])
assert body["released"] == {"created": 1}
assert get_table("attachments").count(target_uid=local(origin, "posts", "p1")) == 1