"""WP-C Patch 1 — migration additive idempotente des colonnes « token par poste ». Ajoute `token_hash` et `token_issued_at` à la table `enrolled_agents`, sans casser les bases existantes ni perdre de données. Comportement runtime inchangé : les colonnes restent inertes tant que l'auth par poste n'est pas branchée (patchs WP-C ultérieurs). Voir DETTE-015 / PLAN-WPC-TDD-EXECUTABLE. """ from __future__ import annotations import sqlite3 from agent_v0.server_v1.agent_registry import AgentRegistry def _columns(db_path) -> set[str]: conn = sqlite3.connect(str(db_path)) try: rows = conn.execute("PRAGMA table_info(enrolled_agents)").fetchall() return {r[1] for r in rows} finally: conn.close() def test_token_columns_present_after_init(tmp_path): """Une base neuve doit contenir les colonnes token dès l'init.""" db = tmp_path / "fleet.db" AgentRegistry(db_path=db) cols = _columns(db) assert "token_hash" in cols assert "token_issued_at" in cols def test_token_columns_idempotent(tmp_path): """Ré-initialiser plusieurs fois ne doit jamais lever (migration idempotente).""" db = tmp_path / "fleet.db" AgentRegistry(db_path=db) AgentRegistry(db_path=db) AgentRegistry(db_path=db) cols = _columns(db) assert "token_hash" in cols assert "token_issued_at" in cols def test_migration_preserves_existing_rows(tmp_path): """Une base ancienne (sans les colonnes) est migrée sans perte de données.""" db = tmp_path / "fleet.db" # Ancien schéma, sans les colonnes token, avec une ligne existante. conn = sqlite3.connect(str(db)) conn.execute( """ CREATE TABLE enrolled_agents ( id INTEGER PRIMARY KEY AUTOINCREMENT, machine_id TEXT NOT NULL UNIQUE, user_name TEXT, user_email TEXT, user_id TEXT, hostname TEXT, os_info TEXT, version TEXT, status TEXT NOT NULL DEFAULT 'active', enrolled_at TEXT NOT NULL, last_seen_at TEXT, uninstalled_at TEXT, uninstall_reason TEXT ) """ ) conn.execute( "INSERT INTO enrolled_agents (machine_id, status, enrolled_at) " "VALUES ('PC-LEGACY', 'active', '2026-01-01T00:00:00+00:00')" ) conn.commit() conn.close() # Le démarrage du registry doit migrer la base existante. AgentRegistry(db_path=db) cols = _columns(db) assert "token_hash" in cols assert "token_issued_at" in cols conn = sqlite3.connect(str(db)) try: row = conn.execute( "SELECT machine_id, token_hash FROM enrolled_agents " "WHERE machine_id = 'PC-LEGACY'" ).fetchone() finally: conn.close() assert row is not None assert row[0] == "PC-LEGACY" assert row[1] is None # colonne ajoutée, NULL par défaut