import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const source = readFileSync(new URL('./merchantIam.ts', import.meta.url), 'utf8') const storeSource = readFileSync(new URL('../store/tenantAdministration.ts', import.meta.url), 'utf8') const contractsSource = readFileSync(new URL('../contracts.ts', import.meta.url), 'utf8') const authSource = readFileSync(new URL('../../auth/store/auth.ts', import.meta.url), 'utf8') const permissionsViewSource = readFileSync(new URL('../views/PermissionsView.vue', import.meta.url), 'utf8') test('declares the documented merchant role and member endpoints', () => { assert.match(source, /url: '\/merchant\/v1\/iam\/roles'/) assert.match(source, /method: 'PUT', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}`/) assert.match(source, /method: 'PUT', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}\/status`/) assert.match(source, /method: 'DELETE', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}`/) assert.match(source, /url: '\/merchant\/v1\/iam\/roles\/permissions'/) assert.match(source, /url: '\/merchant\/v1\/iam\/members'/) assert.match(source, /url: '\/merchant\/v1\/iam\/members\/roles'/) assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}`/) assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}\/status`/) assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}\/password`/) assert.match(source, /url: '\/merchant\/v1\/iam\/profile'/) }) test('supports tenant profile updates and authenticated qualification downloads', () => { assert.match(source, /method: 'PUT', url: '\/merchant\/v1\/iam\/tenant\/profile'/) assert.match(source, /apiBlobRequest\(\{ method: 'GET', url \}\)/) assert.match(source, /url\.startsWith\('\/merchant\/v1\/files\/qualifications\/'\)/) assert.match(storeSource, /await updateTenantProfile/) assert.match(source, /url: '\/merchant\/v1\/iam\/tenant\/qualifications\/submit-review'/) assert.match(source, /params: \{ qualificationType, certificateNo, replace \}/) assert.doesNotMatch(source, /data\.append\('qualificationType'/) assert.match(source, /'SUPERSEDED'/) assert.match(storeSource, /normalizeQualificationStatus\(item\.status\)/) }) test('uses membershipId and full replacement arrays in authorization writes', () => { assert.match(source, /data: \{ roleId, permissionIds \}/) assert.match(source, /data: \{ membershipId, roleIds \}/) assert.doesNotMatch(source, /data: \{ userId, roleIds \}/) }) test('starts empty and only loads server role and member records', () => { assert.match(storeSource, /const members = ref\(\[\]\)/) assert.match(storeSource, /const roles = ref\(\[\]\)/) assert.match(storeSource, /async function loadRoles\(\) \{\s+roles\.value = \[\]/) assert.match(storeSource, /async function loadMembers\(\) \{\s+members\.value = \[\]/) assert.doesNotMatch(storeSource, /initialRoles|initialMembers|示例贸易|演示大道/) assert.match(storeSource, /await createMerchantRole/) assert.match(storeSource, /await updateMerchantRole/) assert.match(storeSource, /await changeMerchantRoleStatus/) assert.match(storeSource, /await deleteMerchantRole/) assert.match(storeSource, /await createMerchantMember/) assert.match(storeSource, /await updateMerchantMember/) assert.match(storeSource, /await changeMerchantMemberStatus/) assert.match(storeSource, /await resetMerchantMemberPassword/) }) test('shares the merchant permission map and preserves resources not rendered by the role editor', () => { assert.match(contractsSource, /export const FRONTEND_TO_MERCHANT_PERMISSION_CODES/) assert.match(contractsSource, /'order:first-leg:cancel': \['merchant:order:cancel'\]/) assert.match(storeSource, /from '@\/modules\/tenant-management\/contracts'/) assert.match(authSource, /FRONTEND_TO_MERCHANT_PERMISSION_CODES/) assert.match(authSource, /from '@\/modules\/tenant-management\/public'/) assert.match(authSource, /Object\.entries\(FRONTEND_TO_MERCHANT_PERMISSION_CODES\)/) assert.match(storeSource, /const preservedPermissionIds = role\.permissionIds\.filter/) assert.match(storeSource, /const nextPermissionIds = \[\.\.\.new Set\(\[\.\.\.preservedPermissionIds, \.\.\.selectedPermissionIds\]\)\]/) assert.match(storeSource, /await replaceMerchantRolePermissions\(id, nextPermissionIds\)/) }) test('keeps client baseline permissions selected when server resources do not define them', () => { assert.match(contractsSource, /REQUIRED_TENANT_PERMISSION_CODES = \['account:profile:view', 'dashboard:view'\]/) assert.match(storeSource, /\.\.\.REQUIRED_TENANT_PERMISSION_CODES, \.\.\.permissionCodes/) assert.match(storeSource, /role\.permissionCodes = effectivePermissionCodes/) assert.match(permissionsViewSource, /group\.items\.every\(\(item\) => isRequiredPermission\(item\.code\)\)/) assert.match(permissionsViewSource, /:disabled="isRequiredPermission\(item\.code\)"/) assert.doesNotMatch(permissionsViewSource, /selectedCodes\.value\.unshift\('account:profile:view'\)/) })