Files
kanbante/tests/test_models.py
T
Spark f1dd921e63 test(palier-1): 16 tests — journal, métriques, détail, âge, WIP (52 verts)
FakeGitea étendu (get_issue, commentaires); le test de réconciliation
suit le nouveau retour de reconcile_board_state; l'assertion de
version suit APP_VERSION.
2026-10-11 10:05:51 -04:00

101 lines
3.6 KiB
Python

"""Model layer: env-driven DB path, migrations, ordering, reconciliation."""
import os
import sqlite3
from app import models
def test_db_path_honours_env_var():
# Regression for audit point C1: the path the Dockerfile sets must win.
assert models.db_path() == os.environ["KANBANTE_DB_PATH"]
def test_board_state_ignores_unknown_column_rows():
# Regression for audit point I4: a legacy row must not KeyError the board.
with models.get_db() as conn:
conn.execute(
"INSERT INTO board_state (repo_full_name, issue_number, column_name) "
"VALUES ('a/b', 99, 'ancienne_colonne')"
)
state = models.get_board_state("a/b")
assert all(cards == [] for cards in state.values())
def test_migration_from_v01_schema(tmp_path, monkeypatch):
legacy = tmp_path / "legacy.db"
conn = sqlite3.connect(legacy)
conn.executescript(
"""
CREATE TABLE board_state (
id INTEGER PRIMARY KEY AUTOINCREMENT,
repo_full_name TEXT NOT NULL,
issue_number INTEGER NOT NULL,
column_name TEXT NOT NULL DEFAULT 'backlog',
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(repo_full_name, issue_number)
);
CREATE TABLE project_notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
repo_full_name TEXT NOT NULL,
note TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(repo_full_name)
);
INSERT INTO board_state (repo_full_name, issue_number, column_name)
VALUES ('a/b', 1, 'todo'), ('a/b', 2, 'colonne_disparue');
INSERT INTO project_notes (repo_full_name, note) VALUES ('a/b', 'salut');
"""
)
conn.commit()
conn.close()
monkeypatch.setenv("KANBANTE_DB_PATH", str(legacy))
models.init_db()
with models.get_db() as conn:
board_cols = [r[1] for r in conn.execute("PRAGMA table_info(board_state)")]
notes_cols = [r[1] for r in conn.execute("PRAGMA table_info(project_notes)")]
remaining = conn.execute("SELECT COUNT(*) FROM board_state").fetchone()[0]
assert "position" in board_cols
assert "updated_at" in notes_cols
assert remaining == 1 # valid row kept, unknown-column row dropped
assert models.get_notes("a/b") == "salut"
def test_move_and_column_order_persisted():
models.move_issue("a/b", 1, "todo")
models.move_issue("a/b", 2, "todo")
models.set_column_order("a/b", "todo", [2, 1])
state = models.get_board_state("a/b")
assert [c["issue_number"] for c in state["todo"]] == [2, 1]
def test_reconcile_closed_to_done_and_purge_deleted():
models.move_issue("a/b", 1, "in_progress")
models.move_issue("a/b", 2, "todo")
models.move_issue("a/b", 3, "review")
changed = models.reconcile_board_state("a/b", open_numbers={1}, closed_numbers={2})
assert changed["closed_to_done"] == 1 and changed["purged"] == 1
assert changed["closed_numbers"] == [2] and changed["purged_numbers"] == [3]
state = models.get_board_state("a/b")
assert [c["issue_number"] for c in state["in_progress"]] == [1]
assert [c["issue_number"] for c in state["done"]] == [2]
assert all(
c["issue_number"] != 3 for cards in state.values() for c in cards
)
def test_notes_roundtrip():
assert models.get_notes("a/b") is None
models.set_notes("a/b", "première version")
models.set_notes("a/b", "deuxième version")
assert models.get_notes("a/b") == "deuxième version"
def test_move_issue_rejects_invalid_column():
import pytest
with pytest.raises(ValueError):
models.move_issue("a/b", 1, "pas_une_colonne")