FunkyJuiceRecipes/tests/test_db_init.py

87 lines
2.7 KiB
Python
Raw Normal View History

2025-12-18 20:30:09 +00:00
from __future__ import annotations
import os
from types import SimpleNamespace
from pathlib import Path
import sqlite3
from funkyjuicerecipes.data.db import (
DB_FILENAME,
init_database,
get_db,
apply_migrations,
)
class DummyPaths(SimpleNamespace):
@property
def data(self) -> Path: # type: ignore[override]
return self._data
@data.setter
def data(self, value: Path) -> None:
self._data = Path(value)
class DummyApp(SimpleNamespace):
def __init__(self, data_dir: Path) -> None:
super().__init__()
self.paths = DummyPaths()
self.paths.data = data_dir
def _tables(conn: sqlite3.Connection) -> set[str]:
cur = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
return {row[0] for row in cur.fetchall()}
def test_init_database_creates_file_and_tables(tmp_path: Path):
app = DummyApp(tmp_path / "data")
db = init_database(app)
db_path = Path(app.paths.data) / DB_FILENAME
assert db_path.exists(), "DB file should be created in app.paths.data"
# Use sqlite3 to introspect tables
with sqlite3.connect(db_path) as conn:
names = _tables(conn)
feat(db,models,dao,logic): nicotine inventory + flavor snapshots; drop legacy nic_pct; migrations, tests • Migrations ◦ Create nicotine table with NOCASE strings, base PG/VG check, strength_mg_per_ml (>0), soft-delete, and optional inventory metadata (bottle_size_ml, cost, purchase_date). ◦ Alter recipe: add nic_target_mg_per_ml (NOT NULL), nic_strength_mg_per_ml (NOT NULL), nicotine_id (FK ON DELETE SET NULL); keep nic_base. Drop legacy nic_pct via rebuild migration. ◦ Alter flavor: add bottle_size_ml, cost, purchase_date (nullable). ◦ Alter recipe_flavor: add flavor snapshot columns (flavor_name_snapshot, flavor_company_snapshot NOCASE, flavor_base_snapshot PG/VG CHECK). ◦ Ensure migration order runs “_created” before ALTERs. • Models ◦ Add Nicotine model. ◦ Update Recipe with nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine FK. ◦ Update Flavor with optional inventory metadata. ◦ Update RecipeFlavor with snapshot fields and unique index remains. • DAOs ◦ Add NicotineRepository (CRUD, list with soft-delete filters; hard_delete_when_safe blocked when referenced by recipes). ◦ Update RecipeRepository: ▪ create/update accept nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine_id. ▪ If nicotine_id provided, snapshot strength/base from inventory unless overridden. ▪ replace_ingredients uses diff-based updates and populates flavor snapshots on insert. ◦ Update FlavorRepository to edit inventory metadata; soft/undelete; hard delete only when safe. • DB init ◦ Bind Peewee models to runtime DB; tweak migration ordering logic. • Logic ◦ Add calculation engine (compute_breakdown, breakdown_for_view) supporting target nicotine mg/mL and stock strength mg/mL; rounding policy for display. • Tests ◦ Update/init DB tests to include nicotine table and flexible migration counts. ◦ Add calculation tests (120 mL example, nic in VG, 50 mg/mL stock, validations). ◦ Add DAO tests including flavor snapshots stability and replace_ingredients diff behavior. ◦ All tests passing: 15 passed. • Docs ◦ Update SPECS and MILESTONES; strike through Milestones 3 and 4. BREAKING CHANGE: remove Recipe.nic_pct field and any compatibility code. API changes in RecipeRepository.create/update now require mg/mL fields and use snapshot+FK model for nicotine.
2025-12-19 01:55:22 +00:00
# migrations + our domain tables (including nicotine)
assert {"migrations", "flavor", "recipe", "recipe_flavor", "nicotine"}.issubset(names)
2025-12-18 20:30:09 +00:00
# migrations table should have entries for all applied migrations
cur = conn.execute("SELECT filename, batch FROM migrations ORDER BY filename")
rows = cur.fetchall()
feat(db,models,dao,logic): nicotine inventory + flavor snapshots; drop legacy nic_pct; migrations, tests • Migrations ◦ Create nicotine table with NOCASE strings, base PG/VG check, strength_mg_per_ml (>0), soft-delete, and optional inventory metadata (bottle_size_ml, cost, purchase_date). ◦ Alter recipe: add nic_target_mg_per_ml (NOT NULL), nic_strength_mg_per_ml (NOT NULL), nicotine_id (FK ON DELETE SET NULL); keep nic_base. Drop legacy nic_pct via rebuild migration. ◦ Alter flavor: add bottle_size_ml, cost, purchase_date (nullable). ◦ Alter recipe_flavor: add flavor snapshot columns (flavor_name_snapshot, flavor_company_snapshot NOCASE, flavor_base_snapshot PG/VG CHECK). ◦ Ensure migration order runs “_created” before ALTERs. • Models ◦ Add Nicotine model. ◦ Update Recipe with nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine FK. ◦ Update Flavor with optional inventory metadata. ◦ Update RecipeFlavor with snapshot fields and unique index remains. • DAOs ◦ Add NicotineRepository (CRUD, list with soft-delete filters; hard_delete_when_safe blocked when referenced by recipes). ◦ Update RecipeRepository: ▪ create/update accept nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine_id. ▪ If nicotine_id provided, snapshot strength/base from inventory unless overridden. ▪ replace_ingredients uses diff-based updates and populates flavor snapshots on insert. ◦ Update FlavorRepository to edit inventory metadata; soft/undelete; hard delete only when safe. • DB init ◦ Bind Peewee models to runtime DB; tweak migration ordering logic. • Logic ◦ Add calculation engine (compute_breakdown, breakdown_for_view) supporting target nicotine mg/mL and stock strength mg/mL; rounding policy for display. • Tests ◦ Update/init DB tests to include nicotine table and flexible migration counts. ◦ Add calculation tests (120 mL example, nic in VG, 50 mg/mL stock, validations). ◦ Add DAO tests including flavor snapshots stability and replace_ingredients diff behavior. ◦ All tests passing: 15 passed. • Docs ◦ Update SPECS and MILESTONES; strike through Milestones 3 and 4. BREAKING CHANGE: remove Recipe.nic_pct field and any compatibility code. API changes in RecipeRepository.create/update now require mg/mL fields and use snapshot+FK model for nicotine.
2025-12-19 01:55:22 +00:00
# We now have more migrations over time; ensure at least the base set were applied
assert len(rows) >= 3
2025-12-18 20:30:09 +00:00
filenames = [r[0] for r in rows]
# Ensure expected filenames are present
assert filenames == sorted(filenames)
assert any("flavor_table_created" in f for f in filenames)
assert any("recipe_table_created" in f for f in filenames)
assert any("recipe_flavor_table_created" in f for f in filenames)
feat(db,models,dao,logic): nicotine inventory + flavor snapshots; drop legacy nic_pct; migrations, tests • Migrations ◦ Create nicotine table with NOCASE strings, base PG/VG check, strength_mg_per_ml (>0), soft-delete, and optional inventory metadata (bottle_size_ml, cost, purchase_date). ◦ Alter recipe: add nic_target_mg_per_ml (NOT NULL), nic_strength_mg_per_ml (NOT NULL), nicotine_id (FK ON DELETE SET NULL); keep nic_base. Drop legacy nic_pct via rebuild migration. ◦ Alter flavor: add bottle_size_ml, cost, purchase_date (nullable). ◦ Alter recipe_flavor: add flavor snapshot columns (flavor_name_snapshot, flavor_company_snapshot NOCASE, flavor_base_snapshot PG/VG CHECK). ◦ Ensure migration order runs “_created” before ALTERs. • Models ◦ Add Nicotine model. ◦ Update Recipe with nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine FK. ◦ Update Flavor with optional inventory metadata. ◦ Update RecipeFlavor with snapshot fields and unique index remains. • DAOs ◦ Add NicotineRepository (CRUD, list with soft-delete filters; hard_delete_when_safe blocked when referenced by recipes). ◦ Update RecipeRepository: ▪ create/update accept nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine_id. ▪ If nicotine_id provided, snapshot strength/base from inventory unless overridden. ▪ replace_ingredients uses diff-based updates and populates flavor snapshots on insert. ◦ Update FlavorRepository to edit inventory metadata; soft/undelete; hard delete only when safe. • DB init ◦ Bind Peewee models to runtime DB; tweak migration ordering logic. • Logic ◦ Add calculation engine (compute_breakdown, breakdown_for_view) supporting target nicotine mg/mL and stock strength mg/mL; rounding policy for display. • Tests ◦ Update/init DB tests to include nicotine table and flexible migration counts. ◦ Add calculation tests (120 mL example, nic in VG, 50 mg/mL stock, validations). ◦ Add DAO tests including flavor snapshots stability and replace_ingredients diff behavior. ◦ All tests passing: 15 passed. • Docs ◦ Update SPECS and MILESTONES; strike through Milestones 3 and 4. BREAKING CHANGE: remove Recipe.nic_pct field and any compatibility code. API changes in RecipeRepository.create/update now require mg/mL fields and use snapshot+FK model for nicotine.
2025-12-19 01:55:22 +00:00
# New nicotine/inventory migrations should also be present
assert any("nicotine_table_created" in f for f in filenames)
# All migrations applied in one run should share the same batch
2025-12-18 20:30:09 +00:00
batches = {r[1] for r in rows}
assert len(batches) == 1
def test_apply_migrations_is_idempotent(tmp_path: Path):
app = DummyApp(tmp_path / "data2")
db = init_database(app)
# Second application should detect none pending
pending = apply_migrations(db)
assert pending == []
feat(db,models,dao,logic): nicotine inventory + flavor snapshots; drop legacy nic_pct; migrations, tests • Migrations ◦ Create nicotine table with NOCASE strings, base PG/VG check, strength_mg_per_ml (>0), soft-delete, and optional inventory metadata (bottle_size_ml, cost, purchase_date). ◦ Alter recipe: add nic_target_mg_per_ml (NOT NULL), nic_strength_mg_per_ml (NOT NULL), nicotine_id (FK ON DELETE SET NULL); keep nic_base. Drop legacy nic_pct via rebuild migration. ◦ Alter flavor: add bottle_size_ml, cost, purchase_date (nullable). ◦ Alter recipe_flavor: add flavor snapshot columns (flavor_name_snapshot, flavor_company_snapshot NOCASE, flavor_base_snapshot PG/VG CHECK). ◦ Ensure migration order runs “_created” before ALTERs. • Models ◦ Add Nicotine model. ◦ Update Recipe with nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine FK. ◦ Update Flavor with optional inventory metadata. ◦ Update RecipeFlavor with snapshot fields and unique index remains. • DAOs ◦ Add NicotineRepository (CRUD, list with soft-delete filters; hard_delete_when_safe blocked when referenced by recipes). ◦ Update RecipeRepository: ▪ create/update accept nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine_id. ▪ If nicotine_id provided, snapshot strength/base from inventory unless overridden. ▪ replace_ingredients uses diff-based updates and populates flavor snapshots on insert. ◦ Update FlavorRepository to edit inventory metadata; soft/undelete; hard delete only when safe. • DB init ◦ Bind Peewee models to runtime DB; tweak migration ordering logic. • Logic ◦ Add calculation engine (compute_breakdown, breakdown_for_view) supporting target nicotine mg/mL and stock strength mg/mL; rounding policy for display. • Tests ◦ Update/init DB tests to include nicotine table and flexible migration counts. ◦ Add calculation tests (120 mL example, nic in VG, 50 mg/mL stock, validations). ◦ Add DAO tests including flavor snapshots stability and replace_ingredients diff behavior. ◦ All tests passing: 15 passed. • Docs ◦ Update SPECS and MILESTONES; strike through Milestones 3 and 4. BREAKING CHANGE: remove Recipe.nic_pct field and any compatibility code. API changes in RecipeRepository.create/update now require mg/mL fields and use snapshot+FK model for nicotine.
2025-12-19 01:55:22 +00:00
# migrations count remains stable after re-run
2025-12-18 20:30:09 +00:00
db_path = Path(app.paths.data) / DB_FILENAME
with sqlite3.connect(db_path) as conn:
cur = conn.execute("SELECT COUNT(*) FROM migrations")
(count,) = cur.fetchone()
feat(db,models,dao,logic): nicotine inventory + flavor snapshots; drop legacy nic_pct; migrations, tests • Migrations ◦ Create nicotine table with NOCASE strings, base PG/VG check, strength_mg_per_ml (>0), soft-delete, and optional inventory metadata (bottle_size_ml, cost, purchase_date). ◦ Alter recipe: add nic_target_mg_per_ml (NOT NULL), nic_strength_mg_per_ml (NOT NULL), nicotine_id (FK ON DELETE SET NULL); keep nic_base. Drop legacy nic_pct via rebuild migration. ◦ Alter flavor: add bottle_size_ml, cost, purchase_date (nullable). ◦ Alter recipe_flavor: add flavor snapshot columns (flavor_name_snapshot, flavor_company_snapshot NOCASE, flavor_base_snapshot PG/VG CHECK). ◦ Ensure migration order runs “_created” before ALTERs. • Models ◦ Add Nicotine model. ◦ Update Recipe with nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine FK. ◦ Update Flavor with optional inventory metadata. ◦ Update RecipeFlavor with snapshot fields and unique index remains. • DAOs ◦ Add NicotineRepository (CRUD, list with soft-delete filters; hard_delete_when_safe blocked when referenced by recipes). ◦ Update RecipeRepository: ▪ create/update accept nic_target_mg_per_ml, nic_strength_mg_per_ml, nic_base, optional nicotine_id. ▪ If nicotine_id provided, snapshot strength/base from inventory unless overridden. ▪ replace_ingredients uses diff-based updates and populates flavor snapshots on insert. ◦ Update FlavorRepository to edit inventory metadata; soft/undelete; hard delete only when safe. • DB init ◦ Bind Peewee models to runtime DB; tweak migration ordering logic. • Logic ◦ Add calculation engine (compute_breakdown, breakdown_for_view) supporting target nicotine mg/mL and stock strength mg/mL; rounding policy for display. • Tests ◦ Update/init DB tests to include nicotine table and flexible migration counts. ◦ Add calculation tests (120 mL example, nic in VG, 50 mg/mL stock, validations). ◦ Add DAO tests including flavor snapshots stability and replace_ingredients diff behavior. ◦ All tests passing: 15 passed. • Docs ◦ Update SPECS and MILESTONES; strike through Milestones 3 and 4. BREAKING CHANGE: remove Recipe.nic_pct field and any compatibility code. API changes in RecipeRepository.create/update now require mg/mL fields and use snapshot+FK model for nicotine.
2025-12-19 01:55:22 +00:00
assert count >= 3