137 lines
4.1 KiB
Python
137 lines
4.1 KiB
Python
# Copyright 2026 Umbra Tools (umbratools.com)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Tests for multi-project readiness (Phase C2 of MULTI_PROJECT_PLAN.md).
|
|
|
|
Covers:
|
|
- dynamic active_project_id (A1) instead of hard-coded project_id=1
|
|
- WAL journal mode enabled on project + registry DBs (A2)
|
|
- bootstrap of a brand-new isolated project from a manuscript (B1)
|
|
- list_projects() excludes the legacy root .umbra (C1)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def manuscript(temp_dir: Path) -> Path:
|
|
p = temp_dir / "Second_Book.txt"
|
|
p.write_text(
|
|
"# Chapter One\n\nHello world.\n\n"
|
|
"# Chapter Two\n\nGoodbye moon.\n\n",
|
|
encoding="utf-8",
|
|
)
|
|
return p
|
|
|
|
|
|
async def test_active_project_id_returns_first_row(temp_dir: Path) -> None:
|
|
from umbraaudio.database import Database
|
|
|
|
db = Database(temp_dir / "project.db")
|
|
await db.connect()
|
|
await db.create_project("Book A", "a.txt", "txt")
|
|
pid = await db.active_project_id()
|
|
assert pid == 1
|
|
await db.close()
|
|
|
|
|
|
def test_database_enables_wal(temp_dir: Path) -> None:
|
|
|
|
import asyncio
|
|
|
|
asyncio.run(_open_wal(temp_dir / "project.db"))
|
|
conn = sqlite3.connect(str(temp_dir / "project.db"))
|
|
assert conn.execute("PRAGMA journal_mode").fetchone()[0] == "wal"
|
|
conn.close()
|
|
|
|
|
|
async def _open_wal(path: Path) -> None:
|
|
from umbraaudio.database import Database
|
|
|
|
db = Database(path)
|
|
await db.connect()
|
|
await db.close()
|
|
|
|
|
|
def test_registry_enables_wal(registry_db_path: Path) -> None:
|
|
import asyncio
|
|
|
|
from umbraaudio.voices.registry import VoiceRegistry
|
|
|
|
async def _open() -> None:
|
|
reg = VoiceRegistry(registry_db_path)
|
|
await reg.connect()
|
|
await reg.close()
|
|
|
|
asyncio.run(_open())
|
|
conn = sqlite3.connect(str(registry_db_path))
|
|
assert conn.execute("PRAGMA journal_mode").fetchone()[0] == "wal"
|
|
conn.close()
|
|
|
|
|
|
def test_bootstrap_creates_isolated_project(manuscript: Path, monkeypatch) -> None:
|
|
import shutil
|
|
import tempfile
|
|
|
|
from umbraaudio import config, project_bootstrap
|
|
|
|
base = Path(tempfile.mkdtemp(prefix="umbra_base_"))
|
|
monkeypatch.setattr(config, "PROJECT_BASE", base)
|
|
|
|
result = project_bootstrap.create_project_from_manuscript(
|
|
manuscript, project_name="Second_Book"
|
|
)
|
|
assert result["slug"] == "Second_Book"
|
|
project_dir = base / "Second_Book"
|
|
assert (project_dir / ".umbra" / "project.db").exists()
|
|
|
|
# One project row, and default isolated chapters in the new DB.
|
|
conn = sqlite3.connect(str(project_dir / ".umbra" / "project.db"))
|
|
project_rows = conn.execute("SELECT id, name FROM project").fetchall()
|
|
assert len(project_rows) == 1
|
|
assert project_rows[0][1] == "Second_Book"
|
|
ch_count = conn.execute("SELECT COUNT(*) FROM chapters").fetchone()[0]
|
|
conn.close()
|
|
assert ch_count > 0
|
|
|
|
# Do not leave the temp base behind.
|
|
shutil.rmtree(base, ignore_errors=True)
|
|
|
|
|
|
def test_list_projects_excludes_root_umbra(monkeypatch) -> None:
|
|
import asyncio
|
|
import tempfile
|
|
|
|
from umbraaudio import config
|
|
|
|
base = Path(tempfile.mkdtemp(prefix="umbra_base_"))
|
|
# Create a legacy root .umbra with a project.db (the C1 case).
|
|
legacy = base / ".umbra"
|
|
legacy.mkdir(parents=True)
|
|
asyncio.run(_make_db(legacy / "project.db", name="DealHunterShip_4.0e_whole-to-audio.docx"))
|
|
# Create a real project dir.
|
|
real = base / "DealHunterShip_4.0e_whole-to-audio"
|
|
real.mkdir(parents=True)
|
|
asyncio.run(_make_db(real / ".umbra" / "project.db", name="Deal Hunter"))
|
|
|
|
monkeypatch.setattr(config, "PROJECT_BASE", base)
|
|
projects = config.list_projects()
|
|
slugs = {p["slug"] for p in projects}
|
|
assert "DealHunterShip_4.0e_whole-to-audio" in slugs
|
|
assert ".umbra" not in slugs
|
|
assert len(slugs) == 1
|
|
|
|
|
|
async def _make_db(path: Path, name: str) -> None:
|
|
from umbraaudio.database import Database
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
db = Database(path)
|
|
await db.connect()
|
|
await db.create_project(name, f"{name}.txt", "txt")
|
|
await db.close()
|