diff --git a/backend/internal/web/handlers_etapes_test.go b/backend/internal/web/handlers_etapes_test.go index 1280c4c..c714b28 100644 --- a/backend/internal/web/handlers_etapes_test.go +++ b/backend/internal/web/handlers_etapes_test.go @@ -27,6 +27,76 @@ func newEtapeTestRouter(q *sqlc.Queries, store *auth.Store) http.Handler { return router } +func TestEtapeSchemaIsOneLevelAndTaskAssignmentUnassignsOnDelete(t *testing.T) { + pool, cleanup := setupTestDB(t) + defer cleanup() + + ctx := context.Background() + + rows, err := pool.Query(ctx, ` + SELECT column_name + FROM information_schema.columns + WHERE table_schema = current_schema() + AND table_name = 'etapes' + `) + if err != nil { + t.Fatalf("query etapes columns: %v", err) + } + defer rows.Close() + + etapeColumns := make(map[string]bool) + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + t.Fatalf("scan etapes column: %v", err) + } + etapeColumns[name] = true + } + if err := rows.Err(); err != nil { + t.Fatalf("iterate etapes columns: %v", err) + } + for _, forbidden := range []string{"parent_id", "parent_etape_id", "etape_id"} { + if etapeColumns[forbidden] { + t.Fatalf("etapes has nested-etape column %q; want one-level etapes only", forbidden) + } + } + + var nullable string + if err := pool.QueryRow(ctx, ` + SELECT is_nullable + FROM information_schema.columns + WHERE table_schema = current_schema() + AND table_name = 'tasks' + AND column_name = 'etape_id' + `).Scan(&nullable); err != nil { + t.Fatalf("query tasks.etape_id nullability: %v", err) + } + if nullable != "YES" { + t.Fatalf("tasks.etape_id is_nullable = %q; want YES", nullable) + } + + var deleteRule string + if err := pool.QueryRow(ctx, ` + SELECT rc.delete_rule + FROM information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu + ON tc.constraint_schema = kcu.constraint_schema + AND tc.constraint_name = kcu.constraint_name + JOIN information_schema.referential_constraints AS rc + ON tc.constraint_schema = rc.constraint_schema + AND tc.constraint_name = rc.constraint_name + WHERE tc.table_schema = current_schema() + AND tc.table_name = 'tasks' + AND tc.constraint_type = 'FOREIGN KEY' + AND kcu.column_name = 'etape_id' + `).Scan(&deleteRule); err != nil { + t.Fatalf("query tasks.etape_id delete rule: %v", err) + } + if deleteRule != "SET NULL" { + t.Fatalf("tasks.etape_id delete rule = %q; want SET NULL", deleteRule) + } +} + func TestEtapeCreateRendersChipAndCount(t *testing.T) { pool, cleanup := setupTestDB(t) defer cleanup()