@@ -92,46 +92,69 @@ class TestIssue977EpisodeZeroOffset:
9292 def test_episode_zero_preserved_with_no_offset (self ):
9393 """Episode 0 with offset=0 stays as E00."""
9494 ep = EpisodeFile (
95- media_path = "old.mkv" , title = "Fate strange Fake" , season = 1 ,
96- episode = 0 , suffix = ".mkv" ,
95+ media_path = "old.mkv" ,
96+ title = "Fate strange Fake" ,
97+ season = 1 ,
98+ episode = 0 ,
99+ suffix = ".mkv" ,
100+ )
101+ result = Renamer .gen_path (
102+ ep , "Fate strange Fake" , method = "pn" , episode_offset = 0
97103 )
98- result = Renamer .gen_path (ep , "Fate strange Fake" , method = "pn" , episode_offset = 0 )
99104 assert "E00" in result
100105
101106 def test_episode_zero_immune_to_positive_offset (self ):
102107 """Episode 0 (special/OVA) should not be shifted by positive offset."""
103108 ep = EpisodeFile (
104- media_path = "old.mkv" , title = "Fate strange Fake" , season = 1 ,
105- episode = 0 , suffix = ".mkv" ,
109+ media_path = "old.mkv" ,
110+ title = "Fate strange Fake" ,
111+ season = 1 ,
112+ episode = 0 ,
113+ suffix = ".mkv" ,
114+ )
115+ result = Renamer .gen_path (
116+ ep , "Fate strange Fake" , method = "pn" , episode_offset = 1
106117 )
107- result = Renamer .gen_path (ep , "Fate strange Fake" , method = "pn" , episode_offset = 1 )
108118 assert "E00" in result
109119
110120 def test_episode_zero_immune_to_negative_offset (self ):
111121 """Episode 0 (special/OVA) should not be shifted by negative offset."""
112122 ep = EpisodeFile (
113- media_path = "old.mkv" , title = "Fate strange Fake" , season = 1 ,
114- episode = 0 , suffix = ".mkv" ,
123+ media_path = "old.mkv" ,
124+ title = "Fate strange Fake" ,
125+ season = 1 ,
126+ episode = 0 ,
127+ suffix = ".mkv" ,
128+ )
129+ result = Renamer .gen_path (
130+ ep , "Fate strange Fake" , method = "pn" , episode_offset = - 12
115131 )
116- result = Renamer .gen_path (ep , "Fate strange Fake" , method = "pn" , episode_offset = - 12 )
117132 assert "E00" in result
118133
119134 def test_regular_episode_offset_still_works (self ):
120135 """Regular episodes should still be affected by offset normally."""
121136 ep = EpisodeFile (
122- media_path = "old.mkv" , title = "Test" , season = 1 ,
123- episode = 13 , suffix = ".mkv" ,
137+ media_path = "old.mkv" ,
138+ title = "Test" ,
139+ season = 1 ,
140+ episode = 13 ,
141+ suffix = ".mkv" ,
124142 )
125143 result = Renamer .gen_path (ep , "Test" , method = "pn" , episode_offset = - 12 )
126144 assert "E01" in result # 13 - 12 = 1
127145
128146 def test_episode_zero_advance_method (self ):
129147 """Episode 0 with advance method and no offset stays E00."""
130148 ep = EpisodeFile (
131- media_path = "old.mkv" , title = "Test" , season = 1 ,
132- episode = 0 , suffix = ".mkv" ,
149+ media_path = "old.mkv" ,
150+ title = "Test" ,
151+ season = 1 ,
152+ episode = 0 ,
153+ suffix = ".mkv" ,
154+ )
155+ result = Renamer .gen_path (
156+ ep , "Bangumi Name" , method = "advance" , episode_offset = 0
133157 )
134- result = Renamer .gen_path (ep , "Bangumi Name" , method = "advance" , episode_offset = 0 )
135158 assert result == "Bangumi Name S01E00.mkv"
136159
137160
@@ -304,7 +327,9 @@ def test_engine_caches_filter_pattern(self):
304327class TestIssue990NumberPrefixTitle :
305328 """Issue #990: Titles starting with numbers crash RSS loop."""
306329
307- PROBLEM_TITLE = "[ANi] 29 岁单身中坚冒险家的日常 - 07 [1080P][Baha][WEB-DL][AAC AVC][CHT][MP4]"
330+ PROBLEM_TITLE = (
331+ "[ANi] 29 岁单身中坚冒险家的日常 - 07 [1080P][Baha][WEB-DL][AAC AVC][CHT][MP4]"
332+ )
308333
309334 def test_raw_parser_correctly_parses_leading_number_title (self ):
310335 """raw_parser correctly parses title starting with number and extracts episode."""
@@ -458,3 +483,77 @@ def test_match_list_no_crash_on_corrupted_data(self, db_session):
458483
459484 # Should not crash even with corrupted data in the DB
460485 unmatched = db .match_list ([torrent ], "https://mikanani.me/RSS/test" )
486+
487+
488+ # ---------------------------------------------------------------------------
489+ # Issue #992: Non-episodic resource causes AttributeError in title_parser
490+ # https://github.com/EstrellaXD/Auto_Bangumi/issues/992
491+ #
492+ # When raw_parser returns None (movie/collection resources), title_parser
493+ # accesses episode.title_zh on None, causing AttributeError.
494+ # ---------------------------------------------------------------------------
495+
496+
497+ class TestIssue992NonEpisodicAttributeError :
498+ """Issue #992: title_parser crashes on non-episodic resources."""
499+
500+ # Titles that raw_parser cannot parse (returns None)
501+ NON_EPISODIC_TITLES = [
502+ "[阿特拉斯字幕组·雪原市出差所][命运-奇异赝品_Fate/strange Fake][04_半神们的卡农曲][简繁日内封PGS][日语配音版_Japanese Dub][Web-DL Remux][1080p AVC AAC]" ,
503+ "[KitaujiSub] Shikanoko Nokonoko Koshitantan [01Pre][WebRip][HEVC_AAC][CHS_JP].mp4" ,
504+ ]
505+
506+ @pytest .mark .parametrize ("title" , NON_EPISODIC_TITLES )
507+ def test_title_parser_returns_none_for_non_episodic (self , title ):
508+ """TitleParser.raw_parser should return None instead of crashing."""
509+ from module .parser .title_parser import TitleParser
510+
511+ result = TitleParser .raw_parser (title )
512+ assert result is None
513+
514+ def test_raw_parser_returns_none_for_unparseable (self ):
515+ """raw_parser returns None for resources it cannot parse."""
516+ result = raw_parser (self .NON_EPISODIC_TITLES [0 ])
517+ assert result is None
518+
519+
520+ # ---------------------------------------------------------------------------
521+ # Issue #1005: BangumiDatabase missing search_official_title method
522+ # https://github.com/EstrellaXD/Auto_Bangumi/issues/1005
523+ # ---------------------------------------------------------------------------
524+
525+
526+ class TestIssue1005SearchOfficialTitle :
527+ """Issue #1005: search_official_title method must exist on BangumiDatabase."""
528+
529+ def test_method_exists (self ):
530+ """BangumiDatabase should have search_official_title method."""
531+ from module .database .bangumi import BangumiDatabase
532+
533+ assert hasattr (BangumiDatabase , "search_official_title" )
534+
535+ def test_search_official_title_finds_match (self , db_session ):
536+ """search_official_title returns the matching bangumi."""
537+ from module .database .bangumi import BangumiDatabase
538+ from module .models import Bangumi
539+
540+ db = BangumiDatabase (db_session )
541+ bangumi = Bangumi (
542+ official_title = "路人女主的养成方法" ,
543+ title_raw = "Saenai Heroine no Sodatekata" ,
544+ season = 1 ,
545+ rss_link = "test" ,
546+ )
547+ db .add (bangumi )
548+
549+ result = db .search_official_title ("路人女主的养成方法" )
550+ assert result is not None
551+ assert result .official_title == "路人女主的养成方法"
552+
553+ def test_search_official_title_returns_none_when_not_found (self , db_session ):
554+ """search_official_title returns None for non-existent title."""
555+ from module .database .bangumi import BangumiDatabase
556+
557+ db = BangumiDatabase (db_session )
558+ result = db .search_official_title ("不存在的番剧" )
559+ assert result is None
0 commit comments