forked from wangziqi/gongxue-base
feat: add platform tenant detail audit console
This commit is contained in:
@@ -1138,6 +1138,108 @@ async function testLegacyAuthHeadersDisabled() {
|
||||
assert.equal(legacyAdminKey.code, 'PLATFORM_ADMIN_KEY_DISABLED', 'platform admin key should be disabled when configured off');
|
||||
}
|
||||
|
||||
async function testPlatformTenantOperationsAndAudit() {
|
||||
const adminHeaders = { 'x-platform-admin-key': 'local-platform-admin-key' };
|
||||
const slug = `integration-tenant-${Date.now().toString(36)}`;
|
||||
const created = await request('/api/platform-admin/tenants', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
method: 'POST',
|
||||
body: {
|
||||
slug,
|
||||
name: '集成测试合作商',
|
||||
legalName: '集成测试合作商有限公司',
|
||||
brandName: '集成测试题库',
|
||||
primaryHost: `${slug}.example.test`,
|
||||
planCode: 'starter_yearly',
|
||||
billingStatus: 'trial',
|
||||
amountCents: 120000,
|
||||
billing: {
|
||||
billingName: '集成测试合作商有限公司',
|
||||
taxId: 'TESTTAXNO',
|
||||
contactName: '平台测试联系人',
|
||||
contactPhone: '13800001111',
|
||||
invoiceTitle: '集成测试合作商有限公司',
|
||||
invoiceType: 'normal_vat',
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.ok(created.item?.id, 'platform admin should create a tenant');
|
||||
|
||||
const tenantId = created.item.id;
|
||||
const detail = await request('/api/platform-admin/tenants/detail', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
query: { tenantId },
|
||||
});
|
||||
assert.equal(detail.item?.tenant?.slug, slug, 'tenant detail should return created tenant');
|
||||
assert.ok(detail.item?.domains?.some(item => item.host === `${slug}.example.test`), 'tenant detail should include primary host');
|
||||
assert.ok(detail.item?.subscriptions?.some(item => item.planCode === 'starter_yearly'), 'tenant detail should include subscription');
|
||||
|
||||
const billing = await request('/api/platform-admin/tenants/billing-profile', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
tenantId,
|
||||
billingName: '集成测试更新主体',
|
||||
taxId: 'UPDATEDTAXNO',
|
||||
contactName: '更新联系人',
|
||||
contactPhone: '13800002222',
|
||||
contactEmail: 'finance@example.test',
|
||||
invoiceTitle: '集成测试更新主体',
|
||||
invoiceType: 'special_vat',
|
||||
bankName: '测试银行',
|
||||
bankAccountMasked: '****2222',
|
||||
},
|
||||
});
|
||||
assert.equal(billing.item?.billingName, '集成测试更新主体', 'platform admin should update tenant billing profile');
|
||||
|
||||
const status = await request('/api/platform-admin/tenants/status', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
tenantId,
|
||||
status: 'active',
|
||||
billingStatus: 'active',
|
||||
reason: 'integration audit coverage',
|
||||
},
|
||||
});
|
||||
assert.equal(status.item?.billingStatus, 'active', 'platform admin should update tenant billing status');
|
||||
|
||||
const updatedDetail = await request('/api/platform-admin/tenants/detail', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
query: { tenantId },
|
||||
});
|
||||
assert.equal(updatedDetail.item?.tenant?.billingName, '集成测试更新主体', 'tenant detail should include updated billing profile');
|
||||
assert.equal(updatedDetail.item?.tenant?.bankAccountMasked, '****2222', 'tenant detail should only expose masked bank account');
|
||||
|
||||
const audit = await request('/api/platform-admin/audit-logs', {
|
||||
tenantId: false,
|
||||
userId: false,
|
||||
headers: adminHeaders,
|
||||
query: { tenantId, limit: 20 },
|
||||
});
|
||||
const actions = new Set((audit.items || []).map(item => item.action));
|
||||
assert.ok(actions.has('platform.tenant.created'), 'platform audit should include tenant creation');
|
||||
assert.ok(actions.has('platform.tenant.billing_profile_upserted'), 'platform audit should include billing profile update');
|
||||
assert.ok(actions.has('platform.tenant.status_updated'), 'platform audit should include status update');
|
||||
|
||||
const studentAuditDenied = await request('/api/platform-admin/audit-logs', {
|
||||
tenantId: false,
|
||||
userId: USER_ID,
|
||||
expectStatus: 403,
|
||||
});
|
||||
assert.equal(studentAuditDenied.code, 'PLATFORM_ADMIN_REQUIRED', 'student must not read platform audit logs');
|
||||
}
|
||||
|
||||
function stopServer() {
|
||||
if (serverProcess && !serverProcess.killed) {
|
||||
serverProcess.kill();
|
||||
@@ -8234,6 +8336,7 @@ async function main() {
|
||||
await check('Supabase JWT identity', testSupabaseJwtIdentity);
|
||||
await check('Supabase JWKS JWT identity', testSupabaseJwksIdentity);
|
||||
await check('legacy auth headers disabled', testLegacyAuthHeadersDisabled);
|
||||
await check('platform tenant operations and audit', testPlatformTenantOperationsAndAudit);
|
||||
await check('catalog and learning', testCatalogAndLearning);
|
||||
await check('composite practice questions', testCompositePracticeQuestions);
|
||||
await check('profile', testProfile);
|
||||
|
||||
Reference in New Issue
Block a user