166 lines
5.1 KiB
JavaScript
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.');
|