import assert from 'node:assert/strict'; import { buildApp } from '../dist/app.js'; import { signAccessToken } from '../dist/auth/jwt.js'; import { SystemOperationsRepository } from '../dist/operations/system-operations-repository.js'; const secret = 'test-only-system-operations-secret-32'; const token = signAccessToken({ sub: '21', sid: '5c4d3af8-c63c-4edb-bf95-b84127bb3f6e', tid: '7', aid: '9', rv: 1 }, secret, 900); let auditQuery; let overviewTenantId; let updateCall; const repository = { async listAuditLogs(input) { auditQuery = input; return { items: [], total: 0, page: input.page, pageSize: input.pageSize }; }, async getSystemOverview(tenantId) { overviewTenantId = tenantId; return { tenant: { id: tenantId, code: 'demo', name: '演示租户', status: 'ACTIVE', timezone: 'Asia/Shanghai' }, counts: { storeCount: 1, userCount: 2, activeSessionCount: 1, appBindingCount: 1, auditTodayCount: 3 }, latestMigration: { version: '2026081003', name: 'm08d_franchise_leads' } }; }, async updateTenant(actor, tenantId, input) { updateCall = { actor, tenantId, input }; return { tenantId, updated: true }; } }; const authRepository = { async validateSession() { return { id: '5c4d3af8-c63c-4edb-bf95-b84127bb3f6e', tenantId: '7', platformAppId: '9', expiresAt: new Date(Date.now() + 60000), user: { id: '21', tenantId: '7', userType: 'STAFF', status: 'ACTIVE', roleVersion: 1, nickname: '管理员', avatarUrl: '', phone: '' } }; } }; const tenantAccess = { roles: ['TENANT_ADMIN'], capabilities: ['tenant.manage'], storeIds: [] }; const app = await buildApp({ systemOperations: { repository, authRepository, accessControl: { async getAccessProfile() { return tenantAccess; } }, jwtSecret: secret } }); const auth = { authorization: `Bearer ${token}` }; const listed = await app.inject({ method: 'GET', url: '/admin-api/audit-logs?action=ORDER_CREATED&resourceType=ORDER&page=2&pageSize=10', headers: auth }); assert.equal(listed.statusCode, 200); assert.deepEqual({ tenantId: auditQuery.tenantId, action: auditQuery.action, resourceType: auditQuery.resourceType, page: auditQuery.page }, { tenantId: '7', action: 'ORDER_CREATED', resourceType: 'ORDER', page: 2 }); assert.ok(auditQuery.from instanceof Date); assert.ok(auditQuery.to instanceof Date); const crossTenant = await app.inject({ method: 'GET', url: '/admin-api/audit-logs?tenantId=8', headers: auth }); assert.equal(crossTenant.statusCode, 403); const invalidRange = await app.inject({ method: 'GET', url: '/admin-api/audit-logs?from=2025-01-01&to=2026-08-01', headers: auth }); assert.equal(invalidRange.statusCode, 400); const overview = await app.inject({ method: 'GET', url: '/admin-api/system/overview', headers: auth }); assert.equal(overview.statusCode, 200); assert.equal(overviewTenantId, '7'); assert.equal(overview.json().data.counts.auditTodayCount, 3); const updated = await app.inject({ method: 'PUT', url: '/admin-api/system/tenant', headers: auth, payload: { name: '新租户名称', timezone: 'Asia/Shanghai' } }); assert.equal(updated.statusCode, 200); assert.deepEqual({ tenantId: updateCall.tenantId, name: updateCall.input.name, timezone: updateCall.input.timezone }, { tenantId: '7', name: '新租户名称', timezone: 'Asia/Shanghai' }); const invalidTimezone = await app.inject({ method: 'PUT', url: '/admin-api/system/tenant', headers: auth, payload: { name: '新租户名称', timezone: 'Mars/Olympus' } }); assert.equal(invalidTimezone.statusCode, 400); const tenantStatusChange = await app.inject({ method: 'PUT', url: '/admin-api/system/tenant', headers: auth, payload: { name: '新租户名称', timezone: 'Asia/Shanghai', status: 'DISABLED' } }); assert.equal(tenantStatusChange.statusCode, 403); assert.equal(tenantStatusChange.json().code, 'SYSTEM_STATUS_FORBIDDEN'); await app.close(); const platformApp = await buildApp({ systemOperations: { repository, authRepository, accessControl: { async getAccessProfile() { return { roles: ['PLATFORM_ADMIN'], capabilities: ['platform.manage'], storeIds: [] }; } }, jwtSecret: secret } }); const platformUpdate = await platformApp.inject({ method: 'PUT', url: '/admin-api/system/tenant', headers: auth, payload: { tenantId: '8', name: '平台目标租户', timezone: 'UTC', status: 'DISABLED' } }); assert.equal(platformUpdate.statusCode, 200); assert.equal(updateCall.tenantId, '8'); assert.equal(updateCall.input.status, 'DISABLED'); await platformApp.close(); const sensitivePool = { async execute(sql) { if (sql.includes('COUNT(*)')) return [[{ total: 1 }], []]; return [[{ id: '99', tenantId: '7', actorType: 'USER', actorId: '21', actorName: '管理员', action: 'CONFIG_UPDATED', resourceType: 'TENANT', resourceId: '7', traceId: 'trace-99', ip: '192.168.10.22', userAgent: 'test', metadata: JSON.stringify({ orderId: '88', phone: '13800138000', nested: { apiKey: 'secret-key', safe: 'visible' } }), createdAt: new Date() }], []]; } }; const sanitized = await new SystemOperationsRepository(sensitivePool).listAuditLogs({ tenantId: '7', page: 1, pageSize: 20 }); assert.equal(sanitized.items[0].ip, '192.168.***.***'); assert.equal(sanitized.items[0].metadata.phone, '[REDACTED]'); assert.equal(sanitized.items[0].metadata.nested.apiKey, '[REDACTED]'); assert.equal(sanitized.items[0].metadata.nested.safe, 'visible'); console.log('PASS: M08-D tenant-scoped audit logs, metadata redaction and system settings routes are present.');