mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-10 18:07:29 +01:00
The scrub before this one caught the truncated spelling in the document and missed the one in the test, which held the first half of a real Signal uuid and the account name beside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017w1HZ3cZ7neL3rFdQ6VMBA
79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence.
|
|
"""Describing a frame's shape without logging what anybody wrote."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from signal_bot.envelope_shape import shape_of
|
|
from tests.conftest import WATCHED_GROUP, envelope
|
|
|
|
PRIVATE_BODY = "the actual message body"
|
|
|
|
|
|
def test_values_are_replaced_by_their_types() -> None:
|
|
"""The whole point: a log line that cannot leak a message."""
|
|
shape = shape_of(envelope(text=PRIVATE_BODY))
|
|
assert PRIVATE_BODY not in shape
|
|
assert "message: str" in shape
|
|
|
|
|
|
def test_names_numbers_and_uuids_do_not_survive() -> None:
|
|
shape = shape_of(envelope(text="hi", sender=("b1c2d3e4-0000", "somebody")))
|
|
assert "b1c2d3e4" not in shape
|
|
assert "kuhy" not in shape
|
|
assert "+15555550123" not in shape
|
|
|
|
|
|
def test_quoted_text_does_not_survive() -> None:
|
|
shape = shape_of(envelope(text="hi", extra="quote"))
|
|
assert "..." not in shape
|
|
assert "quote: {author: str, id: int, text: str}" in shape
|
|
|
|
|
|
def test_the_group_id_is_a_type_but_its_kind_is_a_value() -> None:
|
|
"""DELIVER vs UPDATE is wire format; the id identifies a conversation."""
|
|
shape = shape_of(envelope(text="hi"))
|
|
assert WATCHED_GROUP not in shape
|
|
assert "groupId: str" in shape
|
|
assert "type: 'DELIVER'" in shape
|
|
|
|
|
|
def test_attachment_content_types_are_kept() -> None:
|
|
"""The one thing this exists to answer, so it must survive redaction."""
|
|
shape = shape_of(envelope(text=None, attachments=("image/jpeg", "video/mp4")))
|
|
assert "contentType: 'image/jpeg'" in shape
|
|
assert "x2]" in shape
|
|
|
|
|
|
def test_an_album_is_summarised_not_repeated() -> None:
|
|
shape = shape_of(envelope(text=None, attachments=("image/jpeg",) * 20))
|
|
assert "x20]" in shape
|
|
assert shape.count("contentType") == 1
|
|
|
|
|
|
def test_an_empty_list_is_shown_as_empty() -> None:
|
|
assert shape_of(json.dumps({"envelope": {"members": []}})) == "{envelope: {members: []}}"
|
|
|
|
|
|
def test_null_is_distinguished_from_absent() -> None:
|
|
"""A caption-less photo may send message: null rather than omitting it."""
|
|
assert "message: null" in shape_of(json.dumps({"envelope": {"message": None}}))
|
|
|
|
|
|
def test_numbers_and_booleans_show_their_type() -> None:
|
|
frame = json.dumps({"envelope": {"timestamp": 1, "urgent": True, "score": 1.5}})
|
|
assert shape_of(frame) == "{envelope: {score: float, timestamp: int, urgent: bool}}"
|
|
|
|
|
|
def test_an_unparseable_frame_says_so() -> None:
|
|
assert shape_of("not json") == "<unparseable>"
|
|
assert shape_of(b"\xff\xfe") == "<unparseable>"
|
|
|
|
|
|
def test_keys_are_sorted_so_two_frames_compare() -> None:
|
|
"""Same shape, different key order, identical description."""
|
|
first = json.dumps({"envelope": {"b": 1, "a": "x"}})
|
|
second = json.dumps({"envelope": {"a": "x", "b": 1}})
|
|
assert shape_of(first) == shape_of(second)
|