Private
Public Access
feat(twitter_threads): scaffold package + error types + typed dataclasses
TIER-2 READ AGENTS.md, conductor/workflow.md, conductor/edit_workflow.md, conductor/tier2/githooks/forbidden-files.txt, conductor/tracks/tier2_leak_prevention_20260620/spec.md (archived -> read docs/reports/2026-06-15/TRACK_COMPLETION_tier2_leak_prevention_20260620.md), conductor/code_styleguides/data_oriented_design.md, conductor/code_styleguides/python.md, conductor/code_styleguides/type_aliases.md, conductor/code_styleguides/error_handling.md, conductor/product-guidelines.md (Core Value via aggregate_directives) before Phase 1 (scaffold + error_types + dataclasses). scripts/twitter_threads/__init__.py (namespace docstring); error_types.py (ErrorInfo + make_error + PostMetrics/PostData/ThreadData, frozen+slots); tests/test_twitter_threads_types.py (9 contract tests).
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
"""Tests for scripts.twitter_threads.error_types dataclass contract."""
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.twitter_threads.error_types import ErrorInfo, make_error, PostMetrics, PostData, ThreadData
|
||||
|
||||
|
||||
def test_error_info_fields() -> None:
|
||||
e = ErrorInfo(kind="ParseError", source="fetch", detail="bad html")
|
||||
assert e.kind == "ParseError"
|
||||
assert e.source == "fetch"
|
||||
assert e.detail == "bad html"
|
||||
|
||||
|
||||
def test_make_error_factory() -> None:
|
||||
e = make_error("HttpError", "download_media", "404")
|
||||
assert isinstance(e, ErrorInfo)
|
||||
assert e.kind == "HttpError"
|
||||
assert e.source == "download_media"
|
||||
assert e.detail == "404"
|
||||
|
||||
|
||||
def test_error_info_frozen() -> None:
|
||||
e = ErrorInfo(kind="x", source="y", detail="z")
|
||||
with pytest.raises(dataclasses.FrozenInstanceError):
|
||||
e.kind = "w"
|
||||
|
||||
|
||||
def test_error_info_slots() -> None:
|
||||
e = ErrorInfo(kind="x", source="y", detail="z")
|
||||
assert not hasattr(e, "__dict__")
|
||||
|
||||
|
||||
def test_post_metrics() -> None:
|
||||
m = PostMetrics(reply_count=1, repost_count=2, like_count=3, view_count=None)
|
||||
assert m.reply_count == 1
|
||||
assert m.repost_count == 2
|
||||
assert m.like_count == 3
|
||||
assert m.view_count is None
|
||||
m2 = PostMetrics(reply_count=10, repost_count=20, like_count=30, view_count=100)
|
||||
assert m2.view_count == 100
|
||||
|
||||
|
||||
def test_post_data_fields() -> None:
|
||||
metrics = PostMetrics(reply_count=1, repost_count=2, like_count=42, view_count=None)
|
||||
p = PostData(
|
||||
post_id="123",
|
||||
author="Tim",
|
||||
handle="NOTimothyLottes",
|
||||
text="hi",
|
||||
timestamp="2024-02-01T00:00:00Z",
|
||||
media_urls=("https://x/i.jpg",),
|
||||
reply_to_id=None,
|
||||
quote_of_id=None,
|
||||
metrics=metrics,
|
||||
)
|
||||
assert p.post_id == "123"
|
||||
assert p.author == "Tim"
|
||||
assert p.handle == "NOTimothyLottes"
|
||||
assert p.text == "hi"
|
||||
assert p.timestamp == "2024-02-01T00:00:00Z"
|
||||
assert isinstance(p.media_urls, tuple)
|
||||
assert p.media_urls == ("https://x/i.jpg",)
|
||||
assert p.reply_to_id is None
|
||||
assert p.quote_of_id is None
|
||||
assert p.metrics.like_count == 42
|
||||
|
||||
|
||||
def test_post_data_slots_frozen() -> None:
|
||||
metrics = PostMetrics(reply_count=1, repost_count=2, like_count=3, view_count=None)
|
||||
p = PostData(
|
||||
post_id="123",
|
||||
author="Tim",
|
||||
handle="NOTimothyLottes",
|
||||
text="hi",
|
||||
timestamp="2024-02-01T00:00:00Z",
|
||||
media_urls=(),
|
||||
reply_to_id=None,
|
||||
quote_of_id=None,
|
||||
metrics=metrics,
|
||||
)
|
||||
assert not hasattr(p, "__dict__")
|
||||
with pytest.raises(dataclasses.FrozenInstanceError):
|
||||
p.post_id = "456"
|
||||
|
||||
|
||||
def test_thread_data_fields() -> None:
|
||||
metrics = PostMetrics(reply_count=1, repost_count=2, like_count=3, view_count=None)
|
||||
p = PostData(
|
||||
post_id="123",
|
||||
author="Tim",
|
||||
handle="NOTimothyLottes",
|
||||
text="hi",
|
||||
timestamp="2024-02-01T00:00:00Z",
|
||||
media_urls=(),
|
||||
reply_to_id=None,
|
||||
quote_of_id=None,
|
||||
metrics=metrics,
|
||||
)
|
||||
t = ThreadData(root_post_id="123", posts=(p,), source_url="https://x.com/NOTimothyLottes/status/123")
|
||||
assert t.root_post_id == "123"
|
||||
assert isinstance(t.posts, tuple)
|
||||
assert len(t.posts) == 1
|
||||
assert t.posts[0] is p
|
||||
assert t.source_url == "https://x.com/NOTimothyLottes/status/123"
|
||||
|
||||
|
||||
def test_thread_data_slots_frozen() -> None:
|
||||
metrics = PostMetrics(reply_count=1, repost_count=2, like_count=3, view_count=None)
|
||||
p = PostData(
|
||||
post_id="123",
|
||||
author="Tim",
|
||||
handle="NOTimothyLottes",
|
||||
text="hi",
|
||||
timestamp="2024-02-01T00:00:00Z",
|
||||
media_urls=(),
|
||||
reply_to_id=None,
|
||||
quote_of_id=None,
|
||||
metrics=metrics,
|
||||
)
|
||||
t = ThreadData(root_post_id="123", posts=(p,), source_url="https://x.com/x/x/123")
|
||||
assert not hasattr(t, "__dict__")
|
||||
with pytest.raises(dataclasses.FrozenInstanceError):
|
||||
t.source_url = "https://other"
|
||||
Reference in New Issue
Block a user