Skip to content

Commit 8a15184

Browse files
committed
test(api2): test "source_conversation_truncated" event
Thanks to ChatGPT.[^1] [^1]: https://chatgpt.com/share/6920be5d-c878-800f-aac4-0aecaad8399f
1 parent c75a411 commit 8a15184

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

securedrop/tests/test_journalist_api2.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,3 +1106,104 @@ def test_api_minor_versions(journalist_app, journalist_api_token, test_files, mi
11061106

11071107
else:
11081108
assert "events" not in resp.json
1109+
1110+
1111+
def test_api2_source_conversation_truncated(
1112+
journalist_app,
1113+
journalist_api_token,
1114+
test_files,
1115+
):
1116+
"""
1117+
Test processing of the "source_conversation_truncated" event.
1118+
Items with interaction_count <= upper_bound must be deleted.
1119+
Items with interaction_count > upper_bound must remain.
1120+
"""
1121+
with journalist_app.test_client() as app:
1122+
source = test_files["source"]
1123+
1124+
# Ensure we have submissions/replies and interaction_count fields
1125+
assert len(test_files["submissions"]) >= 1
1126+
assert len(test_files["replies"]) >= 1
1127+
1128+
# Fetch index to get current versions and interaction counts
1129+
index = app.get(
1130+
url_for("api2.index"),
1131+
headers=get_api_headers(journalist_api_token),
1132+
)
1133+
assert index.status_code == 200
1134+
1135+
# Build a map of item_uuid -> interaction_count
1136+
item_uuids = [item.uuid for item in (test_files["submissions"] + test_files["replies"])]
1137+
1138+
batch_resp = app.post(
1139+
url_for("api2.data"),
1140+
json={"items": item_uuids},
1141+
headers=get_api_headers(journalist_api_token),
1142+
)
1143+
assert batch_resp.status_code == 200
1144+
data = batch_resp.json
1145+
1146+
initial_counts = {
1147+
item_uuid: item["interaction_count"] for item_uuid, item in data["items"].items()
1148+
}
1149+
1150+
# Choose a bound that deletes some but not all items
1151+
# Pick the median interaction_count so we get both outcomes
1152+
sorted_counts = sorted(initial_counts.values())
1153+
upper_bound = sorted_counts[len(sorted_counts) // 2]
1154+
1155+
source_version = index.json["sources"][source.uuid]
1156+
1157+
event = Event(
1158+
id="999001",
1159+
target=SourceTarget(source_uuid=source.uuid, version=source_version),
1160+
type=EventType.SOURCE_CONVERSATION_TRUNCATED,
1161+
data={"upper_bound": upper_bound},
1162+
)
1163+
1164+
response = app.post(
1165+
url_for("api2.data"),
1166+
json={"events": [asdict(event)]},
1167+
headers=get_api_headers(journalist_api_token),
1168+
)
1169+
assert response.status_code == 200
1170+
1171+
status_code, msg = response.json["events"][event.id]
1172+
# Because some deletes may fail (simulated) and some succeed, the handler
1173+
# returns 200 if all succeed, or 207 (MultiStatus) if any fail.
1174+
# The test_files fixtures never cause delete_file_object() to raise,
1175+
# so OK (200) is expected.
1176+
assert status_code in (200, 207)
1177+
1178+
# Verify item-wise results
1179+
returned_items = response.json["items"]
1180+
assert isinstance(returned_items, dict)
1181+
1182+
for item_uuid, count in initial_counts.items():
1183+
if count <= upper_bound:
1184+
# Must be returned as deleted: {uuid: None}
1185+
assert item_uuid in returned_items
1186+
assert returned_items[item_uuid] is None
1187+
# Also confirm removal in DB
1188+
assert (
1189+
Submission.query.filter(Submission.uuid == item_uuid).one_or_none()
1190+
or Reply.query.filter(Reply.uuid == item_uuid).one_or_none()
1191+
) is None
1192+
else:
1193+
# Must not be deleted
1194+
assert (
1195+
Submission.query.filter(Submission.uuid == item_uuid).one_or_none()
1196+
or Reply.query.filter(Reply.uuid == item_uuid).one_or_none()
1197+
) is not None
1198+
1199+
# Source must still exist
1200+
assert Source.query.filter(Source.uuid == source.uuid).one_or_none() is not None
1201+
1202+
# Resubmission must yield "Already Reported" (208)
1203+
res2 = app.post(
1204+
url_for("api2.data"),
1205+
json={"events": [asdict(event)]},
1206+
headers=get_api_headers(journalist_api_token),
1207+
)
1208+
assert res2.status_code == 200
1209+
assert res2.json["events"][event.id][0] == 208

0 commit comments

Comments
 (0)