package jnpf.dmsMapper; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.*; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.*; import static org.junit.jupiter.api.Assertions.*; /** Actual mapper SQL, with connection-local tables only; no customer records or durable DDL. */ @EnabledIfEnvironmentVariable(named="DMS_READ_TEST_URL", matches=".+") class DmsTemplateNotificationPostgresTest { @Test void discoversOnlyCompletedMainUpgradesAndResolvesTenantMembers() throws Exception { var ds = new UnpooledDataSource("org.postgresql.Driver",System.getenv("DMS_READ_TEST_URL"), System.getenv("DMS_READ_TEST_USER"),System.getenv("DMS_READ_TEST_PASSWORD")); var config = new Configuration(new Environment("notification-test",new JdbcTransactionFactory(),ds)); config.setDefaultStatementTimeout(15); String path="mapper/DmsTemplateNotificationMapper.xml"; try(var input=getClass().getResourceAsStream("/"+path)) { String xml=new String(Objects.requireNonNull(input).readAllBytes(),StandardCharsets.UTF_8).replace("public.","pg_temp."); new XMLMapperBuilder(new StringReader(xml),config,path,config.getSqlFragments()).parse(); } try(var session=new SqlSessionFactoryBuilder().build(config).openSession(false)) { try { var c=session.getConnection(); try(var sql=c.createStatement()) { for(String table:List.of("dms_wenjian_xiuding_shenhe","workflow_task","base_user","base_user_relation","base_organize","dms_wenjian_jilu_muban_guanli")) sql.execute("CREATE TEMP TABLE "+table+" (LIKE public."+table+" INCLUDING DEFAULTS) ON COMMIT DROP"); sql.execute("CREATE TEMP TABLE dms_template_upgrade_notifications (tenant_id text,task_id text,audit_id text,template_id text,document_no text,document_name text,version_label text,actor_id text,attempts integer default 0,next_attempt_at timestamp default CURRENT_TIMESTAMP,lease_token text,lease_until timestamp,completed_at timestamp,last_error text,outcome text,PRIMARY KEY(tenant_id,task_id)) ON COMMIT DROP"); sql.execute("INSERT INTO pg_temp.workflow_task(f_id,f_tenant_id,f_flow_id,f_status,f_end_time,f_creator_user_id) SELECT id,'test','flow',2,CURRENT_TIMESTAMP,'actor' FROM unnest(ARRAY['ok','record','new','early','no-source','bad-json','object']) id"); sql.execute("UPDATE pg_temp.workflow_task SET f_status=1,f_end_time=NULL WHERE f_id='early'"); sql.execute("INSERT INTO pg_temp.dms_wenjian_xiuding_shenhe(f_id,f_tenant_id,f_flow_task_id,f_flow_id,f_flow_state,biangeng_fangshi,wenjian) SELECT f_id,'test',f_id,'flow',2,'shengji','[{\"fileId\":\"file\",\"laiyuan_muban_id\":\"template\"}]' FROM pg_temp.workflow_task"); sql.execute("UPDATE pg_temp.dms_wenjian_xiuding_shenhe SET biangeng_fangshi='jilu' WHERE f_id='record'"); sql.execute("UPDATE pg_temp.dms_wenjian_xiuding_shenhe SET biangeng_fangshi='xinzeng' WHERE f_id='new'"); sql.execute("UPDATE pg_temp.dms_wenjian_xiuding_shenhe SET wenjian='[{\"fileId\":\"file\"}]' WHERE f_id='no-source'"); sql.execute("UPDATE pg_temp.dms_wenjian_xiuding_shenhe SET wenjian='invalid' WHERE f_id='bad-json'"); sql.execute("UPDATE pg_temp.dms_wenjian_xiuding_shenhe SET wenjian='{}' WHERE f_id='object'"); sql.execute("INSERT INTO pg_temp.base_organize(f_id,f_tenant_id,f_parent_id) VALUES ('root','test','child'),('child','test','root'),('foreign','other','root')"); sql.execute("INSERT INTO pg_temp.base_user(f_id,f_tenant_id,f_organize_id,f_enabled_mark) VALUES ('direct','test','root',1),('child','test','child',1),('relation','test','elsewhere',1),('disabled','test','root',0),('deleted','test','root',1),('other','other','root',1)"); sql.execute("UPDATE pg_temp.base_user SET f_delete_mark=1 WHERE f_id='deleted'"); sql.execute("INSERT INTO pg_temp.base_user_relation(f_id,f_tenant_id,f_user_id,f_object_type,f_object_id) VALUES ('r1','test','relation','organize','child'),('r2','test','direct','organize','child')"); sql.execute("INSERT INTO pg_temp.dms_wenjian_jilu_muban_guanli(f_id,f_tenant_id,xiaoxi_tixing_bumen,wenben_mingcheng) VALUES ('template','test','[\"root\"]','DMS需求文档')"); } var mapper=session.getMapper(DmsTemplateNotificationMapper.class); assertEquals(1,mapper.discover()); assertEquals(0,mapper.discover()); assertEquals(1,mapper.pending().size()); var event=mapper.find("test","ok"); assertNotNull(event); assertEquals("template",event.getTemplateId()); assertNull(mapper.find("other","ok")); assertEquals(List.of("child","direct","relation"),mapper.recipients("test",List.of("root","child"))); assertEquals("[\"root\"]",mapper.template("test","template").get("departments")); assertEquals("DMS需求文档",mapper.template("test","template").get("name")); assertEquals(1,mapper.claim(event,"lease")); assertEquals(0,mapper.claim(event,"other")); assertEquals(0,mapper.finish(event,"wrong",null,"SENT")); assertEquals(1,mapper.finish(event,"lease","offline","RETRY")); assertTrue(mapper.pending().isEmpty()); try(var sql=c.createStatement()) { sql.execute("UPDATE pg_temp.dms_template_upgrade_notifications SET next_attempt_at=CURRENT_TIMESTAMP"); } assertEquals(1,mapper.claim(event,"retry")); assertEquals(1,mapper.finish(event,"retry",null,"SENT")); assertTrue(mapper.pending().isEmpty()); assertEquals(0,mapper.discover()); } finally { session.rollback(true); } } } }