import { createHash } from 'node:crypto'; import { PrismaClient } from '@prisma/client'; import { hashPasswordArgon2id } from '../packages/auth/src/index'; const prisma = new PrismaClient(); const ACTOR_ID = 'usr_test_admin'; const TEST_PASSWORD = 'Test@123456'; const permissions = [ 'dashboard.view', 'active_calls.view', 'active_calls.manage', 'customers.view', 'customers.manage', 'customer_gateways.view', 'customer_gateways.manage', 'vendors.view', 'vendors.manage', 'vendor_gateways.view', 'vendor_gateways.manage', 'line_groups.view', 'line_groups.manage', 'number_library.view', 'number_library.manage', 'recharges.view', 'recharges.manage', 'cdr.view', 'recordings.play', 'quality.view', 'quality.manage', 'users.view', 'users.manage', 'roles.view', 'roles.manage', 'audit.view', ] as const; const roles = [ { id: 'ROLE_TEST_ADMIN', name: '测试管理员', permissionIds: [...permissions] }, { id: 'ROLE_TEST_VIEWER', name: '测试只读员', permissionIds: permissions.filter((permission) => permission.endsWith('.view') || permission === 'cdr.view'), }, { id: 'ROLE_TEST_FINANCE', name: '测试财务员', permissionIds: ['customers.view', 'recharges.view', 'recharges.manage'] }, { id: 'ROLE_TEST_QUALITY', name: '测试质检员', permissionIds: ['quality.view', 'quality.manage', 'recordings.play', 'cdr.view'] }, { id: 'ROLE_TEST_GATEWAY', name: '测试客户网关员', permissionIds: ['customers.view', 'customer_gateways.view', 'customer_gateways.manage', 'line_groups.view'], }, { id: 'ROLE_TEST_VENDOR', name: '测试供应商线路员', permissionIds: ['vendors.view', 'vendors.manage', 'vendor_gateways.view', 'vendor_gateways.manage', 'line_groups.view', 'line_groups.manage'], }, { id: 'ROLE_TEST_ACTIVE', name: '测试话务员', permissionIds: ['active_calls.view', 'active_calls.manage'] }, { id: 'ROLE_TEST_AUDIT', name: '测试审计员', permissionIds: ['audit.view'] }, ] as const; const users = [ { id: ACTOR_ID, username: 'test.admin', displayName: '测试管理员', roleId: 'ROLE_TEST_ADMIN' }, { id: 'usr_test_viewer', username: 'test.viewer', displayName: '测试只读员', roleId: 'ROLE_TEST_VIEWER' }, { id: 'usr_test_fin', username: 'test.finance', displayName: '测试财务员', roleId: 'ROLE_TEST_FINANCE' }, { id: 'usr_test_quality', username: 'test.quality', displayName: '测试质检员', roleId: 'ROLE_TEST_QUALITY' }, { id: 'usr_test_gateway', username: 'test.gateway', displayName: '测试客户网关员', roleId: 'ROLE_TEST_GATEWAY' }, { id: 'usr_test_vendor', username: 'test.vendor', displayName: '测试供应商线路员', roleId: 'ROLE_TEST_VENDOR' }, { id: 'usr_test_active', username: 'test.active', displayName: '测试话务员', roleId: 'ROLE_TEST_ACTIVE' }, { id: 'usr_test_audit', username: 'test.audit', displayName: '测试审计员', roleId: 'ROLE_TEST_AUDIT' }, ] as const; function sipHa1(username: string, domain: string, password: string): string { return createHash('md5').update(`${username}:${domain}:${password}`).digest('hex'); } async function seedAuth() { for (const id of permissions) { const [module, action] = id.split('.'); await prisma.permission.upsert({ where: { id }, update: {}, create: { id, module, action, description: `测试权限 ${id}`, }, }); } for (const role of roles) { await prisma.role.upsert({ where: { id: role.id }, update: { name: role.name, status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: role.id, name: role.name, description: '自动化测试角色', builtIn: false, status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); for (const permissionId of role.permissionIds) { await prisma.rolePermission.upsert({ where: { roleId_permissionId: { roleId: role.id, permissionId } }, update: {}, create: { roleId: role.id, permissionId, createdBy: ACTOR_ID }, }); } } const passwordHash = await hashPasswordArgon2id(TEST_PASSWORD, { memoryKiB: 1024, passes: 1 }); for (const user of users) { await prisma.user.upsert({ where: { id: user.id }, update: { username: user.username, displayName: user.displayName, passwordHash, passwordAlgo: 'argon2id', status: 'ENABLED', failedLoginCount: 0, lockedUntil: null, requirePasswordChange: false, updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: user.id, username: user.username, displayName: user.displayName, email: `${user.username}@example.test`, passwordHash, passwordAlgo: 'argon2id', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.userRole.upsert({ where: { userId_roleId: { userId: user.id, roleId: user.roleId } }, update: {}, create: { userId: user.id, roleId: user.roleId, createdBy: ACTOR_ID }, }); } } async function seedNumberLibrary() { await prisma.geoCity.upsert({ where: { code: 'geo_340100' }, update: { provinceCode: '340000', provinceName: '安徽省', cityCode: '340100', cityName: '合肥市', cityLevel: '地级市', status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { code: 'geo_340100', provinceCode: '340000', provinceName: '安徽省', cityCode: '340100', cityName: '合肥市', cityLevel: '地级市', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.phoneNumberSegment.upsert({ where: { segment7: '1380013' }, update: { cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', carrier: 'MOBILE', source: 'test-seed', batchId: 'test-seed', updatedBy: ACTOR_ID, deletedAt: null, }, create: { segment7: '1380013', cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', carrier: 'MOBILE', source: 'test-seed', batchId: 'test-seed', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.phoneAreaCode.upsert({ where: { areaCode: '0551' }, update: { cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', source: 'test-seed', batchId: 'test-seed', updatedBy: ACTOR_ID, deletedAt: null, }, create: { areaCode: '0551', cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', source: 'test-seed', batchId: 'test-seed', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.carrierPrefixRule.upsert({ where: { prefix: '138' }, update: { carrier: 'MOBILE', priority: 10, source: 'test-seed', batchId: 'test-seed', updatedBy: ACTOR_ID, deletedAt: null, }, create: { prefix: '138', carrier: 'MOBILE', priority: 10, source: 'test-seed', batchId: 'test-seed', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); } async function seedBusinessData() { await prisma.customer.upsert({ where: { id: 'cus_auto_001' }, update: { name: '自动化测试客户A', contactName: '测试联系人', phone: '13800138000', email: 'customer-a@example.test', domain: 'customer-a.example.test', status: 'ENABLED', billingMode: 'PREPAID', balance: '1000.000000', creditLimit: '200.000000', minBalance: '10.000000', notes: '自动化测试固定客户,可用于充值、扣款、网关和话单测试', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'cus_auto_001', name: '自动化测试客户A', contactName: '测试联系人', phone: '13800138000', email: 'customer-a@example.test', domain: 'customer-a.example.test', status: 'ENABLED', billingMode: 'PREPAID', balance: '1000.000000', creditLimit: '200.000000', minBalance: '10.000000', notes: '自动化测试固定客户,可用于充值、扣款、网关和话单测试', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.customer.upsert({ where: { id: 'cus_low_001' }, update: { name: '自动化低余额客户', status: 'ENABLED', billingMode: 'PREPAID', balance: '3.000000', creditLimit: '0.000000', minBalance: '10.000000', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'cus_low_001', name: '自动化低余额客户', domain: 'customer-low.example.test', status: 'ENABLED', billingMode: 'PREPAID', balance: '3.000000', creditLimit: '0.000000', minBalance: '10.000000', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); const businessPrefixes = [ { id: 'bp_auto_671', prefix: '671', name: '自动化业务前缀671', priority: 10 }, { id: 'bp_auto_672', prefix: '672', name: '自动化业务前缀672', priority: 20 }, ] as const; for (const prefix of businessPrefixes) { await prisma.businessPrefix.upsert({ where: { id: prefix.id }, update: { prefix: prefix.prefix, name: prefix.name, description: '自动化测试业务前缀', priority: prefix.priority, status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: prefix.id, prefix: prefix.prefix, name: prefix.name, description: '自动化测试业务前缀', priority: prefix.priority, status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); } await prisma.vendor.upsert({ where: { id: 'ven_auto_001' }, update: { name: '自动化测试供应商A', contactName: '供应商联系人', phone: '13900139000', email: 'vendor-a@example.test', status: 'ENABLED', balance: '5000.000000', creditLimit: '500.000000', settlement: '月结', notes: '自动化测试固定供应商', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'ven_auto_001', name: '自动化测试供应商A', contactName: '供应商联系人', phone: '13900139000', email: 'vendor-a@example.test', status: 'ENABLED', balance: '5000.000000', creditLimit: '500.000000', settlement: '月结', notes: '自动化测试固定供应商', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); const vendorGateways = [ { id: 'vgw_auto_primary', name: '自动化主落地网关', host: '10.88.0.10', priority: 10, weight: 70 }, { id: 'vgw_auto_backup', name: '自动化备落地网关', host: '10.88.0.11', priority: 20, weight: 30 }, ] as const; for (const gateway of vendorGateways) { await prisma.vendorGateway.upsert({ where: { id: gateway.id }, update: { vendorId: 'ven_auto_001', name: gateway.name, authMode: 'IP', host: gateway.host, port: 5060, transport: 'udp', cpsLimit: 50, concurrencyLimit: 500, billingCycleSec: 60, cycleRate: gateway.id === 'vgw_auto_primary' ? '0.035000' : '0.040000', landingCalleePrefix: '86', status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: gateway.id, vendorId: 'ven_auto_001', name: gateway.name, authMode: 'IP', host: gateway.host, port: 5060, transport: 'udp', cpsLimit: 50, concurrencyLimit: 500, billingCycleSec: 60, cycleRate: gateway.id === 'vgw_auto_primary' ? '0.035000' : '0.040000', landingCalleePrefix: '86', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.vendorGatewayCodec.upsert({ where: { vendorGatewayId_codec: { vendorGatewayId: gateway.id, codec: 'PCMA' } }, update: { priority: 10, updatedBy: ACTOR_ID }, create: { id: `${gateway.id}_pcma`, vendorGatewayId: gateway.id, codec: 'PCMA', priority: 10, createdBy: ACTOR_ID, updatedBy: ACTOR_ID }, }); await prisma.vendorGatewayPrefixRule.upsert({ where: { vendorGatewayId_direction_priority: { vendorGatewayId: gateway.id, direction: 'CALLEE', priority: 10 } }, update: { matchPrefix: '671', replacePrefix: '86', updatedBy: ACTOR_ID }, create: { id: `${gateway.id}_callee`, vendorGatewayId: gateway.id, direction: 'CALLEE', matchPrefix: '671', replacePrefix: '86', priority: 10, createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); } await prisma.vendorGatewayForbiddenPeriod.upsert({ where: { id: 'vgwfp_auto_001' }, update: { vendorGatewayId: 'vgw_auto_backup', weekdayMask: 127, startTime: '00:00:00', endTime: '00:10:00', updatedBy: ACTOR_ID, }, create: { id: 'vgwfp_auto_001', vendorGatewayId: 'vgw_auto_backup', weekdayMask: 127, startTime: '00:00:00', endTime: '00:10:00', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.vendorGatewayCallerRewrite.upsert({ where: { id: 'vgwcr_auto_001' }, update: { vendorGatewayId: 'vgw_auto_primary', caller: '05510000001', weight: 100, status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'vgwcr_auto_001', vendorGatewayId: 'vgw_auto_primary', caller: '05510000001', weight: 100, status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.landingLineGroup.upsert({ where: { id: 'llg_auto_001' }, update: { name: '自动化测试线路组', status: 'ENABLED', notes: '主备落地网关测试线路组', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'llg_auto_001', name: '自动化测试线路组', status: 'ENABLED', notes: '主备落地网关测试线路组', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); for (const gateway of vendorGateways) { await prisma.landingLineGroupItem.upsert({ where: { lineGroupId_vendorGatewayId: { lineGroupId: 'llg_auto_001', vendorGatewayId: gateway.id } }, update: { priority: gateway.priority, weight: gateway.weight, concurrencyCap: gateway.id === 'vgw_auto_primary' ? 300 : 100, status: 'ENABLED', updatedBy: ACTOR_ID, }, create: { id: gateway.id === 'vgw_auto_primary' ? 'llgi_auto_primary' : 'llgi_auto_backup', lineGroupId: 'llg_auto_001', vendorGatewayId: gateway.id, priority: gateway.priority, weight: gateway.weight, concurrencyCap: gateway.id === 'vgw_auto_primary' ? 300 : 100, status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); } await prisma.customerGateway.upsert({ where: { id: 'cgw_auto_ip' }, update: { customerId: 'cus_auto_001', name: '自动化IP认证客户网关', authMode: 'IP', sourceIp: '10.66.0.10', lineGroupId: 'llg_auto_001', billingCycleSec: 60, cycleRate: '0.080000', callerMatchMode: 'PREFIXES', calleeMatchMode: 'BUSINESS_PREFIXES', status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'cgw_auto_ip', customerId: 'cus_auto_001', name: '自动化IP认证客户网关', authMode: 'IP', sourceIp: '10.66.0.10', lineGroupId: 'llg_auto_001', billingCycleSec: 60, cycleRate: '0.080000', callerMatchMode: 'PREFIXES', calleeMatchMode: 'BUSINESS_PREFIXES', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.customerGateway.upsert({ where: { id: 'cgw_auto_sip' }, update: { customerId: 'cus_auto_001', name: '自动化SIP认证客户网关', authMode: 'SIP_DIGEST', sipUsername: 'auto_sip_user', sipDomain: 'customer-a.example.test', sipHa1: sipHa1('auto_sip_user', 'customer-a.example.test', TEST_PASSWORD), lineGroupId: 'llg_auto_001', billingCycleSec: 60, cycleRate: '0.090000', callerMatchMode: 'ANY', calleeMatchMode: 'BUSINESS_PREFIXES', status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'cgw_auto_sip', customerId: 'cus_auto_001', name: '自动化SIP认证客户网关', authMode: 'SIP_DIGEST', sipUsername: 'auto_sip_user', sipDomain: 'customer-a.example.test', sipHa1: sipHa1('auto_sip_user', 'customer-a.example.test', TEST_PASSWORD), lineGroupId: 'llg_auto_001', billingCycleSec: 60, cycleRate: '0.090000', callerMatchMode: 'ANY', calleeMatchMode: 'BUSINESS_PREFIXES', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.customerGatewayIp.upsert({ where: { gatewayId_sourceIp: { gatewayId: 'cgw_auto_ip', sourceIp: '10.66.0.10' } }, update: { status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null }, create: { id: 'cgip_auto_001', gatewayId: 'cgw_auto_ip', sourceIp: '10.66.0.10', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID }, }); await prisma.customerGatewayBusinessPrefix.upsert({ where: { gatewayId_businessPrefixId: { gatewayId: 'cgw_auto_ip', businessPrefixId: 'bp_auto_671' } }, update: {}, create: { id: 'cgbp_auto_ip_671', gatewayId: 'cgw_auto_ip', businessPrefixId: 'bp_auto_671', createdBy: ACTOR_ID }, }); await prisma.customerGatewayBusinessPrefix.upsert({ where: { gatewayId_businessPrefixId: { gatewayId: 'cgw_auto_sip', businessPrefixId: 'bp_auto_671' } }, update: {}, create: { id: 'cgbp_auto_sip_671', gatewayId: 'cgw_auto_sip', businessPrefixId: 'bp_auto_671', createdBy: ACTOR_ID }, }); await prisma.customerGatewayCallerPrefix.upsert({ where: { gatewayId_prefix: { gatewayId: 'cgw_auto_ip', prefix: '0551' } }, update: { priority: 10 }, create: { id: 'cgcp_auto_0551', gatewayId: 'cgw_auto_ip', prefix: '0551', priority: 10, createdBy: ACTOR_ID }, }); await prisma.customerGatewayPolicy.upsert({ where: { id: 'cgp_auto_001' }, update: { customerId: 'cus_auto_001', gatewayId: 'cgw_auto_ip', lineGroupId: 'llg_auto_001', name: '自动化客户网关策略', priority: 10, callerMode: 'PREFIX', callerValue: '0551', calleeMode: 'PREFIX', calleeValue: '671', status: 'ENABLED', updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'cgp_auto_001', customerId: 'cus_auto_001', gatewayId: 'cgw_auto_ip', lineGroupId: 'llg_auto_001', name: '自动化客户网关策略', priority: 10, callerMode: 'PREFIX', callerValue: '0551', calleeMode: 'PREFIX', calleeValue: '671', status: 'ENABLED', createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); } async function seedCdrAndQuality() { const startedAt = new Date('2026-01-01T10:00:00.000Z'); const answeredAt = new Date('2026-01-01T10:00:06.000Z'); const endedAt = new Date('2026-01-01T10:01:06.000Z'); await prisma.rawCdr.upsert({ where: { id: 'raw_auto_001' }, update: { eventId: 'evt_auto_001', callId: 'call_auto_001', customerId: 'cus_auto_001', customerGatewayId: 'cgw_auto_ip', customerGatewayPolicyId: 'cgp_auto_001', sourceIp: '10.66.0.10', caller: '05510000001', callee: '67113800138000', rawCallee: '67113800138000', businessPrefixId: 'bp_auto_671', businessPrefix: '671', calleeCityCode: '340100', calleeCityName: '合肥市', calleeProvinceName: '安徽省', calleeOperator: 'MOBILE', calleeNumberType: 'MOBILE', vendorId: 'ven_auto_001', vendorGatewayId: 'vgw_auto_primary', lineGroupId: 'llg_auto_001', landingCaller: '05510000001', landingCallee: '8613800138000', startedAt, answeredAt, endedAt, durationSec: 66, sipCode: 200, hangupReason: 'NORMAL_CLEARING', recordingKey: 'seed/call_auto_001.wav', configVersion: 1, ratingStatus: 'RATED', payload: { source: 'test-seed' }, }, create: { id: 'raw_auto_001', eventId: 'evt_auto_001', callId: 'call_auto_001', customerId: 'cus_auto_001', customerGatewayId: 'cgw_auto_ip', customerGatewayPolicyId: 'cgp_auto_001', sourceIp: '10.66.0.10', caller: '05510000001', callee: '67113800138000', rawCallee: '67113800138000', businessPrefixId: 'bp_auto_671', businessPrefix: '671', calleeCityCode: '340100', calleeCityName: '合肥市', calleeProvinceName: '安徽省', calleeOperator: 'MOBILE', calleeNumberType: 'MOBILE', vendorId: 'ven_auto_001', vendorGatewayId: 'vgw_auto_primary', lineGroupId: 'llg_auto_001', landingCaller: '05510000001', landingCallee: '8613800138000', startedAt, answeredAt, endedAt, durationSec: 66, sipCode: 200, hangupReason: 'NORMAL_CLEARING', recordingKey: 'seed/call_auto_001.wav', configVersion: 1, ratingStatus: 'RATED', payload: { source: 'test-seed' }, }, }); await prisma.ratedCdr.upsert({ where: { rawCdrId: 'raw_auto_001' }, update: { billSec: 60, customerFee: '0.080000', vendorCost: '0.035000', grossProfit: '0.045000', customerRate: { cycleSec: 60, cycleRate: '0.080000' }, vendorRate: { cycleSec: 60, cycleRate: '0.035000' }, }, create: { id: 'rated_auto_001', rawCdrId: 'raw_auto_001', billSec: 60, customerFee: '0.080000', vendorCost: '0.035000', grossProfit: '0.045000', customerRate: { cycleSec: 60, cycleRate: '0.080000' }, vendorRate: { cycleSec: 60, cycleRate: '0.035000' }, }, }); await prisma.recording.upsert({ where: { id: 'rec_auto_001' }, update: { rawCdrId: 'raw_auto_001', storageKey: 'seed/call_auto_001.wav', storagePath: '/recordings/seed/call_auto_001.wav', sha256: 'a'.repeat(64), bytes: BigInt(55758), durationSec: 66, status: 'READY', movedAt: endedAt, }, create: { id: 'rec_auto_001', rawCdrId: 'raw_auto_001', storageKey: 'seed/call_auto_001.wav', storagePath: '/recordings/seed/call_auto_001.wav', sha256: 'a'.repeat(64), bytes: BigInt(55758), durationSec: 66, status: 'READY', movedAt: endedAt, }, }); await prisma.qualitySamplingRule.upsert({ where: { id: 'qsr_auto_001' }, update: { name: '自动化抽检规则', customerId: 'cus_auto_001', lineGroupId: 'llg_auto_001', ratio: '10.00', status: 'ENABLED', effectiveAt: startedAt, expiresAt: null, updatedBy: ACTOR_ID, deletedAt: null, }, create: { id: 'qsr_auto_001', name: '自动化抽检规则', customerId: 'cus_auto_001', lineGroupId: 'llg_auto_001', ratio: '10.00', status: 'ENABLED', effectiveAt: startedAt, createdBy: ACTOR_ID, updatedBy: ACTOR_ID, }, }); await prisma.qualityReview.upsert({ where: { id: 'qr_auto_001' }, update: { recordingId: 'rec_auto_001', reviewerId: 'usr_test_quality', score: 88, result: 'PASS', issueTags: [], notes: '自动化测试质检样本', reviewedAt: endedAt, }, create: { id: 'qr_auto_001', recordingId: 'rec_auto_001', reviewerId: 'usr_test_quality', score: 88, result: 'PASS', issueTags: [], notes: '自动化测试质检样本', reviewedAt: endedAt, }, }); } async function main() { await seedAuth(); await seedNumberLibrary(); await seedBusinessData(); await seedCdrAndQuality(); console.log('Test seed completed.'); console.log(`Login users: ${users.map((user) => user.username).join(', ')}`); console.log(`Default password: ${TEST_PASSWORD}`); } main() .catch((error) => { console.error('Test seed failed.', error); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });