Files
qipai/backend/tests/member-profile-route.test.mjs
2026-06-25 12:07:09 +08:00

118 lines
3.5 KiB
JavaScript

import assert from 'node:assert/strict';
import { buildApp } from '../dist/app.js';
import { signAccessToken } from '../dist/auth/jwt.js';
const secret = 'test-only-member-profile-route-secret';
const token = signAccessToken({
sub: '21', sid: '5c4d3af8-c63c-4edb-bf95-b84127bb3f6e',
tid: '7', aid: '9', rv: 1
}, secret, 900);
const forbiddenToken = signAccessToken({
sub: '22', sid: '6c4d3af8-c63c-4edb-bf95-b84127bb3f6e',
tid: '7', aid: '9', rv: 1
}, secret, 900);
let profileInput;
let benefitsInput;
const app = await buildApp({
members: {
jwtSecret: secret,
authRepository: {
async validateSession(sessionId) {
return {
id: sessionId,
tenantId: '7',
platformAppId: '9',
expiresAt: new Date(Date.now() + 60000),
user: {
id: sessionId === '6c4d3af8-c63c-4edb-bf95-b84127bb3f6e' ? '22' : '21',
tenantId: '7',
userType: 'CUSTOMER',
status: 'ACTIVE',
roleVersion: 1,
nickname: '',
avatarUrl: '',
phone: ''
}
};
}
},
accessControl: {
async getAccessProfile(tenantId, userId) {
return userId === '21'
? { roles: ['CUSTOMER'], capabilities: ['profile.read', 'order.self.read'], storeIds: [] }
: { roles: ['CUSTOMER'], capabilities: ['order.self.read'], storeIds: [] };
}
},
service: {
async listMembers() { throw new Error('not called'); },
async getMember() { throw new Error('not called'); },
async getMyBenefits(input) {
benefitsInput = input;
return {
coupons: [{
couponGrantId: '401',
name: '30元抵扣券',
status: 'AVAILABLE',
discountAmountCents: 3000
}],
packages: [{
packageHoldingId: '501',
name: '三小时畅玩包',
status: 'ACTIVE',
remainingMinutes: 120
}]
};
},
async getMyProfile(input) {
profileInput = input;
return {
memberId: input.userId,
nickname: 'Alice',
maskedPhone: '138****8000',
wallet: { cashBalanceCents: 1000, giftBalanceCents: 200, totalBalanceCents: 1200 },
benefits: { availableCoupons: 2, activePackages: 1, packageMinutes: 90 },
recentLedger: []
};
}
}
}
});
const profile = await app.inject({
method: 'GET',
url: '/app-api/profile',
headers: { authorization: `Bearer ${token}` }
});
assert.equal(profile.statusCode, 200);
assert.equal(profileInput.tenantId, '7');
assert.equal(profileInput.userId, '21');
assert.equal(profile.json().data.wallet.totalBalanceCents, 1200);
const benefits = await app.inject({
method: 'GET',
url: '/app-api/profile/benefits',
headers: { authorization: `Bearer ${token}` }
});
assert.equal(benefits.statusCode, 200);
assert.equal(benefitsInput.tenantId, '7');
assert.equal(benefitsInput.userId, '21');
assert.equal(benefits.json().data.coupons[0].couponGrantId, '401');
assert.equal(benefits.json().data.packages[0].packageHoldingId, '501');
const unauthorized = await app.inject({
method: 'GET',
url: '/app-api/profile'
});
assert.equal(unauthorized.statusCode, 401);
const forbidden = await app.inject({
method: 'GET',
url: '/app-api/profile',
headers: { authorization: `Bearer ${forbiddenToken}` }
});
assert.equal(forbidden.statusCode, 403);
await app.close();
console.log('PASS: M08-A customer profile route exposes only the current member.');