from pathlib import Path def test_is_locked_migration_is_idempotent(): source = Path("alembic/versions/20260116_01_add_is_locked_to_studies.py").read_text(encoding="utf-8") assert 'if "is_locked" not in columns:' in source assert 'if "is_locked" in columns:' in source def test_removed_workflow_tables_are_dropped_conditionally(): source = Path("alembic/versions/20260116_05_remove_document_workflows.py").read_text(encoding="utf-8") assert 'if "workflow_actions" in tables:' in source assert 'if "version_workflows" in tables:' in source assert 'if "workflow_nodes" in tables:' in source assert 'if "workflow_templates" in tables:' in source def test_migration_state_check_script_exists(): source = Path("scripts/check_migration_state.py").read_text(encoding="utf-8") assert "missing alembic_version table" in source assert "studies" in source assert "subjects" in source assert "monitoring_visit_issues" in source def test_remove_qa_role_migration_casts_json_permissions_for_key_lookup(): source = Path("alembic/versions/20260521_01_remove_qa_role.py").read_text(encoding="utf-8") assert "permissions ? 'QA'" not in source assert "permissions::jsonb ? 'QA'" in source def test_role_template_copy_migration_updates_display_copy_for_current_role_keys(): source = Path("alembic/versions/20260522_01_update_role_template_copy.py").read_text(encoding="utf-8") assert "项目负责人,统筹项目全局,协调进度、资源与关键决策。" in source assert "负责各中心临床监查执行,跟进现场质量、数据和问题闭环。" in source assert "负责合同、药品及相关项目事务管理,保障执行支持与物资协同。" in source assert "负责医学审核与稽查,关注质量风险、合规性和医学一致性。" in source assert "负责药物警戒相关工作,跟踪安全性事件并支持风险评估。" in source assert '"IMP": ("CTA"' in source assert '"MEDICAL_REVIEW": ("QA"' in source assert "category = 'QA'" not in source def test_permission_template_migrations_do_not_seed_stale_startup_permissions(): stale_keys = ( "budget:create", "budget:list", "budget:read", "budget:update", "budget:delete", "timeline:create", "timeline:list", "timeline:read", "timeline:update", "timeline:delete", ) for path in Path("alembic/versions").glob("*.py"): if path.name == "20260527_04_remove_stale_startup_permissions.py": continue source = path.read_text(encoding="utf-8") for key in stale_keys: assert key not in source, f"{key} should not be seeded by {path}" def test_legacy_startup_ethics_permission_keys_are_removed_by_followup_migration(): source = Path("alembic/versions/20260527_05_remove_legacy_startup_ethics_permission_keys.py").read_text(encoding="utf-8") assert '"feasibility:create"' in source assert '"feasibility:list"' in source assert '"feasibility:read"' in source assert '"feasibility:update"' in source assert '"feasibility:delete"' in source assert '"ethics:create"' in source assert '"ethics:list"' in source assert '"ethics:read"' in source assert '"ethics:update"' in source assert '"ethics:delete"' in source assert "startup_initiation:" not in source assert "startup_ethics:" not in source assert "DELETE FROM api_endpoint_permissions" in source assert "api_endpoint_permissions" in source assert "permission_templates" in source assert "permission_template_versions" in source def test_precautions_migration_renames_table_and_attachment_entity_type(): source = Path("alembic/versions/20260527_08_rename_knowledge_notes_to_precautions.py").read_text(encoding="utf-8") assert 'rename_table("knowledge_notes", "precautions")' in source assert "entity_type = 'precaution'" in source assert "entity_type = 'knowledge_note'" in source assert "api_endpoint_permissions" in source assert "permission_templates" in source def test_etmf_migration_reuses_existing_document_scope_type(): source = Path("alembic/versions/20260527_09_add_etmf_nodes.py").read_text(encoding="utf-8") assert "postgresql.ENUM(" in source assert 'name="document_scope_type"' in source assert "create_type=False" in source assert "scope_type = sa.Enum" not in source