119 lines
5.5 KiB
Python
119 lines
5.5 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from devplacepy.database import get_table
|
|
from tests.api.sync.conftest import post_record, sync_ok, unique, user_record
|
|
|
|
|
|
def seed_author(client, admin, origin, uid="u1"):
|
|
body = sync_ok(client, admin, "users", origin, [user_record(uid, unique("author"))])
|
|
return body["results"][0]["local_uid"]
|
|
|
|
|
|
def test_unknown_author_is_rejected_with_reason(client, admin, origin):
|
|
body = sync_ok(client, admin, "posts", origin, [post_record(author="missing")])
|
|
result = body["results"][0]
|
|
assert result["status"] == "rejected" and result["reason"] == "unknown_author"
|
|
assert get_table("sync_links").count(origin=origin) == 0
|
|
|
|
|
|
def test_create_normalizes_and_has_no_side_effects(client, admin, origin):
|
|
author = seed_author(client, admin, origin)
|
|
notifications_before = get_table("notifications").count()
|
|
body = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", title=" Hello World ", content="line one\r\nline two ok", topic="nonsense", stars=50)],
|
|
)
|
|
result = body["results"][0]
|
|
assert result["status"] == "created" and result["ignored"] == ["stars"]
|
|
row = get_table("posts").find_one(uid=result["local_uid"])
|
|
assert row["user_uid"] == author
|
|
assert row["title"] == "Hello World"
|
|
assert row["content"] == "line one\nline two ok"
|
|
assert row["topic"] == "random"
|
|
assert row["stars"] == 0
|
|
assert row["created_at"] == "2025-01-02T00:00:00+00:00"
|
|
assert row["slug"].endswith("hello-world")
|
|
assert row["deleted_at"] is None
|
|
assert get_table("notifications").count() == notifications_before
|
|
assert get_table("users").find_one(uid=author)["xp"] == 0
|
|
|
|
|
|
def test_replay_update_stale_and_tombstone(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
created = sync_ok(client, admin, "posts", origin, [post_record("p1")])["results"][0]
|
|
assert sync_ok(client, admin, "posts", origin, [post_record("p1")])["results"][0]["status"] == "unchanged"
|
|
edited = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="edited synced content", updated_at="2025-03-01T00:00:00Z")],
|
|
)["results"][0]
|
|
assert edited["status"] == "updated" and edited["local_uid"] == created["local_uid"]
|
|
assert get_table("posts").find_one(uid=created["local_uid"])["content"] == "edited synced content"
|
|
stale = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="older content here", updated_at="2025-01-05T00:00:00Z")],
|
|
)["results"][0]
|
|
assert stale["status"] == "stale"
|
|
deleted = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="edited synced content", updated_at="2025-04-01T00:00:00Z", deleted=True)],
|
|
)["results"][0]
|
|
assert deleted["status"] == "updated"
|
|
row = get_table("posts").find_one(uid=created["local_uid"])
|
|
assert row["deleted_at"] and row["deleted_by"] == "sync"
|
|
revived = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="edited synced content", updated_at="2025-05-01T00:00:00Z")],
|
|
)["results"][0]
|
|
assert revived["status"] == "updated"
|
|
assert get_table("posts").find_one(uid=created["local_uid"])["deleted_at"] is None
|
|
|
|
|
|
def test_locally_moderated_post_is_not_resurrected(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
created = sync_ok(client, admin, "posts", origin, [post_record("p1")])["results"][0]
|
|
get_table("posts").update(
|
|
{"uid": created["local_uid"], "deleted_at": "2025-02-01T00:00:00+00:00", "deleted_by": admin["uid"]},
|
|
["uid"],
|
|
)
|
|
again = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="remote edit arrives", updated_at="2025-03-01T00:00:00Z")],
|
|
)["results"][0]
|
|
assert again["status"] == "unchanged" and again["reason"] == "locally_deleted"
|
|
assert get_table("posts").find_one(uid=created["local_uid"])["content"] != "remote edit arrives"
|
|
|
|
|
|
def test_purged_local_copy_is_not_recreated(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
created = sync_ok(client, admin, "posts", origin, [post_record("p1")])["results"][0]
|
|
get_table("posts").delete(uid=created["local_uid"])
|
|
again = sync_ok(
|
|
client, admin, "posts", origin,
|
|
[post_record("p1", content="changed after purge", updated_at="2025-03-01T00:00:00Z")],
|
|
)["results"][0]
|
|
assert again["status"] == "rejected" and again["reason"] == "purged_locally"
|
|
assert get_table("posts").count(uid=created["local_uid"]) == 0
|
|
|
|
|
|
def test_invalid_post_reports_field_errors(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
body = sync_ok(client, admin, "posts", origin, [post_record("p1", content="short")])
|
|
result = body["results"][0]
|
|
assert result["status"] == "rejected" and result["reason"] == "invalid"
|
|
assert any(error.startswith("content:") for error in result["errors"])
|
|
|
|
|
|
def test_future_timestamps_are_clamped(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
body = sync_ok(client, admin, "posts", origin, [post_record("p1", created_at="2999-01-01T00:00:00Z")])
|
|
row = get_table("posts").find_one(uid=body["results"][0]["local_uid"])
|
|
assert row["created_at"] < "2100"
|
|
|
|
|
|
def test_post_shows_up_on_its_detail_page(client, admin, origin):
|
|
seed_author(client, admin, origin)
|
|
body = sync_ok(client, admin, "posts", origin, [post_record("p1", title="Visible synced post")])
|
|
slug = get_table("posts").find_one(uid=body["results"][0]["local_uid"])["slug"]
|
|
page = client.get(f"/posts/{slug}")
|
|
assert page.status_code == 200 and "Visible synced post" in page.text
|