- Add database.py with SQLAlchemy engine and session - Implement get_db() for FastAPI dependency injection - Implement init_db() for table creation - Use SQLAlchemy 2.0 declarative_base() syntax - Add comprehensive tests with 100% coverage Tests: 11 passed, 100% coverage
11 KiB
11 KiB
Prompt: Database & Models Implementation (T06-T11)
🎯 OBIETTIVO
Implementare la fase Database & Models del progetto OpenRouter API Key Monitor seguendo rigorosamente TDD (Test-Driven Development).
Task da completare: T06, T07, T08, T09, T10, T11
📋 CONTESTO
- Repository:
/home/google/Sources/LucaSacchiNet/openrouter-watcher - Specifiche:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/architecture.md - Kanban:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/kanban.md - Stato Attuale: Setup completato (T01-T05), 59 test passanti
🗄️ SCHEMA DATABASE (Da architecture.md)
Tabelle
1. users
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1,
CONSTRAINT chk_email_format CHECK (email LIKE '%_@__%.__%')
);
2. api_keys
CREATE TABLE api_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name VARCHAR(100) NOT NULL,
key_encrypted TEXT NOT NULL, -- AES-256-GCM encrypted
is_active BOOLEAN DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_used_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
3. usage_stats
CREATE TABLE usage_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
api_key_id INTEGER NOT NULL,
date DATE NOT NULL,
model VARCHAR(100) NOT NULL,
requests_count INTEGER DEFAULT 0,
tokens_input INTEGER DEFAULT 0,
tokens_output INTEGER DEFAULT 0,
cost DECIMAL(10, 6) DEFAULT 0.0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE,
CONSTRAINT uniq_key_date_model UNIQUE (api_key_id, date, model)
);
4. api_tokens
CREATE TABLE api_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
token_hash VARCHAR(255) NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_used_at TIMESTAMP,
is_active BOOLEAN DEFAULT 1,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
🔧 TASK DETTAGLIATI
T06: Creare database.py (connection & session)
Requisiti:
- Creare
src/openrouter_monitor/database.py - Implementare SQLAlchemy engine con SQLite
- Configurare session maker con expire_on_commit=False
- Implementare funzione
get_db()per dependency injection FastAPI - Implementare
init_db()per creazione tabelle - Usare
check_same_thread=Falseper SQLite
Test richiesti:
- Test connessione database
- Test creazione engine
- Test session creation
- Test init_db crea tabelle
T07: Creare model User (SQLAlchemy)
Requisiti:
- Creare
src/openrouter_monitor/models/user.py - Implementare class
Usercon tutti i campi - Configurare relationships con ApiKey e ApiToken
- Implementare
check_email_formatconstraint - Campi: id, email, password_hash, created_at, updated_at, is_active
- Index su email
Test richiesti:
- Test creazione utente
- Test vincolo email unique
- Test validazione email format
- Test relationship con api_keys
- Test relationship con api_tokens
T08: Creare model ApiKey (SQLAlchemy)
Requisiti:
- Creare
src/openrouter_monitor/models/api_key.py - Implementare class
ApiKey - Configurare relationship con User e UsageStats
- Foreign key su user_id con ON DELETE CASCADE
- Campi: id, user_id, name, key_encrypted, is_active, created_at, last_used_at
- Index su user_id e is_active
Test richiesti:
- Test creazione API key
- Test relationship con user
- Test relationship con usage_stats
- Test cascade delete
T09: Creare model UsageStats (SQLAlchemy)
Requisiti:
- Creare
src/openrouter_monitor/models/usage_stats.py - Implementare class
UsageStats - Configurare relationship con ApiKey
- Unique constraint: (api_key_id, date, model)
- Campi: id, api_key_id, date, model, requests_count, tokens_input, tokens_output, cost, created_at
- Index su api_key_id, date, model
- Usare Numeric(10, 6) per cost
Test richiesti:
- Test creazione usage stats
- Test unique constraint
- Test relationship con api_key
- Test valori default (0)
T10: Creare model ApiToken (SQLAlchemy)
Requisiti:
- Creare
src/openrouter_monitor/models/api_token.py - Implementare class
ApiToken - Configurare relationship con User
- Foreign key su user_id con ON DELETE CASCADE
- Campi: id, user_id, token_hash, name, created_at, last_used_at, is_active
- Index su user_id, token_hash, is_active
Test richiesti:
- Test creazione API token
- Test relationship con user
- Test cascade delete
T11: Setup Alembic e migrazione iniziale
Requisiti:
- Inizializzare Alembic:
alembic init alembic - Configurare
alembic.inicon DATABASE_URL - Configurare
alembic/env.pycon Base metadata - Creare migrazione iniziale che crea tutte le tabelle
- Migrazione deve includere indici e constraints
- Testare upgrade/downgrade
Test richiesti:
- Test alembic init
- Test creazione migration file
- Test upgrade applica cambiamenti
- Test downgrade rimuove cambiamenti
- Test tutte le tabelle create correttamente
🔄 WORKFLOW TDD OBBLIGATORIO
Per OGNI task (T06-T11):
┌─────────────────────────────────────────┐
│ 1. RED - Scrivi il test che fallisce │
│ • Test prima del codice │
│ • Pattern AAA (Arrange-Act-Assert) │
│ • Nomi descrittivi │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 2. GREEN - Implementa codice minimo │
│ • Solo codice necessario per test │
│ • Nessun refactoring ancora │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 3. REFACTOR - Migliora il codice │
│ • Pulisci duplicazioni │
│ • Migliora nomi variabili │
│ • Test rimangono verdi │
└─────────────────────────────────────────┘
📁 STRUTTURA FILE DA CREARE
src/openrouter_monitor/
├── database.py # T06
└── models/
├── __init__.py # Esporta tutti i modelli
├── user.py # T07
├── api_key.py # T08
├── usage_stats.py # T09
└── api_token.py # T10
alembic/
├── alembic.ini # Configurazione
├── env.py # Configurato con metadata
└── versions/
└── 001_initial_schema.py # T11 - Migrazione iniziale
tests/unit/models/
├── test_database.py # Test T06
├── test_user_model.py # Test T07
├── test_api_key_model.py # Test T08
├── test_usage_stats_model.py # Test T09
├── test_api_token_model.py # Test T10
└── test_migrations.py # Test T11
🧪 REQUISITI TEST
Pattern AAA (Arrange-Act-Assert)
@pytest.mark.unit
async def test_create_user_valid_email_succeeds():
# Arrange
email = "test@example.com"
password_hash = "hashed_password"
# Act
user = User(email=email, password_hash=password_hash)
# Assert
assert user.email == email
assert user.password_hash == password_hash
assert user.is_active is True
assert user.created_at is not None
Marker Pytest
@pytest.mark.unit # Logica pura
@pytest.mark.integration # Con database
@pytest.mark.asyncio # Funzioni async
Fixtures Condivise (in conftest.py)
@pytest.fixture
def db_session():
# Sessione database per test
@pytest.fixture
def sample_user():
# Utente di esempio
@pytest.fixture
def sample_api_key():
# API key di esempio
🛡️ VINCOLI TECNICI
SQLAlchemy Configuration
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
Base = declarative_base()
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False} # Solo per SQLite
)
SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine,
expire_on_commit=False
)
Model Base Requirements
- Tutti i modelli ereditano da
Base - Usare type hints
- Configurare
__tablename__ - Definire relationships esplicite
- Usare
ondelete="CASCADE"per FK
Alembic Requirements
- Importare
Baseda models in env.py - Configurare
target_metadata = Base.metadata - Generare migration:
alembic revision --autogenerate -m "initial schema"
📊 AGGIORNAMENTO PROGRESS
Dopo ogni task completato, aggiorna:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/progress.md
Esempio:
### 🗄️ Database & Models (T06-T11)
- [x] T06: Creare database.py - Completato [timestamp]
- [x] T07: Creare model User - Completato [timestamp]
- [ ] T08: Creare model ApiKey - In progress
- [ ] T09: Creare model UsageStats
- [ ] T10: Creare model ApiToken
- [ ] T11: Setup Alembic e migrazione
**Progresso sezione:** 33% (2/6 task)
✅ CRITERI DI ACCETTAZIONE
- T06: database.py con engine, session, get_db(), init_db()
- T07: Model User completo con relationships e constraints
- T08: Model ApiKey completo con relationships
- T09: Model UsageStats con unique constraint e defaults
- T10: Model ApiToken completo con relationships
- T11: Alembic inizializzato con migrazione funzionante
- Tutti i test passano (
pytest tests/unit/models/) - Coverage >= 90%
- 6 commit atomici (uno per task)
- progress.md aggiornato con tutti i task completati
🚀 COMANDO DI VERIFICA
Al termine, esegui:
cd /home/google/Sources/LucaSacchiNet/openrouter-watcher
pytest tests/unit/models/ -v --cov=src/openrouter_monitor/models
alembic upgrade head
alembic downgrade -1
alembic upgrade head
📝 NOTE
- Usa SEMPRE path assoluti:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/ - Segui le convenzioni in
.opencode/agents/tdd-developer.md - Task devono essere verificabili in < 2 ore
- Documenta bug complessi in
/docs/bug_ledger.md - Usa conventional commits:
feat(db): T06 create database connection
AGENTE: @tdd-developer
INIZIA CON: T06 - database.py