from types import SimpleNamespace from wordarr.naming import render_template, sanitize def req(**kw): base = dict(title="", authors="", year=None, series="", volume=None) base.update(kw) return SimpleNamespace(**base) def test_sanitize_keeps_special_chars_but_strips_separators(): assert sanitize("Die drei ???") == "Die drei ???" assert sanitize("a/b\\c") == "abc" assert "\x01" not in sanitize("a\x01b") def test_ebook_template(): r = req(title="The Hobbit", authors="J.R.R. Tolkien", year=1937) assert render_template("{Author}/{Title} ({Year})", r) == "J.R.R. Tolkien/The Hobbit (1937)" def test_missing_year_drops_parens(): r = req(title="The Hobbit", authors="Tolkien") assert render_template("{Title} ({Year})", r) == "The Hobbit" def test_comic_template(): r = req(title="One Piece", series="One Piece", volume=3) assert render_template("{Series}/{Series} - Band {Volume}", r) == "One Piece/One Piece - Band 03" def test_first_author_only(): r = req(title="X", authors="Alice A, Bob B") assert render_template("{Author}", r) == "Alice A" def test_empty_series_level_is_dropped(): r = req(title="Der Ickabog", authors="J.K. Rowling") assert render_template("{Author}/{Series}/{Title} ({Narrator})", r) == "J.K. Rowling/Der Ickabog" def test_series_level_kept_when_set(): r = req(title="Harry Potter und der Feuerkelch", authors="J.K. Rowling", series="Harry Potter") r.narrator = "Rufus Beck" assert render_template("{Author}/{Series}/{Title} ({Narrator})", r) == \ "J.K. Rowling/Harry Potter/Harry Potter und der Feuerkelch (Rufus Beck)" def test_missing_values_do_not_leave_dangling_separators(): """A "{Series}/{Volume} - {Title}" library still has standalone titles.""" req = SimpleNamespace(title="Ein Einzeltitel", authors="Jemand", series="", volume=None, year=None, narrator="") assert render_template("{Series}/{Volume} - {Title}", req) == "Ein Einzeltitel" assert render_template("{Volume} - {Title} ({Year})", req) == "Ein Einzeltitel" numbered = SimpleNamespace(title="Die Höllenbrut", authors="A. F. Morland", series="Tony Ballard", volume=1, year=2020, narrator="") assert render_template("{Series}/{Volume} - {Title}", numbered) == \ "Tony Ballard/01 - Die Höllenbrut" def test_narrator_uses_the_first_name_only(): """A full cast must not blow up the folder name.""" fry = SimpleNamespace(title="Philosopher's Stone", authors="J.K. Rowling", series="Harry Potter", volume=1, year=None, narrator="Stephen Fry") cast = SimpleNamespace(title="Philosopher's Stone", authors="J.K. Rowling", series="Harry Potter", volume=1, year=None, narrator="Hugh Laurie, Matthew Macfadyen, Riz Ahmed, Michelle Gomez") template = "{Series}/{Volume} - {Title} ({Narrator})" assert render_template(template, fry) == "Harry Potter/01 - Philosopher's Stone (Stephen Fry)" assert render_template(template, cast) == "Harry Potter/01 - Philosopher's Stone (Hugh Laurie)" # the full list stays available assert render_template("{Narrators}", cast) == \ "Hugh Laurie, Matthew Macfadyen, Riz Ahmed, Michelle Gomez" def test_narrator_missing_leaves_no_empty_brackets(): req = SimpleNamespace(title="Ein Buch", authors="Jemand", series="", volume=None, year=None, narrator="") assert render_template("{Series}/{Volume} - {Title} ({Narrator})", req) == "Ein Buch"