From 5024bec66614db1f1c6cb608f41413ae5c89465c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 19:51:12 +0000 Subject: [PATCH 1/3] Initial plan From 55c95ee288d769aa7d011da5c0c3604fd4134992 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 19:55:57 +0000 Subject: [PATCH 2/3] Replace assert-based genre validation with explicit ValueError raises Agent-Logs-Url: https://github.com/CyberSecDef/NovelForge/sessions/7b83084a-e47f-4808-b782-8ffa5458e3a9 Co-authored-by: CyberSecDef <17597068+CyberSecDef@users.noreply.github.com> --- novelforge/names.py | 9 +++++---- novelforge/voice.py | 9 +++++---- tests/test_names.py | 23 +++++++++++++++++++++++ tests/test_voice.py | 23 +++++++++++++++++++++++ 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/novelforge/names.py b/novelforge/names.py index 60faf99..24b0991 100644 --- a/novelforge/names.py +++ b/novelforge/names.py @@ -51,10 +51,11 @@ # Validate that every allowed genre has a name-pool mapping. _missing_name_genres = ALLOWED_GENRES - _GENRE_GROUP.keys() -assert not _missing_name_genres, ( - f"Genres missing from _GENRE_GROUP in names.py: {sorted(_missing_name_genres)}. " - f"Add a style-group mapping for each." -) +if _missing_name_genres: + raise ValueError( + f"Genres missing from _GENRE_GROUP in names.py: {sorted(_missing_name_genres)}. " + f"Add a style-group mapping for each." + ) # --------------------------------------------------------------------------- # Name pool data diff --git a/novelforge/voice.py b/novelforge/voice.py index 391aa5f..09e9213 100644 --- a/novelforge/voice.py +++ b/novelforge/voice.py @@ -241,10 +241,11 @@ # Validate that every allowed genre has voice weights. _missing_voice_genres = ALLOWED_GENRES - _GENRE_VOICE_WEIGHTS.keys() -assert not _missing_voice_genres, ( - f"Genres missing from _GENRE_VOICE_WEIGHTS in voice.py: {sorted(_missing_voice_genres)}. " - f"Add a voice weight mapping for each." -) +if _missing_voice_genres: + raise ValueError( + f"Genres missing from _GENRE_VOICE_WEIGHTS in voice.py: {sorted(_missing_voice_genres)}. " + f"Add a voice weight mapping for each." + ) def select_voice_seed(genre: str = "", premise: str = "") -> dict[str, str]: diff --git a/tests/test_names.py b/tests/test_names.py index 1eb9f3a..83f8168 100644 --- a/tests/test_names.py +++ b/tests/test_names.py @@ -1,7 +1,11 @@ """Tests for the novelforge.names module (genre-aware name pools).""" +import importlib + import pytest +import novelforge.names +import novelforge.validation from novelforge.names import ( _GENRE_GROUP, _NAMES, @@ -167,3 +171,22 @@ def test_output_mentions_male_female_last(self): assert "male first names" in result.lower() assert "female first names" in result.lower() assert "last names" in result.lower() + + +# --------------------------------------------------------------------------- +# Genre coverage validation raises ValueError (not silenced by -O flag) +# --------------------------------------------------------------------------- + +class TestNameGenreValidation: + """names.py must raise ValueError for missing genre mappings even under -O.""" + + def test_raises_value_error_when_genre_missing_from_genre_group(self, monkeypatch): + """Reloading names.py with an extra ALLOWED_GENRES entry raises ValueError.""" + extended = novelforge.validation.ALLOWED_GENRES | {"FakeGenreForTest"} + monkeypatch.setattr(novelforge.validation, "ALLOWED_GENRES", extended) + with pytest.raises(ValueError, match="FakeGenreForTest"): + importlib.reload(novelforge.names) + + def test_no_error_when_all_genres_covered(self, monkeypatch): + """Reloading names.py with the standard ALLOWED_GENRES does not raise.""" + importlib.reload(novelforge.names) diff --git a/tests/test_voice.py b/tests/test_voice.py index 674f714..3715a29 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -2,10 +2,13 @@ Tests for novelforge/voice.py — voice seed selection and formatting. """ +import importlib import random import pytest +import novelforge.validation +import novelforge.voice from novelforge.voice import ( _PREMISE_KEYWORD_BOOST, _PREMISE_KEYWORDS, @@ -212,3 +215,23 @@ def test_embeds_seed_content(self): assert seed["prose_style"] in prompt assert seed["emotional_register"] in prompt assert seed["sensory_preference"] in prompt + + + +# --------------------------------------------------------------------------- +# Genre coverage validation raises ValueError (not silenced by -O flag) +# --------------------------------------------------------------------------- + +class TestVoiceGenreValidation: + """voice.py must raise ValueError for missing genre mappings even under -O.""" + + def test_raises_value_error_when_genre_missing_from_voice_weights(self, monkeypatch): + """Reloading voice.py with an extra ALLOWED_GENRES entry raises ValueError.""" + extended = novelforge.validation.ALLOWED_GENRES | {"FakeGenreForTest"} + monkeypatch.setattr(novelforge.validation, "ALLOWED_GENRES", extended) + with pytest.raises(ValueError, match="FakeGenreForTest"): + importlib.reload(novelforge.voice) + + def test_no_error_when_all_genres_covered(self, monkeypatch): + """Reloading voice.py with the standard ALLOWED_GENRES does not raise.""" + importlib.reload(novelforge.voice) From c29554d5093eaafacd3aec5465b4b6c4c294bc4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 19:57:27 +0000 Subject: [PATCH 3/3] Remove unused monkeypatch fixture from no-error test methods Agent-Logs-Url: https://github.com/CyberSecDef/NovelForge/sessions/7b83084a-e47f-4808-b782-8ffa5458e3a9 Co-authored-by: CyberSecDef <17597068+CyberSecDef@users.noreply.github.com> --- tests/test_names.py | 2 +- tests/test_voice.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_names.py b/tests/test_names.py index 83f8168..bed284f 100644 --- a/tests/test_names.py +++ b/tests/test_names.py @@ -187,6 +187,6 @@ def test_raises_value_error_when_genre_missing_from_genre_group(self, monkeypatc with pytest.raises(ValueError, match="FakeGenreForTest"): importlib.reload(novelforge.names) - def test_no_error_when_all_genres_covered(self, monkeypatch): + def test_no_error_when_all_genres_covered(self): """Reloading names.py with the standard ALLOWED_GENRES does not raise.""" importlib.reload(novelforge.names) diff --git a/tests/test_voice.py b/tests/test_voice.py index 3715a29..5049a92 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -232,6 +232,6 @@ def test_raises_value_error_when_genre_missing_from_voice_weights(self, monkeypa with pytest.raises(ValueError, match="FakeGenreForTest"): importlib.reload(novelforge.voice) - def test_no_error_when_all_genres_covered(self, monkeypatch): + def test_no_error_when_all_genres_covered(self): """Reloading voice.py with the standard ALLOWED_GENRES does not raise.""" importlib.reload(novelforge.voice)