Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m43s
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a119cf-56d0-7fa3-840b-eb8732e1db0b
153 lines
8 KiB
Python
153 lines
8 KiB
Python
"""UC-008 synthetic acceptance; contains no private ReichelAg notes."""
|
|
|
|
import base64
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
|
|
from coulomb_social.apps.members.models import Member, User
|
|
from coulomb_social.apps.spaces import pageops
|
|
from coulomb_social.apps.spaces.models import SpaceMembership
|
|
from coulomb_social.apps.spaces.services import create_space, put_space_page
|
|
|
|
|
|
@pytest.mark.usecase("CSOC-UC-008")
|
|
@pytest.mark.django_db
|
|
def test_full_protocol_duplicate_edit_and_clipboard_control(tmp_path, settings, client):
|
|
settings.CONTENT_ROOT = str(tmp_path / "content")
|
|
user = User.objects.create_user(username="writer")
|
|
member = Member.objects.create(
|
|
user=user, tenant_id="tenant:a", user_engine_user_id="writer",
|
|
issuer="https://issuer.test", subject="writer",
|
|
)
|
|
space = create_space(member, title="Synthetic weekly calls", slug="weekly-calls")
|
|
scenario = json.loads((Path(__file__).parent / "usecases/fixtures/reichelag-weekly-call.json")
|
|
.read_text(encoding="utf-8"))
|
|
body = scenario["previous_body"]
|
|
put_space_page(member, space, "previous-call", title="Previous call", body=body)
|
|
client.force_login(user)
|
|
response = client.post(reverse("spaces:page_copy", kwargs={
|
|
"slug": space.slug, "page_slug": "previous-call",
|
|
}), {"dest_space": space.slug, "dest_slug": "next-call", "title": "Next call"})
|
|
assert response.status_code == 302
|
|
assert pageops.read_page(space.slug, "next-call").body == body
|
|
assert pageops.read_page(space.slug, "previous-call").body == body
|
|
edited = scenario["next_body"]
|
|
response = client.post(reverse("spaces:page_edit", kwargs={
|
|
"slug": space.slug, "page_slug": "next-call",
|
|
}), {"title": "Next call", "slug": "next-call", "body": edited,
|
|
"revision": pageops.read_page(space.slug, "next-call").revision})
|
|
assert response.status_code == 302
|
|
assert pageops.read_page(space.slug, "previous-call").body == body
|
|
response = client.get(reverse("spaces:detail", kwargs={"slug": space.slug}) + "?page=next-call")
|
|
assert response.status_code == 200
|
|
assert b"Copy content to clipboard" in response.content
|
|
assert b"Duplicate page" in response.content
|
|
assert b'id="page-content"' in response.content
|
|
assert b"Follow-up point" in response.content
|
|
assert b"spaces/copy-content.js" in response.content
|
|
assert b"Completed point" not in response.content
|
|
|
|
# A reader can export what they can read, without getting authoring controls.
|
|
membership = SpaceMembership.objects.get(space=space, member=member)
|
|
membership.role = "viewer"
|
|
membership.save()
|
|
response = client.get(reverse("spaces:detail", kwargs={"slug": space.slug}) + "?page=next-call")
|
|
assert b"Copy content to clipboard" in response.content
|
|
assert b"Duplicate page" not in response.content
|
|
|
|
unavailable = client.get(
|
|
reverse("spaces:detail", kwargs={"slug": space.slug}) + "?page=missing"
|
|
)
|
|
assert b"Copy content to clipboard" not in unavailable.content
|
|
|
|
|
|
@pytest.mark.usecase("CSOC-UC-008")
|
|
@pytest.mark.django_db
|
|
def test_protocol_naming_rename_and_collision_safety(tmp_path, settings, client):
|
|
settings.CONTENT_ROOT = str(tmp_path / "content")
|
|
user = User.objects.create_user(username="protocol-writer")
|
|
member = Member.objects.create(
|
|
user=user, tenant_id="tenant:synthetic", user_engine_user_id="protocol-writer",
|
|
issuer="https://issuer.test", subject="protocol-writer",
|
|
)
|
|
space = create_space(member, title="Synthetic calls", slug="synthetic-calls")
|
|
scenario = json.loads((Path(__file__).parent / "usecases/fixtures/reichelag-weekly-call.json")
|
|
.read_text(encoding="utf-8"))
|
|
image = base64.b64decode(scenario["visual_base64"])
|
|
pageops.assets_dir(space.slug).joinpath("cover.png").write_bytes(image)
|
|
source = pageops.write_page(
|
|
space.slug, "previous-call", title="Previous call", abstractor="Board call",
|
|
body="## Agenda\n\n- Open point\n", visual="assets/cover.png",
|
|
extra_meta={"source_id": "synthetic-record", "source_modified_at": 12345},
|
|
)
|
|
source_bytes = Path(source.path).read_bytes()
|
|
client.force_login(user)
|
|
copy_url = reverse("spaces:page_copy", kwargs={
|
|
"slug": space.slug, "page_slug": source.slug,
|
|
})
|
|
# Enter a title once; the optional filename is generated on the server.
|
|
response = client.post(copy_url, {"title": "Next Weekly Call", "dest_slug": ""})
|
|
assert response.status_code == 302
|
|
assert response.url.endswith("?page=next-weekly-call")
|
|
duplicate = pageops.read_page(space.slug, "next-weekly-call")
|
|
assert duplicate.title == "Next Weekly Call" and duplicate.body == source.body
|
|
visual_path = pageops.space_dir(space.slug) / duplicate.visual
|
|
assert visual_path.read_bytes() == image
|
|
edit_url = reverse("spaces:page_edit", kwargs={
|
|
"slug": space.slug, "page_slug": duplicate.slug,
|
|
})
|
|
fields = {"title": "Renamed call", "slug": source.slug,
|
|
"revision": duplicate.revision,
|
|
"abstractor": duplicate.abstractor, "visual": duplicate.visual,
|
|
"body": duplicate.body}
|
|
response = client.post(edit_url, fields)
|
|
assert response.status_code == 200
|
|
assert b"already exists" in response.content
|
|
assert Path(source.path).read_bytes() == source_bytes
|
|
assert pageops.read_page(space.slug, duplicate.slug).title == duplicate.title
|
|
# A distinct filename renames the Markdown file and the return address.
|
|
fields["slug"] = "renamed-call"
|
|
response = client.post(edit_url, fields)
|
|
assert response.status_code == 302 and response.url.endswith("?page=renamed-call")
|
|
assert not pageops.read_page(space.slug, duplicate.slug).exists
|
|
renamed = pageops.read_page(space.slug, "renamed-call")
|
|
assert renamed.title == "Renamed call"
|
|
assert renamed.body == source.body and renamed.abstractor == source.abstractor
|
|
assert renamed.visual == duplicate.visual and visual_path.is_file()
|
|
assert renamed.meta["source_id"] == "synthetic-record"
|
|
assert renamed.meta["source_modified_at"] == "12345"
|
|
assert Path(source.path).read_bytes() == source_bytes
|
|
# Changing title alone preserves the filename and imported source metadata.
|
|
fields.update(title="Corrected title", slug="renamed-call", revision=renamed.revision)
|
|
response = client.post(reverse("spaces:page_edit", kwargs={
|
|
"slug": space.slug, "page_slug": "renamed-call",
|
|
}), fields)
|
|
assert response.status_code == 302
|
|
renamed = pageops.read_page(space.slug, "renamed-call")
|
|
assert renamed.title == "Corrected title" and renamed.meta["source_id"] == "synthetic-record"
|
|
# Explicit copy filename overrides the title-derived name.
|
|
response = client.post(copy_url, {"title": "Another call", "dest_slug": "custom-name"})
|
|
assert response.status_code == 302 and response.url.endswith("?page=custom-name")
|
|
# Duplicate collision leaves the existing file and form input intact.
|
|
protected = Path(renamed.path).read_bytes()
|
|
response = client.post(copy_url, {"title": "Renamed call", "dest_slug": ""})
|
|
assert response.status_code == 200 and b"already exists" in response.content
|
|
assert b'value="Renamed call"' in response.content
|
|
assert Path(renamed.path).read_bytes() == protected
|
|
# Renaming to index is refused; changing an index title cannot move its file.
|
|
fields["slug"] = "index"
|
|
fields["revision"] = renamed.revision
|
|
response = client.post(reverse("spaces:page_edit", kwargs={
|
|
"slug": space.slug, "page_slug": renamed.slug,
|
|
}), fields)
|
|
assert response.status_code == 200 and b"already exists" in response.content
|
|
response = client.post(reverse("spaces:page_edit", kwargs={
|
|
"slug": space.slug, "page_slug": "index",
|
|
}), {"title": "Index title", "slug": "move-index", "body": "Index notes\n",
|
|
"revision": pageops.read_page(space.slug, "index").revision})
|
|
assert response.status_code == 302
|
|
assert pageops.read_page(space.slug, "index").title == "Index title"
|
|
assert not pageops.read_page(space.slug, "move-index").exists
|