package jnpf.dmsMapper; import jnpf.dmsEntity.storage.approval.DmsApprovedBatch; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Test; import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; class DmsApprovedBatchMapperXmlTest { @Test void receiptStatementsParseAndScannerOnlySelectsWaitingBatches() throws Exception { Configuration config = new Configuration(); String path = "mapper/DmsApprovedBatchMapper.xml"; try (java.io.InputStream stream = Resources.getResourceAsStream(path)) { new XMLMapperBuilder(stream, config, path, config.getSqlFragments()).parse(); } String prefix = DmsApprovedBatchMapper.class.getName() + "."; org.apache.ibatis.mapping.BoundSql insert = config.getMappedStatement(prefix + "insertBatch").getBoundSql(new DmsApprovedBatch()); assertEquals(14, insert.getParameterMappings().size()); String insertItem = config.getMappedStatement(prefix + "insertItem") .getBoundSql(new jnpf.dmsEntity.storage.approval.DmsApprovedItem()).getSql(); assertTrue(insertItem.contains("file_id")); assertTrue(insertItem.contains("business_version_id")); assertTrue(insertItem.contains("shifou_shoukong")); assertTrue(config.getResultMap(prefix + "Item").getResultMappings().stream() .anyMatch(r -> "shifou_shoukong".equals(r.getColumn()) && "shifouShoukong".equals(r.getProperty()))); String due = config.getMappedStatement(prefix + "due").getBoundSql(null).getSql(); assertTrue(due.contains("status = 'WAITING'")); assertFalse(due.contains("FAILED")); assertFalse(due.contains("EFFECTIVE")); assertFalse(due.contains("REGISTERING")); String registered = config.getMappedStatement(prefix + "registered").getBoundSql(null).getSql(); assertTrue(registered.contains("SET status = 'WAITING'")); assertTrue(registered.contains("AND status = 'REGISTERING'")); String activate = config.getMappedStatement(prefix + "activate").getBoundSql(null).getSql(); assertTrue(activate.contains("AND status IN ('WAITING','FAILED')")); String checksum = config.getMappedStatement(prefix + "storeChecksum").getBoundSql(null).getSql(); assertTrue(checksum.contains("tenant_id = ? AND batch_id = ? AND source_key = ?")); assertTrue(checksum.contains("AND checksum_sha256 IS NULL")); assertTrue(config.getResultMap(prefix + "Batch").getResultMappings().stream() .anyMatch(r -> "source_digest".equals(r.getColumn()) && "sourceDigest".equals(r.getProperty()))); assertTrue(config.getResultMap(prefix + "Batch").getResultMappings().stream() .anyMatch(r -> "approval_render_snapshot".equals(r.getColumn()) && "approvalRenderSnapshot".equals(r.getProperty()))); assertTrue(config.getResultMap(prefix + "Item").getResultMappings().stream() .anyMatch(r -> "storage_receipt".equals(r.getColumn()) && "storageReceipt".equals(r.getProperty()))); } @Test void disablingBackoffOnlyRemovesRetryTimeFromBothSelectionAndClaim() throws Exception { Configuration config = new Configuration(); String path = "mapper/DmsApprovedBatchMapper.xml"; try (java.io.InputStream stream = Resources.getResourceAsStream(path)) { new XMLMapperBuilder(stream, config, path, config.getSqlFragments()).parse(); } for (boolean enabled : new boolean[]{true,false}) { java.util.Map params = new java.util.HashMap<>(); params.put("retryBackoffEnabled",enabled); params.put("includeDisable",false); for (String name : new String[]{"retryCandidates","claim"}) { String sql = config.getMappedStatement(DmsApprovedBatchMapper.class.getName()+"."+name) .getBoundSql(params).getSql().replaceAll("\\s+"," "); assertEquals(enabled,sql.contains("next_attempt_at <= CURRENT_TIMESTAMP")); assertTrue(sql.contains("status <> 'APPLIED'")); assertTrue(sql.contains("lease_until IS NULL OR lease_until <= CURRENT_TIMESTAMP")); assertTrue(sql.contains("status <> 'WAITING' OR scheduled_at <= CURRENT_TIMESTAMP")); } } } @Test void uuidAllocationBypassesMybatisTransactionLocalCache() throws Exception { Configuration config = new Configuration(); String path = "mapper/DmsApprovedBatchMapper.xml"; try (java.io.InputStream stream = Resources.getResourceAsStream(path)) { new XMLMapperBuilder(stream, config, path, config.getSqlFragments()).parse(); } org.apache.ibatis.mapping.MappedStatement statement = config.getMappedStatement( DmsApprovedBatchMapper.class.getName() + ".nextUuidV7"); org.apache.ibatis.executor.BaseExecutor executor = new org.apache.ibatis.executor.BaseExecutor(config, null) { protected int doUpdate(org.apache.ibatis.mapping.MappedStatement s, Object p) { throw new UnsupportedOperationException(); } protected java.util.List doFlushStatements(boolean rollback) { return java.util.List.of(); } @SuppressWarnings("unchecked") protected java.util.List doQuery(org.apache.ibatis.mapping.MappedStatement s, Object p, org.apache.ibatis.session.RowBounds r, org.apache.ibatis.session.ResultHandler h, org.apache.ibatis.mapping.BoundSql b) { return (java.util.List) java.util.List.of(UUID.randomUUID().toString()); } protected org.apache.ibatis.cursor.Cursor doQueryCursor(org.apache.ibatis.mapping.MappedStatement s, Object p, org.apache.ibatis.session.RowBounds r, org.apache.ibatis.mapping.BoundSql b) { throw new UnsupportedOperationException(); } }; java.util.List first = executor.query(statement, null, org.apache.ibatis.session.RowBounds.DEFAULT, null); java.util.List second = executor.query(statement, null, org.apache.ibatis.session.RowBounds.DEFAULT, null); assertNotEquals(first, second, "A second allocation in the same transaction must execute the query again"); assertFalse(statement.isUseCache(), "Allocation must also bypass second-level caching"); } @Test void sqlSessionFactoryParsesApprovalMappersBeforeUuidPostProcessorRuns() throws Exception { com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean factory = new com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean(); factory.setDataSource(org.mockito.Mockito.mock(javax.sql.DataSource.class)); factory.setMapperLocations( new org.springframework.core.io.ClassPathResource("mapper/DmsApprovedBatchMapper.xml"), new org.springframework.core.io.ClassPathResource("mapper/DmsVersionStateMapper.xml")); // Production registers UUID globally only after the SqlSessionFactory has been built. Configuration config = factory.getObject().getConfiguration(); for (String map : new String[]{DmsApprovedBatchMapper.class.getName() + ".Batch", DmsApprovedBatchMapper.class.getName() + ".Item", DmsVersionStateMapper.class.getName() + ".State"}) { for (org.apache.ibatis.mapping.ResultMapping mapping : config.getResultMap(map).getResultMappings()) { if (UUID.class.equals(mapping.getJavaType())) assertInstanceOf(jnpf.dmsDatabase.DmsUuidTypeHandler.class, mapping.getTypeHandler()); } } } }