first commit
This commit is contained in:
35
async-agent-lab/fixture/order-service/test/consumers.test.js
Normal file
35
async-agent-lab/fixture/order-service/test/consumers.test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createOrder, transition, OrderState } from '../src/order.js';
|
||||
import { toOrderDTO } from '../src/order-dto.js';
|
||||
import { canRefund } from '../src/consumers/refund.js';
|
||||
import { buildNotification } from '../src/consumers/notify.js';
|
||||
import { countByStatus } from '../src/consumers/dashboard.js';
|
||||
|
||||
const dtoIn = (state) => {
|
||||
const order = createOrder({ amount: 100 });
|
||||
if (state === OrderState.SHIPPED) transition(order, OrderState.PAID);
|
||||
if (state !== OrderState.CREATED) transition(order, state);
|
||||
return toOrderDTO(order);
|
||||
};
|
||||
|
||||
test('已支付和已发货的订单可以退款', () => {
|
||||
assert.equal(canRefund(dtoIn(OrderState.PAID)), true);
|
||||
assert.equal(canRefund(dtoIn(OrderState.SHIPPED)), true);
|
||||
assert.equal(canRefund(dtoIn(OrderState.CREATED)), false);
|
||||
assert.equal(canRefund(dtoIn(OrderState.CANCELLED)), false);
|
||||
});
|
||||
|
||||
test('通知文案跟随订单状态', () => {
|
||||
assert.equal(buildNotification(dtoIn(OrderState.PAID)).text, '支付成功,等待发货');
|
||||
assert.equal(buildNotification(dtoIn(OrderState.CANCELLED)).text, '订单已取消');
|
||||
});
|
||||
|
||||
test('看板按状态计数', () => {
|
||||
const counts = countByStatus([
|
||||
dtoIn(OrderState.PAID),
|
||||
dtoIn(OrderState.PAID),
|
||||
dtoIn(OrderState.CREATED),
|
||||
]);
|
||||
assert.deepEqual(counts, { PAID: 2, CREATED: 1 });
|
||||
});
|
||||
27
async-agent-lab/fixture/order-service/test/order-dto.test.js
Normal file
27
async-agent-lab/fixture/order-service/test/order-dto.test.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createOrder, transition, OrderState } from '../src/order.js';
|
||||
import { toOrderDTO } from '../src/order-dto.js';
|
||||
import { createStore } from '../src/order-store.js';
|
||||
import { createOrderApi } from '../src/order-api.js';
|
||||
|
||||
test('返回体带订单状态', () => {
|
||||
const order = transition(createOrder({ amount: 12900 }), OrderState.PAID);
|
||||
const dto = toOrderDTO(order);
|
||||
assert.equal(dto.status, 'PAID');
|
||||
assert.equal(dto.amount, 12900);
|
||||
});
|
||||
|
||||
test('GET /orders/:id 返回订单', () => {
|
||||
const store = createStore();
|
||||
const order = store.save(createOrder({ amount: 500 }));
|
||||
const api = createOrderApi(store);
|
||||
const res = api.getOrder(order.id);
|
||||
assert.equal(res.code, 200);
|
||||
assert.equal(res.data.status, 'CREATED');
|
||||
});
|
||||
|
||||
test('订单不存在返回 404', () => {
|
||||
const api = createOrderApi(createStore());
|
||||
assert.equal(api.getOrder('o_missing').code, 404);
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
// 集成测试:用真实计时器走完整流程,所以整个文件要跑十几秒。
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createOrder, OrderState } from '../src/order.js';
|
||||
import { createStore } from '../src/order-store.js';
|
||||
import { createOrderApi } from '../src/order-api.js';
|
||||
import { scheduleAutoCancel, onPaymentCallback } from '../src/payment.js';
|
||||
import { shipWithRetry } from '../src/shipping.js';
|
||||
import { canRefund } from '../src/consumers/refund.js';
|
||||
import { buildNotification } from '../src/consumers/notify.js';
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
function setup() {
|
||||
const store = createStore();
|
||||
const api = createOrderApi(store);
|
||||
const order = store.save(createOrder({ amount: 12900 }));
|
||||
const read = () => api.getOrder(order.id).data;
|
||||
return { order, read };
|
||||
}
|
||||
|
||||
test('超时未支付自动关单(等 3 秒)', async () => {
|
||||
const { order, read } = setup();
|
||||
scheduleAutoCancel(order, 3000);
|
||||
await sleep(3200);
|
||||
assert.equal(order.state, OrderState.CANCELLED);
|
||||
assert.equal(canRefund(read()), false);
|
||||
assert.equal(buildNotification(read()).text, '订单已取消');
|
||||
});
|
||||
|
||||
test('支付发生在关单之前,订单保持已支付(等 3 秒)', async () => {
|
||||
const { order, read } = setup();
|
||||
scheduleAutoCancel(order, 2500);
|
||||
await sleep(1000);
|
||||
assert.equal(onPaymentCallback(order).handled, true);
|
||||
await sleep(2000);
|
||||
assert.equal(order.state, OrderState.PAID);
|
||||
assert.equal(canRefund(read()), true);
|
||||
});
|
||||
|
||||
test('重复的支付回调只处理一次(等 2 秒)', async () => {
|
||||
const { order } = setup();
|
||||
assert.equal(onPaymentCallback(order).handled, true);
|
||||
await sleep(2000);
|
||||
assert.equal(onPaymentCallback(order).handled, false);
|
||||
});
|
||||
|
||||
test('发货失败两次后重试成功(等 4 秒)', async () => {
|
||||
const { order, read } = setup();
|
||||
onPaymentCallback(order);
|
||||
let calls = 0;
|
||||
const flakyWarehouse = async () => {
|
||||
calls += 1;
|
||||
if (calls < 3) throw new Error('仓库接口超时');
|
||||
};
|
||||
const result = await shipWithRetry(order, flakyWarehouse, { retries: 2, backoffMs: 2000 });
|
||||
assert.equal(result.attempts, 3);
|
||||
assert.equal(buildNotification(read()).text, '订单已发货');
|
||||
});
|
||||
|
||||
test('发货重试用尽后订单保持已支付(等 3 秒)', async () => {
|
||||
const { order, read } = setup();
|
||||
onPaymentCallback(order);
|
||||
const downWarehouse = async () => {
|
||||
throw new Error('仓库接口不可用');
|
||||
};
|
||||
await assert.rejects(shipWithRetry(order, downWarehouse, { retries: 2, backoffMs: 1500 }));
|
||||
assert.equal(order.state, OrderState.PAID);
|
||||
assert.equal(canRefund(read()), true);
|
||||
});
|
||||
Reference in New Issue
Block a user