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.');