diff --git a/resources/sql/004_metadata_auth_management.sql b/resources/sql/004_metadata_auth_management.sql index 3e58a88..16fc753 100644 --- a/resources/sql/004_metadata_auth_management.sql +++ b/resources/sql/004_metadata_auth_management.sql @@ -51,20 +51,3 @@ ALTER TABLE users CREATE UNIQUE INDEX IF NOT EXISTS idx_users_keycloak_id ON users(keycloak_id); CREATE INDEX IF NOT EXISTS idx_users_role ON users(role); CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active); - -CREATE TABLE IF NOT EXISTS user_project_membership ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL, - project_id UUID NOT NULL, - project_role VARCHAR(20) DEFAULT 'viewer' NOT NULL, - CONSTRAINT user_project_membership_role_check - CHECK ( - project_role IN ('member', 'viewer') - ), - CONSTRAINT user_project_membership_unique UNIQUE (user_id, project_id) -); - -CREATE INDEX IF NOT EXISTS idx_user_project_membership_user_id - ON user_project_membership(user_id); -CREATE INDEX IF NOT EXISTS idx_user_project_membership_project_id - ON user_project_membership(project_id); diff --git a/resources/sql/005_metadata_project_configuration.sql b/resources/sql/005_metadata_project_configuration.sql index d3f9e12..bda3627 100644 --- a/resources/sql/005_metadata_project_configuration.sql +++ b/resources/sql/005_metadata_project_configuration.sql @@ -25,7 +25,6 @@ CREATE TABLE IF NOT EXISTS projects ( ); CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status); -CREATE INDEX IF NOT EXISTS idx_projects_code ON projects(code); DROP TRIGGER IF EXISTS update_projects_updated_at ON projects; CREATE TRIGGER update_projects_updated_at @@ -33,6 +32,20 @@ CREATE TRIGGER update_projects_updated_at FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); +CREATE TABLE IF NOT EXISTS user_project_membership ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, + project_role VARCHAR(20) DEFAULT 'viewer' NOT NULL, + CONSTRAINT user_project_membership_role_check + CHECK (project_role IN ('member', 'viewer')), + CONSTRAINT user_project_membership_unique UNIQUE (user_id, project_id) +); + +-- The unique (user_id, project_id) index already supports user-side lookups. +CREATE INDEX IF NOT EXISTS idx_user_project_membership_project_id + ON user_project_membership(project_id); + CREATE TABLE IF NOT EXISTS project_databases ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, diff --git a/tests/auth/test_rbac_migration.py b/tests/auth/test_rbac_migration.py index 8111be4..a8cd3ac 100644 --- a/tests/auth/test_rbac_migration.py +++ b/tests/auth/test_rbac_migration.py @@ -1,6 +1,35 @@ from pathlib import Path +def test_metadata_schema_creates_membership_after_referenced_tables(): + auth_sql = Path("resources/sql/004_metadata_auth_management.sql").read_text( + encoding="utf-8" + ) + project_sql = Path( + "resources/sql/005_metadata_project_configuration.sql" + ).read_text(encoding="utf-8") + + assert "CREATE TABLE IF NOT EXISTS user_project_membership" not in auth_sql + assert project_sql.index("CREATE TABLE IF NOT EXISTS projects") < project_sql.index( + "CREATE TABLE IF NOT EXISTS user_project_membership" + ) + assert "user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE" in project_sql + assert ( + "project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE" + in project_sql + ) + + +def test_metadata_schema_avoids_indexes_duplicated_by_unique_constraints(): + project_sql = Path( + "resources/sql/005_metadata_project_configuration.sql" + ).read_text(encoding="utf-8") + + assert "idx_projects_code" not in project_sql + assert "idx_user_project_membership_user_id" not in project_sql + assert "idx_user_project_membership_project_id" in project_sql + + def test_rbac_migration_normalizes_legacy_roles(): sql = Path("resources/sql/006_metadata_rbac_roles.sql").read_text( encoding="utf-8"