126 lines
8.2 KiB
JavaScript
126 lines
8.2 KiB
JavaScript
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'/)
|
|
assert.match(source, /url: '\/merchant\/v1\/iam\/authorization\/permissions'/)
|
|
assert.match(authSource, /getCurrentMerchantPermissionCodes\(\)/)
|
|
assert.doesNotMatch(authSource, /payload\.permissions/)
|
|
assert.doesNotMatch(authSource, /payload\.roles/)
|
|
})
|
|
|
|
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\)/)
|
|
assert.match(source, /validTo: string \| null/)
|
|
assert.match(storeSource, /expiresAt: item\.validTo \|\| '长期有效'/)
|
|
assert.match(source, /enterpriseType: string \| null/)
|
|
assert.match(storeSource, /enterpriseType: profile\.enterpriseType \?\? ''/)
|
|
})
|
|
|
|
test('uploads the required license and omits the optional legal-person file when absent', () => {
|
|
assert.match(source, /url: '\/merchant\/v1\/files\/qualifications\/required-package'/)
|
|
assert.match(source, /data\.append\('businessLicense', businessLicense\)/)
|
|
assert.match(source, /if \(legalPersonId\) data\.append\('legalPersonId', legalPersonId\)/)
|
|
assert.match(source, /\.\.\.\(legalPersonId \? \{ replaceLegalPersonId: Boolean\(replaceLegalPersonId\) \} : \{\}\)/)
|
|
assert.match(source, /legalPersonIdQualificationId: number \| string \| null/)
|
|
})
|
|
|
|
test('uses the dedicated login-identifiers endpoint and keeps phone out of ordinary profile updates', () => {
|
|
assert.match(source, /url: '\/merchant\/v1\/iam\/profile\/login-identifiers'/)
|
|
assert.match(source, /ChangeMerchantLoginIdentifiersRequest/)
|
|
assert.match(source, /Pick<MerchantPersonalProfile, 'displayName' \| 'email'>/)
|
|
})
|
|
|
|
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<TenantMember\[]>\(\[\]\)/)
|
|
assert.match(storeSource, /const roles = ref<TenantRole\[]>\(\[\]\)/)
|
|
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/)
|
|
assert.match(source, /username: string/)
|
|
assert.match(source, /lastLoginAt: string \| null/)
|
|
assert.match(source, /memberCount: number/)
|
|
assert.match(storeSource, /account: member\.username/)
|
|
assert.match(storeSource, /lastLoginAt: formatLastLoginAt\(member\.lastLoginAt\)/)
|
|
assert.match(storeSource, /roles\.value\.find\(\(role\) => role\.id === roleId\)\?\.memberCount \?\? 0/)
|
|
assert.doesNotMatch(storeSource, /lastLoginAt: '服务端未提供'/)
|
|
})
|
|
|
|
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(contractsSource, /'company:member:create': \['merchant:member:create'\]/)
|
|
assert.match(contractsSource, /'company:member:edit': \['merchant:member:edit'\]/)
|
|
assert.match(contractsSource, /'company:member:grant-role': \['merchant:member:grant-role'\]/)
|
|
assert.match(contractsSource, /'company:member:status': \['merchant:member:status'\]/)
|
|
assert.match(contractsSource, /'company:role:create': \['merchant:role:create'\]/)
|
|
assert.match(contractsSource, /'company:role:edit': \['merchant:role:edit'\]/)
|
|
assert.match(contractsSource, /'company:role:status': \['merchant:role:status'\]/)
|
|
assert.match(contractsSource, /'company:role:delete': \['merchant:role:delete'\]/)
|
|
assert.doesNotMatch(contractsSource, /company:(?:member|role):write/)
|
|
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'\)/)
|
|
})
|
|
|
|
test('exposes all message-center permissions in the enterprise role editor', () => {
|
|
for (const permission of ['merchant:notification:view', 'merchant:notification:read', 'merchant:notification:read-all']) {
|
|
assert.match(contractsSource, new RegExp(`code: '${permission}'`))
|
|
assert.match(contractsSource, new RegExp(`'${permission}': \\['${permission}'\\]`))
|
|
}
|
|
assert.match(contractsSource, /key: 'notifications'/)
|
|
assert.match(storeSource, /const mappedMerchantPermissionCodes = new Set\(Object\.values\(FRONTEND_TO_MERCHANT_PERMISSION_CODES\)\.flat\(\)\)/)
|
|
})
|