// Mock @prisma/client for E2E tests. // PrismaService extends PrismaClient → this must be a plain class. // Model access (prisma.user.findMany()) is supported via prototype delegates. function modelMethods(): Record { return { findUnique: () => Promise.resolve(null), findFirst: () => Promise.resolve(null), findMany: () => Promise.resolve([]), findRaw: () => Promise.resolve([]), create: (args: any) => Promise.resolve({ id: 1, ...args?.data }), update: (args: any) => Promise.resolve({ id: 1, ...args?.data }), delete: () => Promise.resolve({ id: 1 }), upsert: (args: any) => Promise.resolve({ id: 1, ...args?.create }), count: () => Promise.resolve(0), aggregate: () => Promise.resolve({}), groupBy: () => Promise.resolve([]), createMany: () => Promise.resolve({ count: 1 }), deleteMany: () => Promise.resolve({ count: 0 }), updateMany: () => Promise.resolve({ count: 0 }), aggregateRaw: () => Promise.resolve([]), } } function createModelDelegate(): any { const methods = modelMethods() return new Proxy(methods, { get(target: any, prop: string) { if (prop === 'then') return undefined if (prop in target) return target[prop] return () => Promise.resolve(undefined) }, }) } // admin user fixture so login tests can get a real JWT const ADMIN_USER = { id: 'admin-test-001', email: 'admin@zhixi.app', displayName: 'Test Admin', passwordHash: '$2b$10$mp8kF.PwWBjb0fp/5d0nZ.VNofYcVm7jhJYtswxLfGU/EJW5K8qCm', // bcrypt hash of "admin123" role: 'SUPER_ADMIN', status: 'ACTIVE', twoFactorEnabled: false, failedLoginCount: 0, lockedUntil: null, deletedAt: null, lastLoginAt: null, lastLoginIp: null, createdAt: new Date(), updatedAt: new Date(), } const ADMIN_SESSION = { id: 1, adminUserId: 'admin-test-001', refreshTokenHash: 'test-hash', ip: null, userAgent: null, revokedAt: null, expiresAt: new Date(Date.now() + 7 * 86400000), createdAt: new Date(), } export class PrismaClient { $connect() { return Promise.resolve() } $disconnect() { return Promise.resolve() } $on() {} $transaction(fn: any) { const delegate = createModelDelegate() return typeof fn === 'function' ? fn(delegate) : Promise.resolve([]) } $executeRaw() { return Promise.resolve(0) } $queryRaw() { return Promise.resolve([]) } $runCommandRaw() { return Promise.resolve({}) } } const modelNames = [ 'user', 'authAccount', 'refreshToken', 'userProfile', 'userPreference', 'userConsent', 'knowledgeBase', 'knowledgeItem', 'knowledgeItemRelation', 'tag', 'knowledgeItemTag', 'uploadedFile', 'documentImport', 'learningSession', 'learningRecord', 'activeRecallQuestion', 'activeRecallAnswer', 'aiAnalysisJob', 'aiAnalysisResult', 'focusItem', 'reviewCard', 'reviewLog', 'reviewPlan', 'dailyLearningActivity', 'notification', 'feedback', 'aiUsageLog', 'waitlistEntry', 'appChangelog', 'knowledgeSource', 'knowledgeChunk', 'importCandidate', 'backupJob', 'adminUser', 'adminSession', 'adminAuditLog', 'membershipPlan', 'adminConversation', 'adminMessage', 'adminCostItem', 'appConfig', 'featureFlag', 'configChangeLog', 'securityEvent', 'sensitiveWord', 'contentSafetyCheck', 'contentReport', 'apiMetric', 'taskLog', 'userMembership', 'quotaUsage', 'costDailySummary', 'secretRecord', 'secretAccessLog', 'modelRoute', 'providerConfig', 'fallbackEvent', 'violationRecord', 'contentReport', 'userDevice', 'accountDeletionRequest', 'workspace', 'knowledgeFolder', 'sourceReference', 'importStepLog', 'recentItem', 'favorite', 'searchHistory', 'chatSession', 'chatMessage', 'chatCitation', 'artifact', 'learningGoal', 'streakRecord', 'notificationPreference', 'pushToken', 'notificationTemplate', 'learningGoal', 'streakRecord', 'learningStats', ] for (const name of modelNames) { ;(PrismaClient.prototype as any)[name] = createModelDelegate() } // Patch adminUser.findUnique so login tests can succeed const origAdminUser = (PrismaClient.prototype as any).adminUser ;(PrismaClient.prototype as any).adminUser = new Proxy(origAdminUser, { get(target: any, prop: string) { if (prop === 'findUnique') { return (args: any) => { if (args?.where?.email === ADMIN_USER.email) return Promise.resolve(ADMIN_USER) if (args?.where?.id === ADMIN_USER.id) return Promise.resolve(ADMIN_USER) return target.findUnique(args) } } if (prop === 'findFirst') { return (args: any) => { if (args?.where?.email === ADMIN_USER.email) return Promise.resolve(ADMIN_USER) return target.findFirst(args) } } return target[prop] }, }) // Patch adminSession so admin auth guard doesn't reject const origAdminSession = (PrismaClient.prototype as any).adminSession ;(PrismaClient.prototype as any).adminSession = new Proxy(origAdminSession, { get(target: any, prop: string) { if (prop === 'findUnique' || prop === 'findFirst') { return (_args?: any) => Promise.resolve(ADMIN_SESSION) } return target[prop] }, }) // Patch user so JwtAuthGuard status check passes for test users const TEST_USER = { id: 'test-user', email: 'test@test.com', role: 'USER', status: 'active', deletedAt: null, } const origUser = (PrismaClient.prototype as any).user ;(PrismaClient.prototype as any).user = new Proxy(origUser, { get(target: any, prop: string) { if (prop === 'findUnique') { return (args: any) => { if (args?.where?.id === 'test-user') return Promise.resolve(TEST_USER) if (args?.where?.id === 'disabled-user') return Promise.resolve({ ...TEST_USER, id: 'disabled-user', status: 'disabled' }) if (args?.where?.id === 'deleted-user') return Promise.resolve({ ...TEST_USER, id: 'deleted-user', deletedAt: new Date() }) return target.findUnique(args) } } return target[prop] }, }) // Patch refreshToken so the refresh flow works in tests const knownRefreshHashes = new Set() const origRefreshToken = (PrismaClient.prototype as any).refreshToken ;(PrismaClient.prototype as any).refreshToken = new Proxy(origRefreshToken, { get(target: any, prop: string) { if (prop === 'findFirst') { return (args: any) => { const hash = args?.where?.tokenHash if (hash && knownRefreshHashes.has(hash)) { return Promise.resolve({ id: 'rt-test-001', userId: 'test-user', tokenHash: hash, deviceId: null, deviceName: null, expiresAt: new Date(Date.now() + 7 * 86400000), revokedAt: null, user: { ...TEST_USER }, }) } return Promise.resolve(null) } } if (prop === 'update') { return () => Promise.resolve({ id: 'rt-test-001' }) } if (prop === 'updateMany') { return () => Promise.resolve({ count: 1 }) } if (prop === 'create') { return (args: any) => { if (args?.data?.tokenHash) { knownRefreshHashes.add(args.data.tokenHash) } return Promise.resolve({ id: 'rt-test-new', ...args?.data }) } } return target[prop] }, }) export const Prisma = { ModelName: {}, PrismaClientKnownRequestError: class extends Error { code: string constructor(message: string, opts: { code: string; clientVersion: string }) { super(message) this.code = opts.code } }, PrismaClientValidationError: class extends Error {}, PrismaClientInitializationError: class extends Error {}, }