feat: add ticket booking domain model
This commit is contained in:
165
src/pages-service/tickets/model.test.mjs
Normal file
165
src/pages-service/tickets/model.test.mjs
Normal file
@@ -0,0 +1,165 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
TICKET_VIEWS,
|
||||
applyRefund,
|
||||
calculateAge,
|
||||
calculateBookingTotals,
|
||||
calculateRefundAmount,
|
||||
createBookingDraft,
|
||||
createPaidOrder,
|
||||
isTravelerEligible,
|
||||
popViewHistory,
|
||||
validateBooking,
|
||||
} from './model.mjs'
|
||||
|
||||
const adultProduct = {
|
||||
id: 'adult',
|
||||
name: '成人票',
|
||||
salePrice: 160,
|
||||
reservation: {
|
||||
maxQuantity: 5,
|
||||
},
|
||||
ageRule: {
|
||||
min: 18,
|
||||
max: 59,
|
||||
},
|
||||
entitlements: [
|
||||
{
|
||||
id: 'adult-ticket',
|
||||
name: '成人门票',
|
||||
allocation: 120,
|
||||
third_party: true,
|
||||
},
|
||||
{
|
||||
id: 'shuttle',
|
||||
name: '摆渡车',
|
||||
allocation: 40,
|
||||
self: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const eligibleTraveler = {
|
||||
id: 't-1',
|
||||
name: '林晓',
|
||||
idType: '身份证',
|
||||
idNumber: '110101199001020010',
|
||||
}
|
||||
|
||||
test('exposes the ticket views', () => {
|
||||
assert.deepEqual(TICKET_VIEWS, {
|
||||
HOME: 'home',
|
||||
DETAIL: 'detail',
|
||||
BOOKING: 'booking',
|
||||
TRAVELERS: 'travelers',
|
||||
PAID: 'paid',
|
||||
ORDERS: 'orders',
|
||||
ORDER_DETAIL: 'orderDetail',
|
||||
REFUND: 'refundApply',
|
||||
REFUND_RESULT: 'refundResult',
|
||||
CREDENTIALS: 'credentials',
|
||||
})
|
||||
})
|
||||
|
||||
test('calculates age from a mainland ID at the visit date', () => {
|
||||
assert.equal(calculateAge('110101196001010010', '2026-08-01'), 66)
|
||||
assert.equal(calculateAge('110101201001020010', '2026-01-01'), 15)
|
||||
})
|
||||
|
||||
test('checks traveler eligibility against an age rule', () => {
|
||||
const traveler = {
|
||||
idType: '身份证',
|
||||
idNumber: '110101201001020010',
|
||||
}
|
||||
|
||||
assert.equal(isTravelerEligible(traveler, { ageRule: { min: 6, max: 17 } }, '2026-08-01'), true)
|
||||
assert.equal(isTravelerEligible(traveler, { ageRule: { min: 60, max: 120 } }, '2026-08-01'), false)
|
||||
})
|
||||
|
||||
test('calculates booking totals including selected insurance', () => {
|
||||
assert.deepEqual(calculateBookingTotals(adultProduct, 2, true, 5), {
|
||||
productAmount: 320,
|
||||
insuranceAmount: 10,
|
||||
paidAmount: 330,
|
||||
})
|
||||
})
|
||||
|
||||
test('validates booking draft fields and selected travelers in order', () => {
|
||||
const draft = createBookingDraft(adultProduct)
|
||||
const incompleteTraveler = { id: 't-1', idType: '身份证', idNumber: '' }
|
||||
const childTraveler = { id: 't-1', name: '小林', idType: '身份证', idNumber: '110101201001020010' }
|
||||
|
||||
assert.equal(validateBooking({ product: adultProduct, draft, travelers: [] }), '请选择使用日期')
|
||||
|
||||
const completeDraft = {
|
||||
...draft,
|
||||
visitDate: '2026-08-01',
|
||||
timeSlot: '08:00—09:30',
|
||||
quantity: 1,
|
||||
travelerIds: ['t-1'],
|
||||
contactName: '林晓',
|
||||
phone: '13800138000',
|
||||
}
|
||||
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, timeSlot: '' }, travelers: [eligibleTraveler] }), '请选择使用场次')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, quantity: 0 }, travelers: [eligibleTraveler] }), '购买数量无效')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, travelerIds: [] }, travelers: [eligibleTraveler] }), '请选择足够数量的出行人')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, quantity: 2, travelerIds: ['t-1', 't-1'] }, travelers: [eligibleTraveler] }), '请选择足够数量的出行人')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: completeDraft, travelers: [incompleteTraveler] }), '请完善出行人实名信息')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: completeDraft, travelers: [childTraveler] }), '出行人年龄不符合票种要求')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, phone: '123' }, travelers: [eligibleTraveler] }), '请填写正确的联系人和手机号')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: completeDraft, travelers: [eligibleTraveler] }), '')
|
||||
})
|
||||
|
||||
test('creates a paid order with entitlements and credentials', () => {
|
||||
const order = createPaidOrder({
|
||||
product: adultProduct,
|
||||
draft: {
|
||||
...createBookingDraft(adultProduct),
|
||||
visitDate: '2026-08-01',
|
||||
timeSlot: '08:00—09:30',
|
||||
travelerIds: ['t-1'],
|
||||
contactName: '林晓',
|
||||
phone: '13800138000',
|
||||
insuranceSelected: true,
|
||||
},
|
||||
travelers: [eligibleTraveler],
|
||||
now: new Date('2026-07-30T10:00:00+08:00'),
|
||||
})
|
||||
|
||||
assert.equal(order.status, 'paid')
|
||||
assert.equal(order.paidAmount, 165)
|
||||
assert.equal(order.entitlements.length, 2)
|
||||
assert.match(order.gateCredential.qrToken, /^GATE-/)
|
||||
assert.equal(order.logs[0].message, '订单支付成功')
|
||||
})
|
||||
|
||||
test('applies refunds and pops view history without mutation', () => {
|
||||
const order = createPaidOrder({
|
||||
product: adultProduct,
|
||||
draft: {
|
||||
...createBookingDraft(adultProduct),
|
||||
visitDate: '2026-08-01',
|
||||
timeSlot: '08:00—09:30',
|
||||
travelerIds: ['t-1'],
|
||||
contactName: '林晓',
|
||||
phone: '13800138000',
|
||||
},
|
||||
travelers: [eligibleTraveler],
|
||||
})
|
||||
const entitlementId = order.entitlements[0].id
|
||||
const history = ['home', 'detail', 'booking']
|
||||
|
||||
assert.equal(calculateRefundAmount(order, [entitlementId]), 120)
|
||||
|
||||
const result = applyRefund(order, [entitlementId], '行程有变', new Date('2026-07-30T10:01:00+08:00'))
|
||||
assert.equal(result.order.status, 'refunding')
|
||||
assert.equal(result.refund.amount, 120)
|
||||
assert.equal(result.order.entitlements[0].status, 'refunding')
|
||||
assert.deepEqual(applyRefund(result.order, [entitlementId], '重复申请').refund.entitlementIds, [])
|
||||
assert.deepEqual(popViewHistory(history), { view: 'booking', history: ['home', 'detail'] })
|
||||
assert.deepEqual(history, ['home', 'detail', 'booking'])
|
||||
assert.deepEqual(popViewHistory([]), { view: 'home', history: [] })
|
||||
})
|
||||
Reference in New Issue
Block a user