package jnpf.limsService.impl; import jnpf.base.ActionResult; import jnpf.base.UserInfo; import jnpf.eln.RecordFreezeApi; import jnpf.eln.RecordFreezeCheckBatch; import jnpf.eln.RecordFreezeCheckRequest; import jnpf.eln.RecordFreezeCheckResult; import jnpf.eln.RecordFreezeReleaseRequest; import jnpf.exception.DataException; import jnpf.limsEntity.JianyanStatus; import jnpf.limsEntity.LimsJianyanXiangEntity; import org.junit.After; import org.junit.Test; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class LimsElnRecordFreezeGateTest { @After public void clearSynchronization() { if (TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronizationManager.clearSynchronization(); } } @Test public void allowsItemsWithoutElnRecordAndReleasesNoLease() { AtomicInteger releaseCalls = new AtomicInteger(); LimsElnRecordFreezeGate gate = new LimsElnRecordFreezeGate(api(request -> batch("item-1", RecordFreezeCheckResult.NOT_APPLICABLE, null), releaseCalls)); TransactionSynchronizationManager.initSynchronization(); gate.prepareAndReleaseAfterCompletion("tenant-1", user(), Collections.singletonList("item-1"), rows()); assertEquals(1, TransactionSynchronizationManager.getSynchronizations().size()); TransactionSynchronizationManager.getSynchronizations().get(0).afterCompletion(0); assertEquals(1, releaseCalls.get()); } @Test public void returnsElnReadySubmissionValuesWithoutExposingFieldCatalog() { LimsElnRecordFreezeGate gate = new LimsElnRecordFreezeGate(api(request -> batch("item-1", RecordFreezeCheckResult.READY, "lease-1"), new AtomicInteger())); TransactionSynchronizationManager.initSynchronization(); Map values = gate.prepareAndReleaseAfterCompletion("tenant-1", user(), Collections.singletonList("item-1"), rows()); assertEquals("ELN 检验结果", values.get("item-1").getTestResult()); assertEquals("compliant", values.get("item-1").getTestConclusion()); } @Test public void usesSingleTenantIdWhenLoginContextHasNoTenant() { AtomicReference preparedTenant = new AtomicReference<>(); AtomicReference releasedTenant = new AtomicReference<>(); RecordFreezeApi api = new RecordFreezeApi() { @Override public ActionResult prepare(RecordFreezeCheckRequest request) { preparedTenant.set(request.getTenantId()); return response(batch("item-1", RecordFreezeCheckResult.NOT_APPLICABLE, null)); } @Override public ActionResult release(RecordFreezeReleaseRequest request) { releasedTenant.set(request.getTenantId()); return response(true); } }; TransactionSynchronizationManager.initSynchronization(); new LimsElnRecordFreezeGate(api).prepareAndReleaseAfterCompletion( "", user(), Collections.singletonList("item-1"), rows()); TransactionSynchronizationManager.getSynchronizations().get(0).afterCompletion(0); assertEquals("0", preparedTenant.get()); assertEquals("0", releasedTenant.get()); } @Test public void rejectsBlockingDiagnosticsBeforeAnyCallerUpdate() { LimsElnRecordFreezeGate gate = new LimsElnRecordFreezeGate(api(request -> batch("item-1", RecordFreezeCheckResult.BLOCKED, null), new AtomicInteger())); TransactionSynchronizationManager.initSynchronization(); try { gate.prepareAndReleaseAfterCompletion("tenant-1", user(), Collections.singletonList("item-1"), rows()); fail("expected DataException"); } catch (DataException expected) { org.junit.Assert.assertTrue(expected.getMessage().contains("ELN 可冻结校验失败")); } } @Test public void failsClosedForRemoteExceptionAndMissingItemResponse() { AtomicInteger releases = new AtomicInteger(); LimsElnRecordFreezeGate unavailable = new LimsElnRecordFreezeGate(api(request -> { throw new IllegalStateException("network"); }, releases)); assertRejected(unavailable, "服务调用失败"); assertEquals(1, releases.get()); TransactionSynchronizationManager.clearSynchronization(); LimsElnRecordFreezeGate missing = new LimsElnRecordFreezeGate(api(request -> new RecordFreezeCheckBatch(), new AtomicInteger())); assertRejected(missing, "响应缺少检验项"); } @Test public void rejectsLostPrepareResponseWithoutRepeatingAuthoritativeCalculation() { AtomicInteger prepares = new AtomicInteger(); AtomicInteger releases = new AtomicInteger(); RecordFreezeApi api = new RecordFreezeApi() { @Override public ActionResult prepare(RecordFreezeCheckRequest request) { prepares.incrementAndGet(); throw new IllegalStateException("timeout"); } @Override public ActionResult release(RecordFreezeReleaseRequest request) { releases.incrementAndGet(); return response(true); } }; assertRejected(new LimsElnRecordFreezeGate(api), "服务调用失败"); assertEquals(1, prepares.get()); assertEquals(1, releases.get()); } @Test public void sourceKeepsFreezeGateBeforeLimsUpdateAndMarksCaughtBusinessErrorsForRollback() throws Exception { String service = new String(Files.readAllBytes(Paths.get( "src/main/java/jnpf/limsService/impl/LimsJianceTaskServiceImpl.java")), StandardCharsets.UTF_8); String submit = service.substring(service.indexOf("public LimsSubmitResultOutcome submitJianyanResult")); assertTrue(submit.indexOf("elnRecordFreezeGate.prepareAndReleaseAfterCompletion") < submit.indexOf("limsJianyanXiangMapper.submitResult(")); String controller = new String(Files.readAllBytes(Paths.get( "../jnpf-lims-controller/src/main/java/jnpf/limsController/LimsJianceTaskController.java")), StandardCharsets.UTF_8); assertTrue(controller.contains("TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()")); } private void assertRejected(LimsElnRecordFreezeGate gate, String expected) { TransactionSynchronizationManager.initSynchronization(); try { gate.prepareAndReleaseAfterCompletion("tenant-1", user(), Collections.singletonList("item-1"), rows()); fail("expected DataException"); } catch (DataException e) { org.junit.Assert.assertTrue(e.getMessage().contains(expected)); } } private static RecordFreezeApi api(final PrepareResponder responder, final AtomicInteger releases) { return new RecordFreezeApi() { @Override public ActionResult prepare(RecordFreezeCheckRequest request) { return response(responder.respond(request)); } @Override public ActionResult release(RecordFreezeReleaseRequest request) { releases.incrementAndGet(); return response(true); } }; } private static ActionResult response(T body) { ActionResult response = new ActionResult<>(); response.setCode(200); response.setData(body); return response; } private static RecordFreezeCheckBatch batch(String id, String status, String token) { RecordFreezeCheckResult item = new RecordFreezeCheckResult(); item.setTestWorkItemId(id); item.setStatus(status); item.setReleaseToken(token); if (RecordFreezeCheckResult.READY.equals(status)) { item.setTestResult("ELN 检验结果"); item.setTestConclusion("compliant"); } if (RecordFreezeCheckResult.BLOCKED.equals(status)) { jnpf.eln.RecordFreezeIssue issue = new jnpf.eln.RecordFreezeIssue(); issue.setCategory("FORMULA"); issue.setCode("FREEZE_FORMULA"); issue.setMessage("公式未成功"); item.getDiagnostics().add(issue); } RecordFreezeCheckBatch batch = new RecordFreezeCheckBatch(); batch.getItems().add(item); return batch; } private static UserInfo user() { UserInfo user = new UserInfo(); user.setUserId("user-1"); return user; } private static Map rows() { LimsJianyanXiangEntity row = new LimsJianyanXiangEntity(); row.setId("item-1"); row.setJianyanZhuangtai(JianyanStatus.PENDING_TEST.getCode()); row.setJianchaRen("user-1"); Map rows = new HashMap<>(); rows.put(row.getId(), row); return rows; } @FunctionalInterface private interface PrepareResponder { RecordFreezeCheckBatch respond(RecordFreezeCheckRequest request); } }