Skip to content

Fix two bugs found during test-matrix expansion#1331

Merged
chazlarson merged 3 commits into
developfrom
fix/validation-route-unpack-and-reset-data
Jun 25, 2026
Merged

Fix two bugs found during test-matrix expansion#1331
chazlarson merged 3 commits into
developfrom
fix/validation-route-unpack-and-reset-data

Conversation

@chazlarson

Copy link
Copy Markdown
Contributor

Summary

Two bugs surfaced while writing the test-matrix expansion PR (#1330). Both are fixed here with regression tests.


Bug 1 — database.reset_data() crashes on a fresh SQLite file

What happened

reset_data() was the only function in modules/database.py that issued a DELETE without first running CREATE TABLE IF NOT EXISTS. On a fresh config directory (clean install, CI, isolated test fixture) this raises:

sqlite3.OperationalError: no such table: section_data

Every other helper in the module (save_section_data, retrieve_section_data, retrieve_all_section_data, etc.) follows the pattern:

cursor.execute(persisted_section_table_create())  # CREATE TABLE IF NOT EXISTS …
cursor.execute(sql, ...)

reset_data skipped the first line.

Fix

# modules/database.py  reset_data()
cursor.execute(persisted_section_table_create())  # ← added
sql = "DELETE from section_data where name == ?"
...

One line; matches the established pattern.


Bug 2 — validate_github route crashes when the GitHub token is invalid

What happened

validate_github_server() in modules/validations.py returns either a plain Flask response or a (jsonify(payload), 400) tuple depending on the HTTP outcome:

# modules/validations.py
if response.status_code == 200:
    return jsonify({"valid": True, ...})
else:
    return jsonify({"valid": False, ...}), 400   # ← tuple

The validate_github route called result.get_json() directly without unpacking the tuple first:

# blueprints/validation_routes.py  (before fix)
result = validations.validate_github_server(data)
if result.get_json().get("valid"):   # ← AttributeError when result is a tuple
    ...

This crashes with AttributeError: 'tuple' object has no attribute 'get_json' every time a GitHub token is rejected.

Root cause: inconsistent route pattern

The validate_radarr and validate_sonarr routes already handled both forms:

result = validations.validate_radarr_server(data)
status_code = 200
if isinstance(result, tuple):
    result, status_code = result
if result.get_json().get("valid"):
    ...

The other five routes (omdb, github, tmdb, mdblist, notifiarr) did not.

Fix

A new module-level helper normalises both forms:

def _unpack_validation_result(result):
    """Normalise a validator return value into (flask_response, status_code|None)."""
    if isinstance(result, tuple):
        return result[0], int(result[1])
    return result, None  # caller uses status_code or 400

All seven routes now use it:

result, status_code = _unpack_validation_result(validations.validate_xxx_server(data))
if result.get_json().get("valid"):
    return jsonify(result.get_json())
return jsonify(result.get_json()), status_code or 400

The None sentinel ensures None or 400 = 400 — the correct fallback for plain-response errors — while an explicit 400 from a tuple passes straight through.


Tests — tests/test_bug_fixes.py (+16 tests)

Bug 1 (4 tests)

  • reset_data() does not crash on a totally fresh SQLite file (no section arg)
  • reset_data(name, section=...) does not crash on a fresh SQLite file
  • reset_data() removes existing rows
  • Section-scoped reset_data(name, section=X) leaves other sections untouched

Bug 2 (6 tests)

  • validate_github returns 400 (not 500) when validator returns a tuple ← the crash
  • validate_github returns 200 on success
  • validate_github returns 400 on plain-response error
  • _unpack_validation_result handles plain response (returns None sentinel)
  • _unpack_validation_result handles tuple
  • _unpack_validation_result coerces status code to int

Hardening (6 tests — parametrized)

  • All 7 routes (radarr, sonarr, omdb, github, tmdb, mdblist, notifiarr) survive a tuple return from their validator and produce 400

CI

  • 817 tests pass (801 existing + 16 new), zero regressions
  • ruff check clean on all 3 changed files

chazlarson and others added 3 commits June 25, 2026 09:45
Bug 1: database.reset_data() crashes on a fresh SQLite file
--------------------------------------------------------------
reset_data() was the only function in modules/database.py that issued
a DELETE without first calling CREATE TABLE IF NOT EXISTS.  On a fresh
config directory (e.g. in CI or after a clean install) this raises:

    sqlite3.OperationalError: no such table: section_data

Fix: add cursor.execute(persisted_section_table_create()) before the
DELETE, matching the pattern used by every other helper in the module.

Bug 2: validate_github route crashes when token is invalid
----------------------------------------------------------
validate_github_server() (in modules/validations.py) returns a
(jsonify(payload), 400) tuple when the GitHub token is rejected.
The validate_github() route called result.get_json() directly without
unpacking the tuple first, crashing with:

    AttributeError: 'tuple' object has no attribute 'get_json'

Root cause: an inconsistency introduced when the routes were written.
The validate_radarr and validate_sonarr routes already handled both
forms with 'if isinstance(result, tuple): result, status_code = result'.
The other five routes (omdb, github, tmdb, mdblist, notifiarr) did not.

Fix: add a module-level helper _unpack_validation_result(result) that
normalises both forms to (flask_response, status_code|None).  All seven
affected routes now use it, so the class of bug cannot recur regardless
of what a validator chooses to return:

    result, status_code = _unpack_validation_result(validations.validate_xxx_server(data))
    if result.get_json().get('valid'):
        return jsonify(result.get_json())
    return jsonify(result.get_json()), status_code or 400

The sentinel None for 'plain response' makes 'None or 400 = 400'
correct for all error cases (the original hardcoded behaviour for the
five routes that previously didn't handle tuples).

Tests
-----
New file tests/test_bug_fixes.py (+16 tests):
  Bug 1: reset_data() on empty db (no section arg), reset_data() on
    empty db (with section arg), removes existing rows, section-scoped
    delete does not disturb sibling sections.
  Bug 2: validate_github returns 400 (not 500) when validator returns
    tuple, 200 on plain success, 400 on plain error.
  _unpack_validation_result unit tests: plain response, tuple, status
    code coerced to int.
  Parametrized check that all 7 routes (radarr, sonarr, omdb, github,
    tmdb, mdblist, notifiarr) survive a tuple return and produce 400.

817 tests pass (801 existing + 16 new). ruff clean.
@chazlarson chazlarson merged commit 4e147e3 into develop Jun 25, 2026
7 checks passed
@chazlarson chazlarson deleted the fix/validation-route-unpack-and-reset-data branch June 25, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants