完善借款、编号、付款与预算并发及验收(09-06-release-gate-integrity)
汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、PWA/采购事务、错误原因与日期校验及ERP退役。 统一共享预算池锁序和锁后判断,恢复差旅原预算选择,补原因码固定夹具及反向完整性检查。 纳入配套迁移、测试、受管QA、分层验收合同、规约及任务记录;原始证据留在ignored artifacts。 关联任务:09-17-company-loan-principal-repayment、09-17-erp-exclusive-retirement、09-18-reimbursement-periodic-numbering、09-08-reimbursement-payment-closeout、09-18-local-concurrency-product-fixes、09-19-budget-pool-concurrency、09-05-error-explainability-payment-readiness。 预算补审由Claude定向验证2文件928项通过,原4446/28及r6结果仍仅对应各自历史输入;本轮未重跑测试。 完整新候选门禁、R1真实入口接入与12项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
这个提交包含在:
@@ -4,13 +4,13 @@ const { promisify } = require('node:util');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const YAML = require('yaml');
|
||||
const { parseCompose, lifecycleServices } = require('../fixtures/deployment-command-fake.cjs');
|
||||
const { parseCompose, lifecycleServices, startCommandServer } = require('../fixtures/deployment-command-fake.cjs');
|
||||
|
||||
const execute = promisify(execFile);
|
||||
const ROOT = path.resolve(__dirname, '../../../..');
|
||||
const DEPLOY = path.join(ROOT, 'deploy/trialrun/deploy.sh');
|
||||
const COMPOSE = path.join(ROOT, 'deploy/trialrun/docker-compose.yml');
|
||||
const FAKE = path.join(__dirname, '../fixtures/deployment-command-fake.cjs');
|
||||
const FAKE = path.join(__dirname, '../fixtures/deployment-command-client.py');
|
||||
const ORIGINAL_PATH = process.env.PATH;
|
||||
|
||||
function remoteSection(source) {
|
||||
@@ -33,6 +33,7 @@ describe('database lifecycle isolation in the actual deployment remote script',
|
||||
let fixtureRoot;
|
||||
let source;
|
||||
let services;
|
||||
let closeCommands;
|
||||
|
||||
beforeEach(async () => {
|
||||
fixtureRoot = await mkdtemp(path.join(os.tmpdir(), 'hyb-db-lifecycle-'));
|
||||
@@ -40,7 +41,7 @@ describe('database lifecycle isolation in the actual deployment remote script',
|
||||
services = YAML.parse(await readFile(COMPOSE, 'utf8')).services;
|
||||
const bin = path.join(fixtureRoot, 'bin');
|
||||
await mkdir(bin);
|
||||
const fakeSource = '#!' + process.execPath + '\n' + await readFile(FAKE, 'utf8');
|
||||
const fakeSource = await readFile(FAKE, 'utf8');
|
||||
for (const command of ['docker', 'docker-compose', 'curl', 'timeout', 'sleep', 'ssh', 'rsync', 'node', 'git', 'sudo']) {
|
||||
const file = path.join(bin, command);
|
||||
await writeFile(file, fakeSource);
|
||||
@@ -52,15 +53,18 @@ describe('database lifecycle isolation in the actual deployment remote script',
|
||||
await mkdir(path.join(fixtureRoot, 'postgres'));
|
||||
await writeFile(path.join(fixtureRoot, 'postgres/PG_VERSION'), '16\n');
|
||||
await writeFile(path.join(fixtureRoot, 'env'), 'APP_ORIGIN=https://example.invalid\n', { mode: 0o600 });
|
||||
closeCommands = await startCommandServer(fixtureRoot);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
expect(process.env.PATH).toBe(ORIGINAL_PATH);
|
||||
await closeCommands?.();
|
||||
closeCommands = undefined;
|
||||
await rm(fixtureRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
async function run({ mode = 'existing', worker = false, minio = false, engine = 'v2', health = 'healthy', initFails = false, unsupportedIsolation = false, driftAfter = '', workerImagePresent = true, workerTagPresent = true, script = remoteSection(source) } = {}) {
|
||||
await writeFile(path.join(fixtureRoot, 'config.json'), JSON.stringify({ services, engine, health, initFails, unsupportedIsolation, driftAfter, workerImagePresent, workerTagPresent }));
|
||||
await writeFile(path.join(fixtureRoot, 'config.json'), JSON.stringify({ services, mode, engine, health, initFails, unsupportedIsolation, driftAfter, workerImagePresent, workerTagPresent }));
|
||||
await writeFile(path.join(fixtureRoot, 'trace.jsonl'), '');
|
||||
const scriptPath = path.join(fixtureRoot, 'remote.sh');
|
||||
await writeFile(scriptPath, script);
|
||||
@@ -253,6 +257,28 @@ describe('database lifecycle isolation in the actual deployment remote script',
|
||||
expect(result.trace).toEqual([{ command: 'ssh', args: ['unexpected.invalid'] }]);
|
||||
});
|
||||
|
||||
it('the private transport preserves empty, quoted and multiline command arguments', async () => {
|
||||
const result = await run({ script: "set -e\nssh '' 'space 中文' $'first\\nsecond'\n" });
|
||||
expect(result.code).toBe(97);
|
||||
expect(result.trace).toEqual([{ command: 'ssh', args: ['', 'space 中文', 'first\nsecond'] }]);
|
||||
});
|
||||
|
||||
it('the private transport preserves ready stdin validation and failure status', async () => {
|
||||
const result = await run({ script: `set -e\nprintf '%s' '{"data":{"buildSha":"wrong"}}' | docker compose -p synthetic-project exec -T server node -e 'EXPECTED_BUILD_SHA'\n` });
|
||||
expect(result.code).toBe(97);
|
||||
expect(result.stderr).toContain('wrong_ready_build');
|
||||
expect(result.trace).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('an unavailable private transport fails instead of invoking real commands', async () => {
|
||||
await closeCommands();
|
||||
closeCommands = undefined;
|
||||
const result = await run({ script: 'set -e\ndocker compose version\n' });
|
||||
expect(result.code).toBe(97);
|
||||
expect(result.stderr).toContain('transport_failed');
|
||||
expect(result.trace).toEqual([]);
|
||||
});
|
||||
|
||||
it('the existing wrapper pins mode regardless of an inherited initialize value', async () => {
|
||||
const wrapper = await readFile(path.join(ROOT, 'deploy/trialrun/deploy-existing.sh'), 'utf8');
|
||||
const invocations = [...wrapper.matchAll(/DEPLOY_SERVER="\$DEPLOY_SERVER" \\\nREMOTE_DIR="\$\{contract\[REMOTE_DIR\]\}"[\s\S]*?bash "\$ROOT_DIR\/deploy\/trialrun\/deploy\.sh"/g)];
|
||||
|
||||
@@ -2,7 +2,7 @@ const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const ts = require("../../../../client/node_modules/typescript");
|
||||
const expectedMessages = require("../fixtures/error-reason-messages.json");
|
||||
const { getErrorReason } = require("../../../src/constants/errorReasons");
|
||||
const { ERROR_REASONS, getErrorReason } = require("../../../src/constants/errorReasons");
|
||||
const { getMissingRequiredOcrFields } = require("../../../src/services/ocr");
|
||||
const { validatePassword } = require("../../../src/utils/passwordPolicy");
|
||||
|
||||
@@ -32,6 +32,13 @@ describe("client business error projection", () => {
|
||||
expect(formatDisplayDateTime("2026-12-31T18:30:45.000Z", { includeSeconds: true })).toBe("2027年1月1日 02:30:45");
|
||||
});
|
||||
|
||||
it("requires an independent message fixture for every user-facing registered reason", () => {
|
||||
const missing = ERROR_REASONS
|
||||
.filter((reason) => reason.audience === "user" && !Object.hasOwn(expectedMessages, reason.code))
|
||||
.map((reason) => reason.code);
|
||||
expect(missing).toEqual([]);
|
||||
});
|
||||
|
||||
it.each(Object.entries(expectedMessages))("keeps exact registered and displayed content for %s", (reasonCode, expected) => {
|
||||
expect(getErrorReason(reasonCode).defaultMessage).toBe(expected);
|
||||
expect(resolveErrorMessage(new ApiError({ status: 409, code: "VALID_001", message: "private payload", details: { reasonCode } }))).toBe(expected);
|
||||
|
||||
在新工单中引用
屏蔽一个用户