Files
Threadwell/scripts/twitter/tests/test_models_boundary.py
T

184 lines
6.2 KiB
Python

from __future__ import annotations
import unittest
from pathlib import Path
from twitter.models import (
LegacyManifest,
LegacyThreadData,
MediaItem,
MediaLocation,
MediaManifest,
PostData,
)
class MediaLocationTests(unittest.TestCase):
"""Tests for the MediaLocation boundary dataclass."""
def test_local_round_trip_with_path_bytes_sha(self) -> None:
loc = MediaLocation.from_dict({
"kind": "local",
"local_path": "asset.png",
"bytes": 100,
"sha256": "abc",
})
self.assertEqual(loc.kind, "local")
self.assertEqual(loc.local_path, Path("asset.png"))
self.assertEqual(loc.bytes, 100)
self.assertEqual(loc.sha256, "abc")
self.assertIsNone(loc.url)
self.assertIsNone(loc.checked_at)
self.assertIsNone(loc.checked_status)
def test_remote_round_trip_with_url(self) -> None:
loc = MediaLocation.from_dict({
"kind": "remote",
"url": "https://example.com/asset.png",
})
self.assertEqual(loc.kind, "remote")
self.assertEqual(loc.url, "https://example.com/asset.png")
self.assertIsNone(loc.local_path)
self.assertIsNone(loc.bytes)
self.assertIsNone(loc.sha256)
def test_empty_dict_raises_keyerror(self) -> None:
with self.assertRaises(KeyError):
MediaLocation.from_dict({})
def test_bytes_bool_raises_typeerror(self) -> None:
with self.assertRaisesRegex(TypeError, "bytes"):
MediaLocation.from_dict({
"kind": "local",
"local_path": "/tmp/x",
"bytes": True,
"sha256": "abc",
})
class MediaItemTests(unittest.TestCase):
"""Tests for the MediaItem boundary dataclass."""
def test_image_round_trip_with_locations_tuple(self) -> None:
item = MediaItem.from_dict({
"post_id": "100",
"media_id": "AAA",
"kind": "image",
"locations": [
{"kind": "local", "local_path": "asset.png", "bytes": 100, "sha256": "abc"},
{"kind": "remote", "url": "https://example.com/asset.png"},
],
})
self.assertEqual(item.post_id, "100")
self.assertEqual(item.media_id, "AAA")
self.assertEqual(item.kind, "image")
self.assertIsInstance(item.locations, tuple)
self.assertEqual(len(item.locations), 2)
self.assertEqual(item.locations[0].kind, "local")
self.assertEqual(item.locations[1].kind, "remote")
self.assertIsNone(item.embed)
self.assertIsNone(item.caption)
def test_locations_not_list_raises_typeerror(self) -> None:
with self.assertRaisesRegex(TypeError, "locations"):
MediaItem.from_dict({
"post_id": "p",
"media_id": "m",
"kind": "image",
"locations": "not a list",
})
def test_embed_not_bool_raises_typeerror(self) -> None:
with self.assertRaisesRegex(TypeError, "embed"):
MediaItem.from_dict({
"post_id": "p",
"media_id": "m",
"kind": "image",
"locations": [],
"embed": "true",
})
class MediaManifestTests(unittest.TestCase):
"""Tests for the MediaManifest boundary dataclass."""
def test_minimal_manifest_round_trip(self) -> None:
manifest = MediaManifest.from_dict({
"root_post_id": "100",
"items": [
{
"post_id": "100",
"media_id": "AAA",
"kind": "image",
"locations": [
{"kind": "local", "local_path": "x.png", "bytes": 1, "sha256": "a"},
],
}
],
})
self.assertEqual(manifest.root_post_id, "100")
self.assertIsInstance(manifest.items, tuple)
self.assertEqual(len(manifest.items), 1)
self.assertEqual(manifest.items[0].media_id, "AAA")
self.assertIsNone(manifest.captured_at)
def test_items_not_list_raises_typeerror(self) -> None:
with self.assertRaisesRegex(TypeError, "items"):
MediaManifest.from_dict({"root_post_id": "p", "items": {}})
class LegacyManifestTests(unittest.TestCase):
"""Tests for the LegacyManifest boundary dataclass."""
def test_round_trip_with_threads(self) -> None:
manifest = LegacyManifest.from_dict({
"threads": [
{
"root_post_id": "100",
"posts": [],
"source_url": "https://x.com/example/status/100",
}
],
})
self.assertIsInstance(manifest.threads, tuple)
self.assertEqual(len(manifest.threads), 1)
self.assertEqual(manifest.threads[0].root_post_id, "100")
class LegacyThreadDataTests(unittest.TestCase):
"""Tests for the LegacyThreadData boundary dataclass."""
def test_round_trip_with_posts_tuple(self) -> None:
thread = LegacyThreadData.from_dict({
"root_post_id": "100",
"source_url": "https://x.com/example/status/100",
"posts": [
{
"post_id": "100",
"author": "Example",
"handle": "example",
"text": "Hello",
"timestamp": "2026-08-24 12:00:00",
"media_urls": [],
"reply_to_id": None,
"quote_of_id": None,
"metrics": {
"reply_count": 0,
"repost_count": 0,
"like_count": 0,
"view_count": None,
},
}
],
})
self.assertEqual(thread.root_post_id, "100")
self.assertEqual(thread.source_url, "https://x.com/example/status/100")
self.assertIsInstance(thread.posts, tuple)
self.assertEqual(len(thread.posts), 1)
self.assertIsInstance(thread.posts[0], PostData)
self.assertEqual(thread.posts[0].post_id, "100")
if __name__ == "__main__":
unittest.main()