Merge remote-tracking branch 'origin/test' into dev
# Conflicts: # scripts/admin-test-env-modules/phase4-overseas-pda.mjs
This commit is contained in:
+1
-1
@@ -133,7 +133,7 @@ node scripts/create-pending-review-orders.mjs
|
||||
| Mapper 注册静态合同 | `node scripts/mapper-scan-contract.mjs`、`node --test scripts/mapper-scan-contract.test.mjs`;自动核对全部持久化模块的 `@Mapper`、服务根包注解过滤扫描和唯一扫描入口,不启动 Spring、不连接数据库;`backend-full-regression.ps1` 在 Maven 前强制执行 |
|
||||
| Java 质量聚合摘要 | `node --test scripts/java-quality-summary.test.mjs`、`node scripts/java-quality-summary.mjs --output target/java-quality-summary.json`;聚合 Maven/服务拓扑、OpenAPI 覆盖、JaCoCo 和 SpotBugs 报告,默认只报告存量缺口,CI 用显式 `--require-jacoco --require-spotbugs` 检查报告完整性且当前允许失败 |
|
||||
| 单服务部署失败回滚合同 | `scripts/deploy-built-service-rollback.test.mjs`;模拟成功、健康失败和启动失败,确认仅恢复同服务先前健康镜像且部署任务仍失败 |
|
||||
| 容器日志单写合同 | `scripts/container-logging-contract.test.mjs`;离线确认应用只写标准输出、Compose 使用有界 `local` 驱动且不挂载应用日志目录 |
|
||||
| 容器日志单写合同 | `scripts/container-logging-contract.test.mjs`;离线确认应用只写标准输出、Compose 默认使用宝塔可见的有界 `json-file` 驱动且不挂载应用日志目录 |
|
||||
|
||||
Admin/Client HTTP 脚本的离线合同只验证脚本结构、参数和模块边界,不访问测试环境:
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ test('PDA module covers the container-number receiving contract and excludes out
|
||||
assert.match(source, /item\?\.shippingReady === false/)
|
||||
assert.match(source, /!expectedSet\.has\(id\(item\?\.containerNo\)\)/)
|
||||
assert.match(source, /unexpectedAwaiting=/)
|
||||
assert.match(source, /candidatesByContainer\.get\(containerNo\)\?\.operable === true/)
|
||||
assert.match(source, /candidatesByContainer\.get\(containerNo\)\?\.operable !== true/)
|
||||
assert.match(source, /missingPickupReady\.length === 0 && unexpectedAwaiting\.length === 0/)
|
||||
assert.doesNotMatch(source, /shippingReady == null|shippingReady !== true/)
|
||||
assert.match(source, /nonBlank\(item\?\.resourceKey\)/)
|
||||
|
||||
@@ -350,22 +350,23 @@ async function awaitPickupReadyCandidates(context, token, producers, allProducer
|
||||
const known = new Set(allProducers.map((item) => id(item?.containerNo)).filter(nonBlank))
|
||||
if (!expected.length) return false
|
||||
let response = null
|
||||
let seen = new Set()
|
||||
let candidatesByContainer = new Map()
|
||||
for (let attempt = 1; attempt <= 30; attempt += 1) {
|
||||
response = await context.requestOverseas('GET',
|
||||
'/api/pda/v1/last-leg/receiving-containers/candidates', undefined, {}, token)
|
||||
const candidateRows = success(response) && Array.isArray(response.body?.data) ? response.body.data : []
|
||||
seen = new Set(candidateRows.map((item) => id(item?.containerNo)).filter(nonBlank))
|
||||
if (expected.every((containerNo) => seen.has(containerNo))
|
||||
&& awaiting.every((containerNo) => !seen.has(containerNo))) break
|
||||
candidatesByContainer = new Map(candidateRows
|
||||
.map((item) => [id(item?.containerNo), item]).filter(([containerNo]) => nonBlank(containerNo)))
|
||||
if (expected.every((containerNo) => candidatesByContainer.get(containerNo)?.operable === true)
|
||||
&& awaiting.every((containerNo) => candidatesByContainer.get(containerNo)?.operable !== true)) break
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
}
|
||||
const seenExpected = expected.filter((containerNo) => seen.has(containerNo))
|
||||
const missingPickupReady = expected.filter((containerNo) => !seen.has(containerNo))
|
||||
const unexpectedAwaiting = awaiting.filter((containerNo) => seen.has(containerNo))
|
||||
const unexpectedHistorical = [...seen].filter((containerNo) => !known.has(containerNo))
|
||||
const seenExpected = expected.filter((containerNo) => candidatesByContainer.has(containerNo))
|
||||
const missingPickupReady = expected.filter((containerNo) => candidatesByContainer.get(containerNo)?.operable !== true)
|
||||
const unexpectedAwaiting = awaiting.filter((containerNo) => candidatesByContainer.get(containerNo)?.operable === true)
|
||||
const unexpectedHistorical = [...candidatesByContainer.keys()].filter((containerNo) => !known.has(containerNo))
|
||||
const targetCorrelated = producers.every((item) => nonBlank(item?.resourceKey)
|
||||
&& item?.shippingReady !== false && seen.has(id(item?.containerNo)))
|
||||
&& item?.shippingReady !== false && candidatesByContainer.get(id(item?.containerNo))?.operable === true)
|
||||
const ready = missingPickupReady.length === 0 && unexpectedAwaiting.length === 0 && targetCorrelated
|
||||
context.addStep(MODULE, '实际处理柜全部达到PICKUP_READY并进入PDA候选', response, ready,
|
||||
`targetCorrelated=${targetCorrelated}, expected=${expected.join('|')}, awaiting=${awaiting.join('|') || 'none'}, seenExpected=${seenExpected.join('|') || 'none'}, missingPickupReady=${missingPickupReady.join('|') || 'none'}, unexpectedAwaiting=${unexpectedAwaiting.join('|') || 'none'}, unexpectedHistorical=${unexpectedHistorical.join('|') || 'none'}; ${trace(response)}`, OPTIONS)
|
||||
|
||||
@@ -28,6 +28,11 @@ const composeFiles = [
|
||||
'deploy/docker-compose.overseas.yml',
|
||||
];
|
||||
|
||||
const infrastructureComposeFiles = [
|
||||
['deploy/docker-compose.rocketmq.yml', 3],
|
||||
['docker-compose-infra.yml', 3],
|
||||
];
|
||||
|
||||
test('deployable applications log only to stdout and stderr', () => {
|
||||
for (const path of applicationConfigs) {
|
||||
const source = readFileSync(path, 'utf8');
|
||||
@@ -35,10 +40,10 @@ test('deployable applications log only to stdout and stderr', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('Compose uses one bounded local log stream without application log mounts', () => {
|
||||
test('Compose uses one bounded BaoTa-compatible log stream without application log mounts', () => {
|
||||
for (const path of composeFiles) {
|
||||
const source = readFileSync(path, 'utf8');
|
||||
assert.match(source, /driver:\s+local/, path);
|
||||
assert.match(source, /driver:\s+\$\{DOCKER_LOG_DRIVER:-json-file\}/, path);
|
||||
assert.match(source, /DOCKER_LOG_MAX_SIZE/, path);
|
||||
assert.match(source, /DOCKER_LOG_MAX_FILE/, path);
|
||||
assert.doesNotMatch(source, /LOG_PATH|LOG_ROOT|\/app\/logs/, path);
|
||||
@@ -52,3 +57,19 @@ test('runtime image does not prepare an application log directory', () => {
|
||||
const dockerfile = readFileSync('deploy/docker/Dockerfile', 'utf8');
|
||||
assert.doesNotMatch(dockerfile, /\/app\/logs/);
|
||||
});
|
||||
|
||||
test('infrastructure containers also use bounded BaoTa-compatible logs', () => {
|
||||
for (const [path, expectedServiceCount] of infrastructureComposeFiles) {
|
||||
const source = readFileSync(path, 'utf8');
|
||||
assert.match(source, /x-bounded-logging:\s+&bounded-logging/, path);
|
||||
assert.match(source, /driver:\s+\$\{DOCKER_LOG_DRIVER:-json-file\}/, path);
|
||||
assert.match(source, /DOCKER_LOG_MAX_SIZE/, path);
|
||||
assert.match(source, /DOCKER_LOG_MAX_FILE/, path);
|
||||
assert.equal(
|
||||
source.match(/logging:\s+\*bounded-logging/g)?.length,
|
||||
expectedServiceCount,
|
||||
path,
|
||||
);
|
||||
assert.doesNotMatch(source, /driver:\s+local/, path);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user