2026-08-01 11:39:55 +08:00
|
|
|
"""Tests for the message-type normalization parser (Task 1.6).
|
|
|
|
|
|
|
|
|
|
The parser must be total — it never raises on weird input — because every
|
|
|
|
|
incoming WeChat record runs through it during a scrape, and one malformed
|
|
|
|
|
type code must not abort the whole pull. Unknown codes pass through as
|
|
|
|
|
``"unknown"`` so the message is still ingested (and recoverable via
|
|
|
|
|
``raw_json``); only the ``-2`` date-divider code maps to ``"divider"``,
|
|
|
|
|
which the scraper uses as its drop signal.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from app.scraper.parsers import normalize_wechat_type
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"raw,expected",
|
|
|
|
|
[
|
2026-08-01 13:37:42 +08:00
|
|
|
# Phase-0 confirmed codes (see docs/superpowers/specs/phase0-findings.md §2).
|
2026-08-01 11:39:55 +08:00
|
|
|
(1, "text"),
|
2026-08-01 13:37:42 +08:00
|
|
|
(-2, "divider"), # date separator — NOT a real message (scraper drops it)
|
|
|
|
|
(2, "image"), # image / sticker (webp) — file/fileTh are direct URLs
|
|
|
|
|
(3, "image_legacy"), # old image format — file is an opaque ID (needs another endpoint)
|
|
|
|
|
(4, "video"), # mp4 — file is a direct URL
|
|
|
|
|
(10, "system"), # system / link / card / invite — non-media, skip download
|
|
|
|
|
# Still-unconfirmed codes — pass through as "unknown", never dropped.
|
|
|
|
|
# (6, 9, 13, 15, 21, 22 — voice/file/transfer/redpacket/quote/etc; raw_json retains the code.)
|
|
|
|
|
(6, "unknown"),
|
|
|
|
|
(9, "unknown"),
|
|
|
|
|
(34, "unknown"), # voice (legacy WeChat code, not yet seen in yunke)
|
2026-08-01 11:39:55 +08:00
|
|
|
(49, "unknown"), # file / link / transfer
|
|
|
|
|
(10000, "unknown"),
|
|
|
|
|
# Stringified ints (the upstream API may send either form).
|
|
|
|
|
("1", "text"),
|
|
|
|
|
("-2", "divider"),
|
2026-08-01 13:37:42 +08:00
|
|
|
("2", "image"),
|
|
|
|
|
("3", "image_legacy"),
|
|
|
|
|
("4", "video"),
|
|
|
|
|
("10", "system"),
|
2026-08-01 11:39:55 +08:00
|
|
|
("34", "unknown"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_normalize_wechat_type_maps_known_and_unknown(raw, expected):
|
|
|
|
|
assert normalize_wechat_type(raw) == expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_normalize_wechat_type_never_raises():
|
|
|
|
|
"""Whatever we throw at it, it returns a string — never raises."""
|
|
|
|
|
for raw in (None, "", "junk", "1.5", [], {}, object(), float("nan")):
|
|
|
|
|
out = normalize_wechat_type(raw)
|
|
|
|
|
assert isinstance(out, str)
|
|
|
|
|
assert out == "unknown"
|