All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 44s
M3-04: RecentItem/Favorite/SearchHistory models, Tag CRUD, global search, workspace dashboard M3-05: NotificationPreference/PushToken/Template models, preferences, push tokens, admin templates M3-06: CacheService with wrap() penetration protection, key naming conventions, admin cache management E2E: 27 new tests for M3-04/05/06 (35/36 passing overall) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
// 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<string, Function> {
|
|
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',
|
|
]
|
|
|
|
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]
|
|
},
|
|
})
|
|
|
|
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 {},
|
|
}
|