-- DMS file-property and version-lifecycle database regression. -- Prerequisite: DMS V2.3 plus upgrade_dms_file_properties_20260820.sql. -- The entire test runs in one transaction and always rolls back its test data. \set ON_ERROR_STOP on SET lock_timeout = '5s'; SET statement_timeout = '60s'; BEGIN; DO $dms_file_property_schema_assertions$ DECLARE required_object TEXT; BEGIN FOREACH required_object IN ARRAY ARRAY[ 'public.dms_version_states', 'public.dms_version_relations', 'public.uni_dms_documents_document_no', 'public.uni_dms_file_versions_document_label', 'public.uni_dms_file_versions_document_id_id', 'public.uni_dms_version_states_document_effective', 'public.idx_dms_version_states_document_status', 'public.idx_dms_version_states_pending_effective', 'public.idx_dms_version_states_review_due' ] LOOP IF to_regclass(required_object) IS NULL THEN RAISE EXCEPTION 'required DMS file-property object % is missing', required_object; END IF; END LOOP; END; $dms_file_property_schema_assertions$; INSERT INTO public.dms_folders ( id, tenant_id, parent_id, name, created_by ) VALUES ( '019d1000-0000-7000-8000-000000000001', 'dms-file-property-test', NULL, 'File property regression root', 'dms-file-property-test' ); INSERT INTO public.dms_permission_resources ( id, tenant_id, resource_type, source_type, source_id, registered_by ) VALUES ( '019d1000-0000-7000-8000-000000000001', 'dms-file-property-test', 'FOLDER', 'MIGRATION', 'file-property-folder', 'dms-file-property-test' ); -- Document A starts in the legacy/raw-upload state so one-time completion can be tested. INSERT INTO public.dms_documents ( id, tenant_id, folder_id, name, created_by ) VALUES ( '019d1000-0000-7000-8000-000000000002', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000001', 'Controlled document A', 'dms-file-property-test' ); INSERT INTO public.dms_permission_resources ( id, tenant_id, resource_type, source_type, source_id, registered_by ) VALUES ( '019d1000-0000-7000-8000-000000000002', 'dms-file-property-test', 'DOCUMENT', 'MIGRATION', 'file-property-document-a', 'dms-file-property-test' ); INSERT INTO public.dms_file_versions ( id, tenant_id, document_id, version_no, file_name, created_by ) VALUES ( '019d1000-0000-7000-8000-000000000003', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', 1, 'controlled-document-a-v1.docx', 'dms-file-property-test' ); INSERT INTO public.dms_permission_resources ( id, tenant_id, resource_type, source_type, source_id, registered_by ) VALUES ( '019d1000-0000-7000-8000-000000000003', 'dms-file-property-test', 'FILE_VERSION', 'MIGRATION', 'file-property-version-a1', 'dms-file-property-test' ); DO $dms_incomplete_metadata_rejected$ BEGIN BEGIN INSERT INTO public.dms_version_states ( file_version_id, tenant_id, document_id, lifecycle_status, approved_at, approved_by, archived_at, effective_from, approval_source_type, approval_source_id, status_changed_by ) VALUES ( '019d1000-0000-7000-8000-000000000003', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', 'EFFECTIVE', CURRENT_TIMESTAMP - INTERVAL '3 days', 'quality-user', CURRENT_TIMESTAMP - INTERVAL '2 days', CURRENT_TIMESTAMP - INTERVAL '1 day', 'MIGRATION', 'incomplete-state-must-fail', 'quality-user' ); RAISE EXCEPTION 'incomplete business metadata entered the lifecycle'; EXCEPTION WHEN check_violation THEN NULL; END; END; $dms_incomplete_metadata_rejected$; UPDATE public.dms_documents SET document_no = 'MM-R02', document_type = 'QUALITY_MANUAL', responsible_dept_id = 'dept-quality' WHERE id = '019d1000-0000-7000-8000-000000000002'; UPDATE public.dms_file_versions SET version_label = 'D(09)', prepared_by = 'author-a', prepared_on = DATE '2023-08-30' WHERE id = '019d1000-0000-7000-8000-000000000003'; DO $dms_completed_metadata_immutable$ BEGIN BEGIN UPDATE public.dms_documents SET document_no = 'MM-R03' WHERE id = '019d1000-0000-7000-8000-000000000002'; RAISE EXCEPTION 'completed document number was changed'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; BEGIN UPDATE public.dms_file_versions SET version_label = 'D(10)' WHERE id = '019d1000-0000-7000-8000-000000000003'; RAISE EXCEPTION 'completed version label was changed'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; END; $dms_completed_metadata_immutable$; INSERT INTO public.dms_version_states ( file_version_id, tenant_id, document_id, lifecycle_status, approved_at, approved_by, archived_at, effective_from, effective_to, review_cycle_months, next_review_on, approval_source_type, approval_source_id, status_changed_by ) VALUES ( '019d1000-0000-7000-8000-000000000003', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', 'EFFECTIVE', CURRENT_TIMESTAMP - INTERVAL '3 days', 'quality-user', CURRENT_TIMESTAMP - INTERVAL '2 days', CURRENT_TIMESTAMP - INTERVAL '1 day', CURRENT_TIMESTAMP + INTERVAL '365 days', 36, CURRENT_DATE + 365, 'WORKFLOW', 'workflow-a-v1-approved', 'quality-user' ); -- A second revision is already approved and due for promotion, but remains pending. INSERT INTO public.dms_file_versions ( id, tenant_id, document_id, version_no, version_label, file_name, prepared_by, prepared_on, created_by ) VALUES ( '019d1000-0000-7000-8000-000000000004', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', 2, 'D(10)', 'controlled-document-a-v2.docx', 'author-b', CURRENT_DATE - 10, 'dms-file-property-test' ); INSERT INTO public.dms_permission_resources ( id, tenant_id, resource_type, source_type, source_id, registered_by ) VALUES ( '019d1000-0000-7000-8000-000000000004', 'dms-file-property-test', 'FILE_VERSION', 'MIGRATION', 'file-property-version-a2', 'dms-file-property-test' ); INSERT INTO public.dms_version_states ( file_version_id, tenant_id, document_id, lifecycle_status, approved_at, approved_by, archived_at, effective_from, approval_source_type, approval_source_id, status_changed_by ) VALUES ( '019d1000-0000-7000-8000-000000000004', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', 'PENDING_EFFECTIVE', CURRENT_TIMESTAMP - INTERVAL '3 hours', 'quality-user', CURRENT_TIMESTAMP - INTERVAL '2 hours', CURRENT_TIMESTAMP - INTERVAL '1 hour', 'WORKFLOW', 'workflow-a-v2-approved', 'quality-user' ); DO $dms_current_pending_not_history$ DECLARE current_count INTEGER; pending_count INTEGER; history_count INTEGER; BEGIN SELECT count(*) INTO current_count FROM public.dms_version_states WHERE tenant_id = 'dms-file-property-test' AND document_id = '019d1000-0000-7000-8000-000000000002' AND lifecycle_status = 'EFFECTIVE' AND effective_from <= CURRENT_TIMESTAMP AND (effective_to IS NULL OR CURRENT_TIMESTAMP < effective_to); SELECT count(*) INTO pending_count FROM public.dms_version_states WHERE tenant_id = 'dms-file-property-test' AND document_id = '019d1000-0000-7000-8000-000000000002' AND lifecycle_status = 'PENDING_EFFECTIVE'; SELECT count(*) INTO history_count FROM public.dms_version_states WHERE tenant_id = 'dms-file-property-test' AND document_id = '019d1000-0000-7000-8000-000000000002' AND lifecycle_status IN ('SUPERSEDED', 'EXPIRED', 'REVOKED', 'INVALIDATED', 'OBSOLETE'); IF current_count <> 1 OR pending_count <> 1 OR history_count <> 0 THEN RAISE EXCEPTION 'unexpected classification: current %, pending %, history %', current_count, pending_count, history_count; END IF; END; $dms_current_pending_not_history$; -- Promotion is one transaction: retire the current revision before activating the pending one. UPDATE public.dms_version_states SET lifecycle_status = 'SUPERSEDED', effective_to = CURRENT_TIMESTAMP, status_reason = 'Superseded by D(10)', status_changed_by = 'dms-scheduler', revision = revision + 1 WHERE file_version_id = '019d1000-0000-7000-8000-000000000003'; UPDATE public.dms_version_states SET lifecycle_status = 'EFFECTIVE', status_reason = 'Scheduled promotion', status_changed_by = 'dms-scheduler', revision = revision + 1 WHERE file_version_id = '019d1000-0000-7000-8000-000000000004'; -- Review extension changes the lifecycle row, not the immutable FileVersion. UPDATE public.dms_version_states SET review_cycle_months = 24, last_reviewed_on = CURRENT_DATE, next_review_on = CURRENT_DATE + 730, effective_to = CURRENT_TIMESTAMP + INTERVAL '730 days', status_reason = 'Periodic review passed', status_changed_by = 'quality-user', revision = revision + 1 WHERE file_version_id = '019d1000-0000-7000-8000-000000000004'; DO $dms_lifecycle_after_promotion$ BEGIN IF (SELECT count(*) FROM public.dms_version_states WHERE tenant_id = 'dms-file-property-test' AND document_id = '019d1000-0000-7000-8000-000000000002' AND lifecycle_status = 'EFFECTIVE') <> 1 THEN RAISE EXCEPTION 'promotion did not leave exactly one current revision'; END IF; IF (SELECT count(*) FROM public.dms_version_states WHERE tenant_id = 'dms-file-property-test' AND document_id = '019d1000-0000-7000-8000-000000000002' AND lifecycle_status IN ('SUPERSEDED', 'EXPIRED', 'REVOKED', 'INVALIDATED', 'OBSOLETE')) <> 1 THEN RAISE EXCEPTION 'superseded revision was not classified as history'; END IF; BEGIN UPDATE public.dms_version_states SET lifecycle_status = 'EFFECTIVE', effective_to = CURRENT_TIMESTAMP + INTERVAL '30 days', status_reason = 'Illegal rollback', status_changed_by = 'quality-user', revision = revision + 1 WHERE file_version_id = '019d1000-0000-7000-8000-000000000003'; RAISE EXCEPTION 'terminal lifecycle state was reversed'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; END; $dms_lifecycle_after_promotion$; -- Document B is the target of a version-owned related-file declaration. INSERT INTO public.dms_documents ( id, tenant_id, folder_id, document_no, name, document_type, responsible_dept_id, created_by ) VALUES ( '019d1000-0000-7000-8000-000000000005', 'dms-file-property-test', '019d1000-0000-7000-8000-000000000001', 'SOP-R01', 'Related document B', 'SOP', 'dept-quality', 'dms-file-property-test' ); INSERT INTO public.dms_permission_resources ( id, tenant_id, resource_type, source_type, source_id, registered_by ) VALUES ( '019d1000-0000-7000-8000-000000000005', 'dms-file-property-test', 'DOCUMENT', 'MIGRATION', 'file-property-document-b', 'dms-file-property-test' ); INSERT INTO public.dms_version_relations ( tenant_id, document_id, file_version_id, related_document_id, relation_type, created_by ) VALUES ( 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', '019d1000-0000-7000-8000-000000000004', '019d1000-0000-7000-8000-000000000005', 'REFERENCE', 'quality-user' ); DO $dms_related_document_constraints$ BEGIN BEGIN INSERT INTO public.dms_version_relations ( tenant_id, document_id, file_version_id, related_document_id, relation_type, created_by ) VALUES ( 'dms-file-property-test', '019d1000-0000-7000-8000-000000000002', '019d1000-0000-7000-8000-000000000004', '019d1000-0000-7000-8000-000000000002', 'REFERENCE', 'quality-user' ); RAISE EXCEPTION 'self-related document was accepted'; EXCEPTION WHEN check_violation THEN NULL; END; BEGIN DELETE FROM public.dms_version_relations WHERE tenant_id = 'dms-file-property-test'; RAISE EXCEPTION 'append-only version relation was deleted'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; END; $dms_related_document_constraints$; SET CONSTRAINTS ALL IMMEDIATE; ROLLBACK; \echo 'DMS file-property and version-lifecycle regression passed.'