import asyncio import tempfile import unittest from argparse import Namespace from pathlib import Path from unittest.mock import AsyncMock, patch import sau_cli class BrowserCliParserTests(unittest.TestCase): def test_build_parser_accepts_xiaohongshu_login(self): parser = sau_cli.build_parser() args = parser.parse_args(["xiaohongshu", "login", "--account", "creator"]) self.assertEqual(args.platform, "xiaohongshu") self.assertEqual(args.action, "login") def test_douyin_upload_video_accepts_desc(self): with tempfile.TemporaryDirectory() as tmp_dir: video_path = Path(tmp_dir) / "demo.mp4" video_path.write_bytes(b"video") parser = sau_cli.build_parser() args = parser.parse_args( [ "douyin", "upload-video", "--account", "creator", "--file", str(video_path), "--title", "标题", "--desc", "视频简介", ] ) self.assertEqual(args.desc, "视频简介") def test_douyin_upload_video_accepts_dual_thumbnail_aspects(self): with tempfile.TemporaryDirectory() as tmp_dir: video_path = Path(tmp_dir) / "demo.mp4" landscape_path = Path(tmp_dir) / "landscape.png" portrait_path = Path(tmp_dir) / "portrait.png" video_path.write_bytes(b"video") landscape_path.write_bytes(b"image") portrait_path.write_bytes(b"image") parser = sau_cli.build_parser() args = parser.parse_args( [ "douyin", "upload-video", "--account", "creator", "--file", str(video_path), "--title", "标题", "--thumbnail-landscape", str(landscape_path), "--thumbnail-portrait", str(portrait_path), ] ) self.assertEqual(args.thumbnail_landscape, landscape_path) self.assertEqual(args.thumbnail_portrait, portrait_path) def test_tencent_upload_video_accepts_dual_thumbnail_aspects(self): with tempfile.TemporaryDirectory() as tmp_dir: video_path = Path(tmp_dir) / "demo.mp4" landscape_path = Path(tmp_dir) / "landscape.png" portrait_path = Path(tmp_dir) / "portrait.png" video_path.write_bytes(b"video") landscape_path.write_bytes(b"image") portrait_path.write_bytes(b"image") parser = sau_cli.build_parser() args = parser.parse_args( [ "tencent", "upload-video", "--account", "creator", "--file", str(video_path), "--title", "标题", "--thumbnail-landscape", str(landscape_path), "--thumbnail-portrait", str(portrait_path), ] ) self.assertEqual(args.thumbnail_landscape, landscape_path) self.assertEqual(args.thumbnail_portrait, portrait_path) def test_kuaishou_upload_note_accepts_title_and_note(self): with tempfile.TemporaryDirectory() as tmp_dir: image_path = Path(tmp_dir) / "1.png" image_path.write_bytes(b"image") parser = sau_cli.build_parser() args = parser.parse_args( [ "kuaishou", "upload-note", "--account", "creator", "--images", str(image_path), "--title", "图文标题", "--note", "图文正文", ] ) self.assertEqual(args.title, "图文标题") self.assertEqual(args.note, "图文正文") def test_xiaohongshu_upload_video_defaults_to_headless(self): with tempfile.TemporaryDirectory() as tmp_dir: video_path = Path(tmp_dir) / "demo.mp4" video_path.write_bytes(b"video") parser = sau_cli.build_parser() args = parser.parse_args( [ "xiaohongshu", "upload-video", "--account", "creator", "--file", str(video_path), "--title", "视频标题", ] ) self.assertTrue(args.headless) def test_xiaohongshu_upload_note_accepts_headed(self): with tempfile.TemporaryDirectory() as tmp_dir: image_path = Path(tmp_dir) / "1.png" image_path.write_bytes(b"image") parser = sau_cli.build_parser() args = parser.parse_args( [ "xiaohongshu", "upload-note", "--account", "creator", "--images", str(image_path), "--title", "图文标题", "--note", "图文正文", "--headed", ] ) self.assertFalse(args.headless) class BrowserCliDispatchTests(unittest.TestCase): def test_dispatch_xiaohongshu_check_prints_valid(self): args = Namespace(platform="xiaohongshu", action="check", account="creator") with patch("sau_cli.check_xiaohongshu_account", new=AsyncMock(return_value=True)): code = asyncio.run(sau_cli.dispatch(args)) self.assertEqual(code, 0) def test_dispatch_douyin_upload_note_uses_new_request_fields(self): args = Namespace( platform="douyin", action="upload-note", account="creator", images=[Path("1.png")], title="图文标题", note="图文正文", tags="测试,图文", schedule=0, debug=False, headless=True, ) with patch("sau_cli.upload_note", new=AsyncMock()) as mock_upload: asyncio.run(sau_cli.dispatch(args)) request = mock_upload.await_args.args[0] self.assertEqual(request.title, "图文标题") self.assertEqual(request.note, "图文正文") def test_dispatch_douyin_upload_video_uses_dual_thumbnail_request_fields(self): args = Namespace( platform="douyin", action="upload-video", account="creator", file=Path("demo.mp4"), title="视频标题", desc="视频简介", tags="测试,视频", schedule=0, thumbnail=None, thumbnail_landscape=Path("landscape.png"), thumbnail_portrait=Path("portrait.png"), product_link="", product_title="", debug=False, headless=True, ) with patch("sau_cli.upload_video", new=AsyncMock()) as mock_upload: asyncio.run(sau_cli.dispatch(args)) request = mock_upload.await_args.args[0] self.assertEqual(request.thumbnail_landscape_file, Path("landscape.png")) self.assertEqual(request.thumbnail_portrait_file, Path("portrait.png")) def test_dispatch_tencent_upload_video_uses_dual_thumbnail_request_fields(self): args = Namespace( platform="tencent", action="upload-video", account="creator", file=Path("demo.mp4"), title="视频标题", desc="视频简介", tags="测试,视频", schedule=0, thumbnail=None, thumbnail_landscape=Path("landscape.png"), thumbnail_portrait=Path("portrait.png"), short_title=None, category=None, draft=False, debug=False, headless=True, ) with patch("sau_cli.upload_tencent_video", new=AsyncMock()) as mock_upload: asyncio.run(sau_cli.dispatch(args)) request = mock_upload.await_args.args[0] self.assertEqual(request.thumbnail_landscape_file, Path("landscape.png")) self.assertEqual(request.thumbnail_portrait_file, Path("portrait.png")) def test_dispatch_xiaohongshu_upload_video_uses_headed_request(self): args = Namespace( platform="xiaohongshu", action="upload-video", account="creator", file=Path("demo.mp4"), title="视频标题", desc="视频简介", tags="测试,视频", schedule=0, thumbnail=None, debug=False, headless=False, ) with patch("sau_cli.upload_xiaohongshu_video", new=AsyncMock()) as mock_upload: asyncio.run(sau_cli.dispatch(args)) request = mock_upload.await_args.args[0] self.assertEqual(request.title, "视频标题") self.assertEqual(request.description, "视频简介") self.assertFalse(request.headless) def test_dispatch_xiaohongshu_upload_note_uses_headless_request(self): args = Namespace( platform="xiaohongshu", action="upload-note", account="creator", images=[Path("1.png"), Path("2.png")], title="图文标题", note="图文正文", tags="测试,图文", schedule=0, debug=False, headless=True, ) with patch("sau_cli.upload_xiaohongshu_note", new=AsyncMock()) as mock_upload: asyncio.run(sau_cli.dispatch(args)) request = mock_upload.await_args.args[0] self.assertEqual(request.title, "图文标题") self.assertEqual(request.note, "图文正文") self.assertTrue(request.headless) self.assertEqual(len(request.image_files), 2) if __name__ == "__main__": unittest.main()