package jnpf.dmsMapper; import jnpf.dmsDatabase.DmsUuidTypeHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DmsVersionStateMapperSqlTest { @Test void expiryPreservesValidityFactsAndRequiresDueDeadlineForOneTenantVersion() { Map parameters = revisionParameters(); parameters.put("effectiveFrom", new java.util.Date(1726329600000L)); parameters.put("effectiveTo", new java.util.Date(1757865600000L)); parameters.put("statusReason", "到期检查确认"); parameters.put("changedBy", "jnpf-file"); String sql = sql("expire", parameters); assertTrue(sql.contains("lifecycle_status = 'EXPIRED'")); assertFalse(sql.contains("effective_from =")); assertFalse(sql.contains("effective_to =")); assertTrue(sql.contains("effective_to <= statement_timestamp()")); assertTrue(sql.contains("tenant_id = ?")); assertTrue(sql.contains("file_version_id = ?")); assertTrue(sql.contains("lifecycle_status = 'EFFECTIVE'")); assertTrue(sql.contains("revision = revision + 1")); } @Test void directUploadCreatesImmediatelyEffectiveLifecycleState() { Configuration configuration = new Configuration(); configuration.getTypeHandlerRegistry().register(UUID.class, DmsUuidTypeHandler.class); loadXml(configuration); MappedStatement statement = configuration.getMappedStatement( DmsVersionStateMapper.class.getName() + ".insertDirectEffective"); Map parameters = new HashMap<>(); parameters.put("tenantId", "tenant-1"); parameters.put("documentId", UUID.fromString("018f0000-0000-7000-8000-000000000301")); parameters.put("fileVersionId", UUID.fromString("018f0000-0000-7000-8000-000000000302")); parameters.put("userId", "user-1"); BoundSql boundSql = statement.getBoundSql(parameters); String sql = boundSql.getSql().replaceAll("\\s+", " ").trim(); assertTrue(sql.startsWith("INSERT INTO public.dms_version_states")); assertTrue(sql.contains("'EFFECTIVE'")); assertTrue(sql.contains("'DIRECT'")); assertTrue(sql.contains("CURRENT_TIMESTAMP")); assertTrue(sql.contains("CAST(? AS TEXT)")); assertFalse(sql.contains("SELECT *")); } @Test void revisionSupersedesOnlyTheExpectedCurrentVersion() { String sql = sql("supersedeCurrent", revisionParameters()); assertTrue(sql.startsWith("UPDATE public.dms_version_states")); assertTrue(sql.contains("lifecycle_status = 'SUPERSEDED'")); assertTrue(sql.contains("file_version_id = ?")); assertTrue(sql.contains("lifecycle_status = 'EFFECTIVE'")); assertTrue(sql.contains("effective_to = CURRENT_TIMESTAMP")); assertTrue(sql.contains("revision = revision + 1")); } @Test void revisionCreatesNewEffectiveStateWithIdempotencySource() { String sql = sql("insertRevisionEffective", revisionParameters()); assertTrue(sql.startsWith("INSERT INTO public.dms_version_states")); assertTrue(sql.contains("'EFFECTIVE'")); assertTrue(sql.contains("CONCAT('revision:', CAST(? AS TEXT), ':', ?)")); assertTrue(sql.contains("CURRENT_TIMESTAMP")); assertFalse(sql.contains("SELECT *")); } @Test void workflowUploadCreatesPendingApprovalState() { Map parameters = revisionParameters(); parameters.put("approvalSourceId", "workflow:key"); parameters.put("flowId", "flow-1"); parameters.put("taskId", "task-1"); parameters.put("fileType", "CREATE"); String sql = sql("insertWorkflowPending", parameters); assertTrue(sql.contains("'PENDING_APPROVAL'")); assertTrue(sql.contains("'WORKFLOW'")); assertTrue(sql.contains("workflow_flow_id")); assertFalse(sql.contains("SELECT *")); } @Test void activationOnlyUpdatesPendingApprovalState() { String sql = sql("activateWorkflowPending", revisionParameters()); assertTrue(sql.contains("lifecycle_status = 'EFFECTIVE'")); assertTrue(sql.contains("lifecycle_status = 'PENDING_APPROVAL'")); assertTrue(sql.contains("workflow_flow_id = ?")); assertTrue(sql.contains("workflow_task_id = ?")); assertTrue(sql.contains("workflow_file_type = ?")); assertTrue(sql.contains("revision = revision + 1")); assertTrue(sql.contains("archived_at = CURRENT_TIMESTAMP")); } @Test void readableVersionExcludesPendingAndNeverApprovedInvalidatedStates() { Configuration configuration = new Configuration(); configuration.getTypeHandlerRegistry().register(UUID.class, DmsUuidTypeHandler.class); configuration.addMapper(DmsFileVersionMapper.class); MappedStatement statement = configuration.getMappedStatement( DmsFileVersionMapper.class.getName() + ".selectReadableByTenantAndId"); Map parameters = revisionParameters(); parameters.put("id", parameters.get("fileVersionId")); String sql = statement.getBoundSql(parameters).getSql().replaceAll("\\s+", " ").trim(); assertFalse(sql.contains("PENDING_APPROVAL")); assertFalse(sql.contains("PENDING_EFFECTIVE")); assertTrue(sql.contains("s.lifecycle_status = 'INVALIDATED' AND s.approved_at IS NOT NULL")); } private void loadXml(Configuration configuration) { String resource = "mapper/DmsVersionStateMapper.xml"; try (java.io.InputStream input = org.apache.ibatis.io.Resources.getResourceAsStream(resource)) { new org.apache.ibatis.builder.xml.XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse(); } catch (java.io.IOException e) { throw new IllegalStateException(e); } } private String sql(String method, Map parameters) { Configuration configuration = new Configuration(); configuration.getTypeHandlerRegistry().register(UUID.class, DmsUuidTypeHandler.class); loadXml(configuration); MappedStatement statement = configuration.getMappedStatement( DmsVersionStateMapper.class.getName() + "." + method); return statement.getBoundSql(parameters).getSql().replaceAll("\\s+", " ").trim(); } private Map revisionParameters() { Map parameters = new HashMap<>(); parameters.put("tenantId", "tenant-1"); parameters.put("documentId", UUID.fromString("018f0000-0000-7000-8000-000000000301")); parameters.put("baseFileVersionId", UUID.fromString("018f0000-0000-7000-8000-000000000302")); parameters.put("fileVersionId", UUID.fromString("018f0000-0000-7000-8000-000000000303")); parameters.put("sourceId", "revision-1"); parameters.put("revisionReason", "更新质量职责"); parameters.put("newVersionLabel", "D(01)"); parameters.put("userId", "user-1"); return parameters; } }