118 lines
3.5 KiB
JavaScript
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.');
|