From e7c0788a0b724116e13f7982fe3a8da56b307d06 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 15 Aug 2026 09:49:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phase3-evidence-stage-contract.test.mjs | 22 ++++++++++++++ .../repository/SupplierSqlContractTest.java | 1 + .../service/SupplierServiceTest.java | 29 +++++++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 scripts/admin-test-env-modules/phase3-evidence-stage-contract.test.mjs diff --git a/scripts/admin-test-env-modules/phase3-evidence-stage-contract.test.mjs b/scripts/admin-test-env-modules/phase3-evidence-stage-contract.test.mjs new file mode 100644 index 000000000..61a2f88b3 --- /dev/null +++ b/scripts/admin-test-env-modules/phase3-evidence-stage-contract.test.mjs @@ -0,0 +1,22 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const source = readFileSync(new URL('./phase3-evidence-gate.mjs', import.meta.url), 'utf8') + +test('evidence gate requires stable PDA identity and server LOADING before any evidence request', () => { + for (const field of ['taskId', 'inboundId', 'loadingPlanId', 'containerBookingId', 'warehouseId']) { + assert.match(source, new RegExp(`${field}`), `${field} must be part of the preflight identity`) + } + assert.match(source, /pda-loading\/tasks\?loadingPlanId=/) + assert.match(source, /task\?\.id\) === taskId/) + assert.match(source, /task\?\.loadingPlanId\) === loadingPlanId/) + assert.match(source, /task\?\.containerBookingId\) === containerBookingId/) + assert.match(source, /taskItem\?\.inboundId\) === inboundId/) + assert.match(source, /processStatus === 'LOADING'/) + assert.match(source, /if \(!serverLoading\)[\s\S]*return/) + assert.match(source, /AWAITING_REVIEW 不作为证据开放条件/) + const gate = source.slice(source.indexOf('const taskResponse'), source.indexOf('const path =')) + assert.doesNotMatch(gate, /loading\/evidence/) + assert.match(source, /const path = `\/api\/platform\/inbounds\/\$\{inboundId\}\/loading\/evidence/) +}) diff --git a/warehouse/src/test/java/com/baiyue/warehouse/repository/SupplierSqlContractTest.java b/warehouse/src/test/java/com/baiyue/warehouse/repository/SupplierSqlContractTest.java index f0211202d..71f649600 100644 --- a/warehouse/src/test/java/com/baiyue/warehouse/repository/SupplierSqlContractTest.java +++ b/warehouse/src/test/java/com/baiyue/warehouse/repository/SupplierSqlContractTest.java @@ -13,6 +13,7 @@ class SupplierSqlContractTest { assertThat(stream).isNotNull(); String sql = new String(stream.readAllBytes(), StandardCharsets.UTF_8); assertThat(sql).contains("CREATE TABLE IF NOT EXISTS wh_supplier", "tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0", + "chk_wh_supplier_tenant CHECK (tenant_id = 0)", "category='BOOKING' AND scope IN ('DOMESTIC','INTERNATIONAL')", "license_file_id VARCHAR(128)", "MONTHLY_30','MONTHLY_45','MONTHLY_60','MONTHLY_90','PER_TRANSACTION','IMMEDIATE", "created_by BIGINT UNSIGNED", "updated_by BIGINT UNSIGNED", diff --git a/warehouse/src/test/java/com/baiyue/warehouse/service/SupplierServiceTest.java b/warehouse/src/test/java/com/baiyue/warehouse/service/SupplierServiceTest.java index 5b9157c07..a6b564bf4 100644 --- a/warehouse/src/test/java/com/baiyue/warehouse/service/SupplierServiceTest.java +++ b/warehouse/src/test/java/com/baiyue/warehouse/service/SupplierServiceTest.java @@ -2,6 +2,8 @@ package com.baiyue.warehouse.service; import com.baiyue.common.core.exception.BizException; import com.baiyue.warehouse.dto.SupplierDtos.SaveRequest; +import com.baiyue.warehouse.dto.SupplierDtos.PageQuery; +import com.baiyue.warehouse.dto.SupplierDtos.StatusRequest; import com.baiyue.warehouse.entity.SupplierEntity; import com.baiyue.warehouse.repository.SupplierRepository; import org.junit.jupiter.api.BeforeEach; @@ -13,6 +15,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,20 +33,40 @@ class SupplierServiceTest { @Test void createsPlatformPublicBookingSupplier() { when(repository.existsName(0L, "测试供应商", null)).thenReturn(false); - service.create(booking); + service.create(101L, booking); verify(repository).insert(any(SupplierEntity.class)); } @Test void rejectsScopeForNonBookingSupplier() { SaveRequest customs = new SaveRequest("报关供应商", "CUSTOMS", "DOMESTIC", "宁波", "法人", "联系人", "13800000000", null, null, null, null, "ACTIVE", "PER_TRANSACTION", null); - assertThatThrownBy(() -> service.create(customs)).isInstanceOf(BizException.class) + assertThatThrownBy(() -> service.create(101L, customs)).isInstanceOf(BizException.class) .hasMessageContaining("仅适用于订舱供应商"); } @Test void requiresScopeForBookingSupplier() { SaveRequest missingScope = new SaveRequest("订舱供应商", "BOOKING", null, "宁波", "法人", "联系人", "13800000000", null, null, null, null, "ACTIVE", "PER_TRANSACTION", null); - assertThatThrownBy(() -> service.create(missingScope)).isInstanceOf(BizException.class); + assertThatThrownBy(() -> service.create(101L, missingScope)).isInstanceOf(BizException.class); + } + + @Test void rejectsScopeFilterWithoutBookingCategory() { + assertThatThrownBy(() -> service.page(new PageQuery(1, 20, "CUSTOMS", "DOMESTIC", null, null, null))) + .isInstanceOf(BizException.class).hasMessageContaining("scope仅适用于订舱供应商"); + } + + @Test void repeatedStatusChangeIsIdempotent() { + SupplierEntity entity = new SupplierEntity(); entity.setId(101L); entity.setTenantId(0L); entity.setStatus("ACTIVE"); + when(repository.find(0L, 101L)).thenReturn(entity); + service.changeStatus(101L, new StatusRequest("ACTIVE")); + verify(repository, never()).save(any()); + } + + @Test void validatesInternalSupplierSnapshot() { + SupplierEntity entity = new SupplierEntity(); entity.setId(101L); entity.setTenantId(0L); entity.setName("测试供应商"); + entity.setCategory("BOOKING"); entity.setScope("DOMESTIC"); entity.setStatus("ACTIVE"); + when(repository.find(0L, 101L)).thenReturn(entity); + var snapshot = service.internalSnapshot(101L, true, "BOOKING", "DOMESTIC"); + org.assertj.core.api.Assertions.assertThat(snapshot.supplierId()).isEqualTo(101L); } }