220 lines
9.1 KiB
Python
220 lines
9.1 KiB
Python
import unittest
|
|
from importlib.machinery import SourceFileLoader
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
MODULE = SourceFileLoader("aaz_music_spider", str(ROOT / "AAZ音乐.py")).load_module()
|
|
Spider = MODULE.Spider
|
|
|
|
|
|
HOME_HTML = """
|
|
<html><body>
|
|
<ul>
|
|
<li>
|
|
<div class="name"><a href="/m/1001.html" title="晴天">晴天</a></div>
|
|
<div class="mv"><a href="/v/mv1001">MV</a></div>
|
|
</li>
|
|
<li>
|
|
<div class="name"><a href="/m/1002.html" title="七里香">七里香</a></div>
|
|
</li>
|
|
</ul>
|
|
</body></html>
|
|
"""
|
|
|
|
SINGER_HTML = """
|
|
<html><body>
|
|
<li>
|
|
<div class="pic"><img src="/img/singer.jpg"></div>
|
|
<div class="name"><a href="/s/jay" title="周杰伦">周杰伦</a></div>
|
|
</li>
|
|
</body></html>
|
|
"""
|
|
|
|
PLAYLIST_HTML = """
|
|
<html><body>
|
|
<li>
|
|
<div class="pic"><img src="/img/playlist.jpg"></div>
|
|
<div class="name"><a href="/p/top100" title="华语热歌">华语热歌</a></div>
|
|
</li>
|
|
</body></html>
|
|
"""
|
|
|
|
ALBUM_HTML = """
|
|
<html><body>
|
|
<li>
|
|
<div class="pic"><img src="/img/album.jpg"></div>
|
|
<div class="name"><a href="/a/fantasy" title="范特西">范特西</a></div>
|
|
</li>
|
|
</body></html>
|
|
"""
|
|
|
|
MV_HTML = """
|
|
<html><body>
|
|
<li>
|
|
<div class="pic"><img src="/img/mv.jpg"></div>
|
|
<div class="name"><a href="/v/qingtianmv" title="晴天MV">晴天MV</a></div>
|
|
</li>
|
|
</body></html>
|
|
"""
|
|
|
|
SEARCH_HTML = """
|
|
<html><body>
|
|
<ul>
|
|
<li><div class="name"><a href="/m/2001.html" title="夜曲">夜曲</a></div></li>
|
|
<li><div class="name"><a href="/s/jay" title="周杰伦">周杰伦</a></div></li>
|
|
<li><div class="name"><a href="/p/jaybest" title="周董精选">周董精选</a></div></li>
|
|
<li><div class="name"><a href="/a/fantasy" title="范特西">范特西</a></div></li>
|
|
<li><div class="name"><a href="/v/nocturnemv" title="夜曲MV">夜曲MV</a></div></li>
|
|
</ul>
|
|
</body></html>
|
|
"""
|
|
|
|
SONG_DETAIL_HTML = """
|
|
<html>
|
|
<head><meta name="description" content="夜曲歌曲简介"></head>
|
|
<body>
|
|
<div class="djname"><h1>夜曲<a href="javascript:location.reload()">刷新</a></h1></div>
|
|
<div class="name"><a href="/s/jay">周杰伦</a></div>
|
|
所属专辑:<a href="/a/fantasy">范特西</a>
|
|
<img class="rotate" id="mcover" src="/img/song_detail.jpg">
|
|
<div>歌曲时长:03:45</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
FOLDER_DETAIL_HTML = """
|
|
<html><body>
|
|
<div class="title"><h1>周董精选</h1></div>
|
|
<div class="pic"><img src="/img/folder_detail.jpg"></div>
|
|
<div class="info">经典歌曲合集</div>
|
|
<ul>
|
|
<li><div class="name"><a href="/m/3001.html" title="安静">安静</a></div></li>
|
|
<li><div class="name"><a href="/m/3002.html" title="晴天">晴天</a></div></li>
|
|
</ul>
|
|
</body></html>
|
|
"""
|
|
|
|
PLAY_JSON = '{"url":"https://cdn.example.com/aaz/song2001.mp3","pic":"https://cdn.example.com/cover.jpg"}'
|
|
EMPTY_PLAY_JSON = '{"url":""}'
|
|
|
|
|
|
class TestAAZMusicSpider(unittest.TestCase):
|
|
def setUp(self):
|
|
Spider._instance = None
|
|
self.spider = Spider()
|
|
self.spider.init()
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_home_content_returns_fixed_classes_and_song_cards(self, mock_fetch):
|
|
mock_fetch.return_value = SimpleNamespace(status_code=200, text=HOME_HTML)
|
|
result = self.spider.homeContent(False)
|
|
self.assertEqual(
|
|
[item["type_id"] for item in result["class"]],
|
|
["new", "top", "singer", "playtype", "album", "mv"],
|
|
)
|
|
self.assertEqual([item["vod_id"] for item in result["list"]], ["song:1001", "song:1002"])
|
|
self.assertEqual(result["list"][0]["vod_name"], "晴天")
|
|
self.assertEqual(result["list"][0]["vod_remarks"], "高清MV")
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_home_video_content_reuses_home_list(self, mock_fetch):
|
|
mock_fetch.return_value = SimpleNamespace(status_code=200, text=HOME_HTML)
|
|
result = self.spider.homeVideoContent()
|
|
self.assertEqual([item["vod_id"] for item in result["list"]], ["song:1001", "song:1002"])
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_category_content_maps_song_and_folder_types(self, mock_fetch):
|
|
mock_fetch.side_effect = [
|
|
SimpleNamespace(status_code=200, text=HOME_HTML),
|
|
SimpleNamespace(status_code=200, text=HOME_HTML),
|
|
SimpleNamespace(status_code=200, text=SINGER_HTML),
|
|
SimpleNamespace(status_code=200, text=PLAYLIST_HTML),
|
|
SimpleNamespace(status_code=200, text=ALBUM_HTML),
|
|
SimpleNamespace(status_code=200, text=MV_HTML),
|
|
]
|
|
self.assertEqual(self.spider.categoryContent("new", "1", False, {})["list"][0]["vod_id"], "song:1001")
|
|
self.assertEqual(self.spider.categoryContent("top", "1", False, {})["list"][1]["vod_id"], "song:1002")
|
|
self.assertEqual(self.spider.categoryContent("singer", "1", False, {})["list"][0]["vod_id"], "singer:jay")
|
|
self.assertEqual(self.spider.categoryContent("playtype", "1", False, {})["list"][0]["vod_id"], "playlist:top100")
|
|
self.assertEqual(self.spider.categoryContent("album", "1", False, {})["list"][0]["vod_id"], "album:fantasy")
|
|
self.assertEqual(self.spider.categoryContent("mv", "1", False, {})["list"][0]["vod_id"], "mv:qingtianmv")
|
|
|
|
def test_category_content_returns_empty_for_unknown_type(self):
|
|
self.assertEqual(
|
|
self.spider.categoryContent("bad", "1", False, {}),
|
|
{"page": 1, "limit": 0, "total": 0, "list": []},
|
|
)
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_search_content_maps_mixed_result_types(self, mock_fetch):
|
|
mock_fetch.return_value = SimpleNamespace(status_code=200, text=SEARCH_HTML)
|
|
result = self.spider.searchContent("周杰伦", False, "1")
|
|
self.assertEqual(
|
|
[item["vod_id"] for item in result["list"]],
|
|
["song:2001", "singer:jay", "playlist:jaybest", "album:fantasy", "mv:nocturnemv"],
|
|
)
|
|
|
|
def test_search_content_returns_empty_for_blank_keyword(self):
|
|
self.assertEqual(
|
|
self.spider.searchContent("", False, "1"),
|
|
{"page": 1, "limit": 0, "total": 0, "list": []},
|
|
)
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_detail_content_builds_song_metadata_and_single_play_url(self, mock_fetch):
|
|
mock_fetch.return_value = SimpleNamespace(status_code=200, text=SONG_DETAIL_HTML)
|
|
vod = self.spider.detailContent(["song:2001"])["list"][0]
|
|
self.assertEqual(vod["vod_name"], "夜曲")
|
|
self.assertEqual(vod["vod_pic"], "https://www.aaz.cx/img/song_detail.jpg")
|
|
self.assertEqual(vod["vod_remarks"], "周杰伦 | 范特西 | 03:45")
|
|
self.assertEqual(vod["vod_play_from"], "AAZ音乐")
|
|
self.assertEqual(vod["vod_play_url"], "播放$song:2001")
|
|
|
|
@patch.object(Spider, "fetch")
|
|
def test_detail_content_builds_folder_track_list_for_singer_playlist_album_and_mv(self, mock_fetch):
|
|
mock_fetch.side_effect = [
|
|
SimpleNamespace(status_code=200, text=FOLDER_DETAIL_HTML),
|
|
SimpleNamespace(status_code=200, text=FOLDER_DETAIL_HTML),
|
|
SimpleNamespace(status_code=200, text=FOLDER_DETAIL_HTML),
|
|
SimpleNamespace(status_code=200, text=FOLDER_DETAIL_HTML),
|
|
]
|
|
singer_vod = self.spider.detailContent(["singer:jay"])["list"][0]
|
|
playlist_vod = self.spider.detailContent(["playlist:jaybest"])["list"][0]
|
|
album_vod = self.spider.detailContent(["album:fantasy"])["list"][0]
|
|
mv_vod = self.spider.detailContent(["mv:nocturnemv"])["list"][0]
|
|
expected = "安静$song:3001#晴天$song:3002"
|
|
self.assertEqual(singer_vod["vod_play_url"], expected)
|
|
self.assertEqual(playlist_vod["vod_play_url"], expected)
|
|
self.assertEqual(album_vod["vod_play_url"], expected)
|
|
self.assertEqual(mv_vod["vod_play_url"], expected)
|
|
|
|
def test_detail_content_returns_empty_list_for_invalid_vod_id(self):
|
|
self.assertEqual(self.spider.detailContent(["bad"])["list"], [])
|
|
|
|
@patch.object(Spider, "post")
|
|
def test_player_content_resolves_song_id_to_direct_url(self, mock_post):
|
|
mock_post.return_value = SimpleNamespace(status_code=200, text=PLAY_JSON)
|
|
result = self.spider.playerContent("AAZ音乐", "song:2001", {})
|
|
self.assertEqual(result["parse"], 0)
|
|
self.assertEqual(result["url"], "https://cdn.example.com/aaz/song2001.mp3")
|
|
self.assertEqual(result["header"]["Referer"], "https://www.aaz.cx/")
|
|
self.assertEqual(mock_post.call_args.args[0], "https://www.aaz.cx/js/play.php")
|
|
self.assertEqual(mock_post.call_args.kwargs["data"], "id=2001&type=music")
|
|
self.assertEqual(
|
|
mock_post.call_args.kwargs["headers"]["Content-Type"],
|
|
"application/x-www-form-urlencoded; charset=UTF-8",
|
|
)
|
|
|
|
@patch.object(Spider, "post")
|
|
def test_player_content_returns_empty_url_for_invalid_or_empty_payload(self, mock_post):
|
|
mock_post.return_value = SimpleNamespace(status_code=200, text=EMPTY_PLAY_JSON)
|
|
self.assertEqual(self.spider.playerContent("AAZ音乐", "song:2001", {})["url"], "")
|
|
self.assertEqual(self.spider.playerContent("AAZ音乐", "bad", {})["url"], "")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|