Files
qipai/backend/tests/member-profile-route.test.mjs
T
2026-08-10 10:35:44 +08:00

166 lines
5.1 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);
const managerToken = signAccessToken({
sub: '23', sid: '7c4d3af8-c63c-4edb-bf95-b84127bb3f6e',
tid: '7', aid: '9', rv: 1
}, secret, 900);
let profileInput;
let benefitsInput;
let memberListInput;
let memberDetailInput;
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'
: sessionId === '7c4d3af8-c63c-4edb-bf95-b84127bb3f6e' ? '23' : '21',
tenantId: '7',
userType: 'CUSTOMER',
status: 'ACTIVE',
roleVersion: 1,
nickname: '',
avatarUrl: '',
phone: ''
}
};
}
},
accessControl: {
async getAccessProfile(tenantId, userId) {
return userId === '23'
? { roles: ['STORE_ADMIN'], capabilities: ['user.read'], storeIds: ['11'] }
: userId === '21'
? { roles: ['CUSTOMER'], capabilities: ['profile.read', 'order.self.read'], storeIds: [] }
: { roles: ['CUSTOMER'], capabilities: ['order.self.read'], storeIds: [] };
}
},
service: {
async listMembers(input) {
memberListInput = input;
return {
items: [{
memberId: '31', nickname: '门店会员', maskedPhone: '138****8000',
wallet: { totalBalanceCents: 1200 }, orders: { orderCount: 2 }
}],
total: 1
};
},
async getMember(input) {
memberDetailInput = input;
return { memberId: input.memberId, nickname: '门店会员', recentLedger: [] };
},
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 members = await app.inject({
method: 'GET',
url: '/app-api/management/members?page=1&pageSize=20&status=ACTIVE&search=138',
headers: { authorization: `Bearer ${managerToken}` }
});
assert.equal(members.statusCode, 200);
assert.equal(memberListInput.actor.userId, '23');
assert.deepEqual(memberListInput.actor.access.storeIds, ['11']);
assert.equal(memberListInput.status, 'ACTIVE');
assert.equal(memberListInput.search, '138');
assert.equal(members.json().data.items[0].memberId, '31');
const memberDetail = await app.inject({
method: 'GET',
url: '/app-api/management/members/31',
headers: { authorization: `Bearer ${managerToken}` }
});
assert.equal(memberDetail.statusCode, 200);
assert.equal(memberDetailInput.memberId, '31');
const memberForbidden = await app.inject({
method: 'GET',
url: '/app-api/management/members',
headers: { authorization: `Bearer ${token}` }
});
assert.equal(memberForbidden.statusCode, 403);
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.');