diff --git a/REFINED_SCHEMA_PLAN.md b/REFINED_SCHEMA_PLAN.md index 2c08f04..d610746 100644 --- a/REFINED_SCHEMA_PLAN.md +++ b/REFINED_SCHEMA_PLAN.md @@ -16,6 +16,17 @@ ## 📊 Technical Decisions +### PostgreSQL Version Requirements + +**Minimum: PostgreSQL 13** (for `gen_random_uuid()`) +**Recommended: PostgreSQL 15** (matches `docker-compose.yml`) +**IF NOT EXISTS syntax:** PostgreSQL 9.5+ (already satisfied by minimum version) + +**Rationale:** +- PostgreSQL 13 introduced `gen_random_uuid()` as a built-in function +- PostgreSQL 15 is the current stable release and matches our deployment target +- All schema features (IF NOT EXISTS, CREATE OR REPLACE FUNCTION) work with PostgreSQL 9.5+ + ### 1. File Structure: Single Source of Truth **Decision:** Single `internal/database/schema.go` file referencing existing `database/schema/schema.sql`. @@ -300,6 +311,7 @@ func executeSchema(ctx context.Context, db *pgxpool.Pool) error { _, err = tx.Exec(ctx, SchemaFile) if err != nil { + log.Printf("HINT: Run manually to debug: psql -h localhost -U postgres -d bookhoard -f database/schema/schema.sql") return fmt.Errorf("schema execution failed: %w", err) } @@ -414,6 +426,53 @@ ls -la ../../database/schema/schema.sql # Should show the file exists at the correct relative location ``` +#### Step 2.3: Add Unit Tests for schema.go + +Create `internal/database/schema_test.go`: + +```go +package database + +import ( + "context" + "testing" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/stretchr/testify/assert" +) + +func TestParseTableNames(t *testing.T) { + tables, err := parseTableNames() + assert.NoError(t, err) + assert.Greater(t, len(tables), 20, "Should parse at least 20 tables") + + // Verify critical tables are detected + tableMap := make(map[string]bool) + for _, table := range tables { + tableMap[table] = true + } + assert.True(t, tableMap["users"], "Should find 'users' table") + assert.True(t, tableMap["libraries"], "Should find 'libraries' table") + assert.True(t, tableMap["media_items"], "Should find 'media_items' table") +} + +func TestGenerateLockID(t *testing.T) { + result := generateLockID("bookhoard:schema:init") + assert.Equal(t, int64(7804706162000639061), result, "FNV-1a hash must match expected value") +} + +func TestVerifyTables_MissingTables(t *testing.T) { + // This would require a mock database or test setup + // For now, skip or implement with pgxpool mock + t.Skip("Requires database connection - implement in integration tests") +} +``` + +Run tests with: +```bash +go test ./internal/database -v +``` + --- ### Phase 3: Integrate into main.go @@ -481,6 +540,7 @@ func main() { - [ ] No schema file duplication (single source of truth) - [ ] Verifies critical functions exist, not just tables - [ ] Provides clear error messages for missing schema components +- [ ] Includes debug hint when schema execution fails (psql command) **Phase 3 Verification:** - [ ] `cmd/server/main.go` compiles without errors @@ -491,7 +551,7 @@ func main() { **Integration Testing:** - [ ] Fresh database initializes correctly - [ ] Existing database doesn't break -- [ ] Concurrent startup works with advisory locks +- [ ] Concurrent startup works with advisory locks (manual test below) - [ ] Partial state recovers successfully - [ ] TestKoboInitialization passes - [ ] All integration tests pass @@ -502,6 +562,32 @@ func main() { - [ ] `go build ./...` succeeds for entire project - [ ] `bash scripts/verify-guidelines.sh` passes (0 errors) +**Concurrent Startup Test (Manual):** +```bash +# Test advisory locks work correctly with multiple instances +# Start 5 instances simultaneously +for i in {1..5}; do + podman compose -p test$i up -d app + sleep 0.5 # Stagger starts slightly +done + +# Wait 10 seconds for all to initialize +sleep 10 + +# Check all logs for successful initialization +for i in {1..5}; do + echo "=== Instance $i ===" + podman logs test$i_bookhoard_app 2>&1 | grep "schema initialization complete" +done + +# Cleanup +for i in {1..5}; do + podman compose -p test$i down +done +``` + +**Expected result:** All 5 instances should show "schema initialization complete" in logs, proving advisory locks prevent race conditions. + --- ## 📈 Expected Outcomes @@ -585,6 +671,23 @@ If new version has schema issues: ## 📞 Support +### PostgreSQL Version Issues + +If you see errors about `gen_random_uuid()` not existing: +```bash +# Check PostgreSQL version +podman exec bookhoard_db psql --version +# Should be 13.0 or higher +``` + +If using an older PostgreSQL version, update `docker-compose.yml`: +```yaml +db: + image: postgres:15-alpine # Or postgres:13-alpine minimum +``` + +### Troubleshooting + If issues occur: ### Check logs first: @@ -623,8 +726,9 @@ podman compose up -d ## 📚 Documentation Create documentation explaining the automatic schema initialization: -- **docs/contributing/database-schema.md** - How schema initialization works, how to modify schema safely -- **README.md** - Add "Database Initialization" section documenting first-run behavior +- **docs/contributing/database-schema.md** - How schema initialization works, how to modify schema safely, PostgreSQL version requirements +- **README.md** - Add "Database Initialization" section documenting first-run behavior and PostgreSQL version requirements +- **internal/database/schema_test.go** - Unit tests for schema parsing and verification functions ---