diff --git a/packages/backend/server/src/core/auth/guard.ts b/packages/backend/server/src/core/auth/guard.ts index eed1d6f727..7983f3adef 100644 --- a/packages/backend/server/src/core/auth/guard.ts +++ b/packages/backend/server/src/core/auth/guard.ts @@ -8,13 +8,11 @@ import { UseGuards, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; +import { PrismaClient } from '@prisma/client'; import type { NextAuthOptions } from 'next-auth'; import { AuthHandler } from 'next-auth/core'; -import { - getRequestResponseFromContext, - PrismaService, -} from '../../fundamentals'; +import { getRequestResponseFromContext } from '../../fundamentals'; import { NextAuthOptionsProvide } from './next-auth-options'; import { AuthService } from './service'; @@ -57,7 +55,7 @@ class AuthGuard implements CanActivate { @Inject(NextAuthOptionsProvide) private readonly nextAuthOptions: NextAuthOptions, private readonly auth: AuthService, - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly reflector: Reflector ) {} diff --git a/packages/backend/server/src/core/auth/next-auth-options.ts b/packages/backend/server/src/core/auth/next-auth-options.ts index b65ed520c0..70a58c3205 100644 --- a/packages/backend/server/src/core/auth/next-auth-options.ts +++ b/packages/backend/server/src/core/auth/next-auth-options.ts @@ -1,6 +1,7 @@ import { PrismaAdapter } from '@auth/prisma-adapter'; import { FactoryProvider, Logger } from '@nestjs/common'; import { verify } from '@node-rs/argon2'; +import { PrismaClient } from '@prisma/client'; import { assign, omit } from 'lodash-es'; import { NextAuthOptions } from 'next-auth'; import Credentials from 'next-auth/providers/credentials'; @@ -8,12 +9,7 @@ import Email from 'next-auth/providers/email'; import Github from 'next-auth/providers/github'; import Google from 'next-auth/providers/google'; -import { - Config, - MailService, - PrismaService, - SessionService, -} from '../../fundamentals'; +import { Config, MailService, SessionService } from '../../fundamentals'; import { FeatureType } from '../features'; import { Quota_FreePlanV1_1 } from '../quota'; import { @@ -31,7 +27,7 @@ export const NextAuthOptionsProvider: FactoryProvider = { provide: NextAuthOptionsProvide, useFactory( config: Config, - prisma: PrismaService, + prisma: PrismaClient, mailer: MailService, session: SessionService ) { @@ -284,5 +280,5 @@ export const NextAuthOptionsProvider: FactoryProvider = { }; return nextAuthOptions; }, - inject: [Config, PrismaService, MailService, SessionService], + inject: [Config, PrismaClient, MailService, SessionService], }; diff --git a/packages/backend/server/src/core/auth/next-auth.controller.ts b/packages/backend/server/src/core/auth/next-auth.controller.ts index ab083845aa..22ef1dca5f 100644 --- a/packages/backend/server/src/core/auth/next-auth.controller.ts +++ b/packages/backend/server/src/core/auth/next-auth.controller.ts @@ -15,7 +15,7 @@ import { UseGuards, } from '@nestjs/common'; import { hash, verify } from '@node-rs/argon2'; -import type { User } from '@prisma/client'; +import { PrismaClient, type User } from '@prisma/client'; import type { NextFunction, Request, Response } from 'express'; import { pick } from 'lodash-es'; import { nanoid } from 'nanoid'; @@ -26,7 +26,6 @@ import { AuthThrottlerGuard, Config, metrics, - PrismaService, SessionService, Throttle, } from '../../fundamentals'; @@ -45,7 +44,7 @@ export class NextAuthController { constructor( readonly config: Config, - readonly prisma: PrismaService, + readonly prisma: PrismaClient, private readonly authService: AuthService, @Inject(NextAuthOptionsProvide) private readonly nextAuthOptions: NextAuthOptions, diff --git a/packages/backend/server/src/core/auth/service.ts b/packages/backend/server/src/core/auth/service.ts index 26f863d1f7..41535175e2 100644 --- a/packages/backend/server/src/core/auth/service.ts +++ b/packages/backend/server/src/core/auth/service.ts @@ -8,13 +8,12 @@ import { } from '@nestjs/common'; import { hash, verify } from '@node-rs/argon2'; import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken'; -import type { User } from '@prisma/client'; +import { PrismaClient, type User } from '@prisma/client'; import { nanoid } from 'nanoid'; import { Config, MailService, - PrismaService, verifyChallengeResponse, } from '../../fundamentals'; import { Quota_FreePlanV1_1 } from '../quota'; @@ -32,7 +31,7 @@ export const getUtcTimestamp = () => Math.floor(Date.now() / 1000); export class AuthService { constructor( private readonly config: Config, - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly mailer: MailService ) {} diff --git a/packages/backend/server/src/core/auth/utils/jwt.ts b/packages/backend/server/src/core/auth/utils/jwt.ts index 929a26f1ad..92eac1f55c 100644 --- a/packages/backend/server/src/core/auth/utils/jwt.ts +++ b/packages/backend/server/src/core/auth/utils/jwt.ts @@ -2,14 +2,15 @@ import { randomUUID } from 'node:crypto'; import { BadRequestException } from '@nestjs/common'; import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken'; +import { PrismaClient } from '@prisma/client'; import { JWT } from 'next-auth/jwt'; -import { Config, PrismaService } from '../../../fundamentals'; +import { Config } from '../../../fundamentals'; import { getUtcTimestamp, UserClaim } from '../service'; export const jwtEncode = async ( config: Config, - prisma: PrismaService, + prisma: PrismaClient, token: JWT | undefined, maxAge: number | undefined ) => { diff --git a/packages/backend/server/src/core/doc/history.ts b/packages/backend/server/src/core/doc/history.ts index 303b6fd56e..5a65c3bea5 100644 --- a/packages/backend/server/src/core/doc/history.ts +++ b/packages/backend/server/src/core/doc/history.ts @@ -2,13 +2,13 @@ import { isDeepStrictEqual } from 'node:util'; import { Injectable, Logger } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; +import { PrismaClient } from '@prisma/client'; import { Config, type EventPayload, metrics, OnEvent, - PrismaService, } from '../../fundamentals'; import { QuotaService } from '../quota'; import { Permission } from '../workspaces/types'; @@ -19,7 +19,7 @@ export class DocHistoryManager { private readonly logger = new Logger(DocHistoryManager.name); constructor( private readonly config: Config, - private readonly db: PrismaService, + private readonly db: PrismaClient, private readonly quota: QuotaService ) {} diff --git a/packages/backend/server/src/core/doc/manager.ts b/packages/backend/server/src/core/doc/manager.ts index 1fa7ffafc5..3d0035e1ed 100644 --- a/packages/backend/server/src/core/doc/manager.ts +++ b/packages/backend/server/src/core/doc/manager.ts @@ -5,7 +5,7 @@ import { OnModuleInit, } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; -import { Snapshot, Update } from '@prisma/client'; +import { PrismaClient, Snapshot, Update } from '@prisma/client'; import { chunk } from 'lodash-es'; import { defer, retry } from 'rxjs'; import { @@ -25,7 +25,6 @@ import { mergeUpdatesInApplyWay as jwstMergeUpdates, metrics, OnEvent, - PrismaService, } from '../../fundamentals'; function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean { @@ -72,7 +71,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { private busy = false; constructor( - private readonly db: PrismaService, + private readonly db: PrismaClient, private readonly config: Config, private readonly cache: Cache, private readonly event: EventEmitter diff --git a/packages/backend/server/src/core/features/management.ts b/packages/backend/server/src/core/features/management.ts index e2e0f8c82c..3176e4d7d6 100644 --- a/packages/backend/server/src/core/features/management.ts +++ b/packages/backend/server/src/core/features/management.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; -import { Config, PrismaService } from '../../fundamentals'; +import { Config } from '../../fundamentals'; import { FeatureService } from './service'; import { FeatureType } from './types'; @@ -12,7 +13,7 @@ export class FeatureManagementService { constructor( private readonly feature: FeatureService, - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly config: Config ) {} diff --git a/packages/backend/server/src/core/features/service.ts b/packages/backend/server/src/core/features/service.ts index 23b442492e..2183b27c5b 100644 --- a/packages/backend/server/src/core/features/service.ts +++ b/packages/backend/server/src/core/features/service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; -import { PrismaService } from '../../fundamentals'; import { UserType } from '../users/types'; import { WorkspaceType } from '../workspaces/types'; import { FeatureConfigType, getFeature } from './feature'; @@ -8,7 +8,7 @@ import { FeatureKind, FeatureType } from './types'; @Injectable() export class FeatureService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaClient) {} async getFeaturesVersion() { const features = await this.prisma.features.findMany({ diff --git a/packages/backend/server/src/core/quota/quota.ts b/packages/backend/server/src/core/quota/quota.ts index 0aaf6d1282..d6d7657c01 100644 --- a/packages/backend/server/src/core/quota/quota.ts +++ b/packages/backend/server/src/core/quota/quota.ts @@ -1,4 +1,5 @@ -import { PrismaService } from '../../fundamentals'; +import { PrismaClient } from '@prisma/client'; + import { formatDate, formatSize, Quota, QuotaSchema } from './types'; const QuotaCache = new Map(); @@ -6,7 +7,7 @@ const QuotaCache = new Map(); export class QuotaConfig { readonly config: Quota; - static async get(prisma: PrismaService, featureId: number) { + static async get(prisma: PrismaClient, featureId: number) { const cachedQuota = QuotaCache.get(featureId); if (cachedQuota) { diff --git a/packages/backend/server/src/core/quota/service.ts b/packages/backend/server/src/core/quota/service.ts index f4880eda9e..d8b2b65fe7 100644 --- a/packages/backend/server/src/core/quota/service.ts +++ b/packages/backend/server/src/core/quota/service.ts @@ -1,15 +1,16 @@ import { Injectable } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; -import { type EventPayload, OnEvent, PrismaService } from '../../fundamentals'; +import { type EventPayload, OnEvent } from '../../fundamentals'; import { FeatureKind } from '../features'; import { QuotaConfig } from './quota'; import { QuotaType } from './types'; -type Transaction = Parameters[0]>[0]; +type Transaction = Parameters[0]>[0]; @Injectable() export class QuotaService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaClient) {} // get activated user quota async getUserQuota(userId: string) { diff --git a/packages/backend/server/src/core/users/resolver.ts b/packages/backend/server/src/core/users/resolver.ts index b047f0c2f1..e76079d797 100644 --- a/packages/backend/server/src/core/users/resolver.ts +++ b/packages/backend/server/src/core/users/resolver.ts @@ -7,7 +7,7 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import type { User } from '@prisma/client'; +import { PrismaClient, type User } from '@prisma/client'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { @@ -15,7 +15,6 @@ import { EventEmitter, type FileUpload, PaymentRequiredException, - PrismaService, Throttle, } from '../../fundamentals'; import { Auth, CurrentUser, Public, Publicable } from '../auth/guard'; @@ -40,7 +39,7 @@ import { UsersService } from './users'; @Resolver(() => UserType) export class UserResolver { constructor( - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly storage: AvatarStorage, private readonly users: UsersService, private readonly feature: FeatureManagementService, diff --git a/packages/backend/server/src/core/users/users.ts b/packages/backend/server/src/core/users/users.ts index f733249f9a..06c8aa0b62 100644 --- a/packages/backend/server/src/core/users/users.ts +++ b/packages/backend/server/src/core/users/users.ts @@ -1,10 +1,9 @@ import { Injectable } from '@nestjs/common'; - -import { PrismaService } from '../../fundamentals'; +import { PrismaClient } from '@prisma/client'; @Injectable() export class UsersService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaClient) {} async findUserByEmail(email: string) { return this.prisma.user.findFirst({ diff --git a/packages/backend/server/src/core/workspaces/controller.ts b/packages/backend/server/src/core/workspaces/controller.ts index 02dc651ba8..006518acd1 100644 --- a/packages/backend/server/src/core/workspaces/controller.ts +++ b/packages/backend/server/src/core/workspaces/controller.ts @@ -7,9 +7,10 @@ import { Param, Res, } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; import type { Response } from 'express'; -import { CallTimer, PrismaService } from '../../fundamentals'; +import { CallTimer } from '../../fundamentals'; import { Auth, CurrentUser, Publicable } from '../auth'; import { DocHistoryManager, DocManager } from '../doc'; import { WorkspaceBlobStorage } from '../storage'; @@ -26,7 +27,7 @@ export class WorkspacesController { private readonly permission: PermissionService, private readonly docManager: DocManager, private readonly historyManager: DocHistoryManager, - private readonly prisma: PrismaService + private readonly prisma: PrismaClient ) {} // get workspace blob diff --git a/packages/backend/server/src/core/workspaces/permission.ts b/packages/backend/server/src/core/workspaces/permission.ts index 3c17355d4a..c188e1166b 100644 --- a/packages/backend/server/src/core/workspaces/permission.ts +++ b/packages/backend/server/src/core/workspaces/permission.ts @@ -1,7 +1,6 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; -import { Prisma } from '@prisma/client'; +import { type Prisma, PrismaClient } from '@prisma/client'; -import { PrismaService } from '../../fundamentals'; import { Permission } from './types'; export enum PublicPageMode { @@ -11,7 +10,7 @@ export enum PublicPageMode { @Injectable() export class PermissionService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaClient) {} /// Start regin: workspace permission async get(ws: string, user: string) { diff --git a/packages/backend/server/src/core/workspaces/resolvers/page.ts b/packages/backend/server/src/core/workspaces/resolvers/page.ts index 11401895da..8364a351b5 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/page.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/page.ts @@ -9,9 +9,12 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client'; +import { + PrismaClient, + type WorkspacePage as PrismaWorkspacePage, +} from '@prisma/client'; -import { CloudThrottlerGuard, PrismaService } from '../../../fundamentals'; +import { CloudThrottlerGuard } from '../../../fundamentals'; import { Auth, CurrentUser } from '../../auth'; import { UserType } from '../../users'; import { DocID } from '../../utils/doc'; @@ -43,7 +46,7 @@ class WorkspacePage implements Partial { @Resolver(() => WorkspaceType) export class PagePermissionResolver { constructor( - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly permission: PermissionService ) {} diff --git a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts index 57e01ef2c4..87fe5bd46d 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts @@ -15,7 +15,7 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import type { User } from '@prisma/client'; +import { PrismaClient, type User } from '@prisma/client'; import { getStreamAsBuffer } from 'get-stream'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { applyUpdate, Doc } from 'yjs'; @@ -25,7 +25,6 @@ import { EventEmitter, type FileUpload, MailService, - PrismaService, Throttle, } from '../../../fundamentals'; import { Auth, CurrentUser, Public } from '../../auth'; @@ -57,7 +56,7 @@ export class WorkspaceResolver { constructor( private readonly auth: AuthService, private readonly mailer: MailService, - private readonly prisma: PrismaService, + private readonly prisma: PrismaClient, private readonly permissions: PermissionService, private readonly quota: QuotaManagementService, private readonly users: UsersService, diff --git a/packages/backend/server/src/fundamentals/index.ts b/packages/backend/server/src/fundamentals/index.ts index 350955530a..af674c34ff 100644 --- a/packages/backend/server/src/fundamentals/index.ts +++ b/packages/backend/server/src/fundamentals/index.ts @@ -21,7 +21,6 @@ export { GlobalExceptionFilter, OptionalModule, } from './nestjs'; -export { PrismaService } from './prisma'; export { SessionService } from './session'; export * from './storage'; export { type StorageProvider, StorageProviderFactory } from './storage'; diff --git a/packages/backend/server/src/fundamentals/prisma/index.ts b/packages/backend/server/src/fundamentals/prisma/index.ts index 1c5c8e257b..535e238122 100644 --- a/packages/backend/server/src/fundamentals/prisma/index.ts +++ b/packages/backend/server/src/fundamentals/prisma/index.ts @@ -3,16 +3,16 @@ import { PrismaClient } from '@prisma/client'; import { PrismaService } from './service'; -// both `PrismaService` and `PrismaClient` can be injected +// only `PrismaClient` can be injected const clientProvider: Provider = { provide: PrismaClient, - useExisting: PrismaService, + useClass: PrismaService, }; @Global() @Module({ - providers: [PrismaService, clientProvider], - exports: [PrismaService, clientProvider], + providers: [clientProvider], + exports: [clientProvider], }) export class PrismaModule {} export { PrismaService } from './service'; diff --git a/packages/backend/server/src/plugins/payment/resolver.ts b/packages/backend/server/src/plugins/payment/resolver.ts index 34746757d3..e171b7a998 100644 --- a/packages/backend/server/src/plugins/payment/resolver.ts +++ b/packages/backend/server/src/plugins/payment/resolver.ts @@ -18,11 +18,12 @@ import { Resolver, } from '@nestjs/graphql'; import type { User, UserInvoice, UserSubscription } from '@prisma/client'; +import { PrismaClient } from '@prisma/client'; import { groupBy } from 'lodash-es'; import { Auth, CurrentUser, Public } from '../../core/auth'; import { UserType } from '../../core/users'; -import { Config, PrismaService } from '../../fundamentals'; +import { Config } from '../../fundamentals'; import { decodeLookupKey, SubscriptionService } from './service'; import { InvoiceStatus, @@ -303,7 +304,7 @@ export class SubscriptionResolver { export class UserSubscriptionResolver { constructor( private readonly config: Config, - private readonly db: PrismaService + private readonly db: PrismaClient ) {} @ResolveField(() => UserSubscriptionType, { nullable: true }) diff --git a/packages/backend/server/src/plugins/payment/service.ts b/packages/backend/server/src/plugins/payment/service.ts index 069763ffef..30a1ffb3f8 100644 --- a/packages/backend/server/src/plugins/payment/service.ts +++ b/packages/backend/server/src/plugins/payment/service.ts @@ -7,10 +7,11 @@ import type { UserStripeCustomer, UserSubscription, } from '@prisma/client'; +import { PrismaClient } from '@prisma/client'; import Stripe from 'stripe'; import { FeatureManagementService } from '../../core/features'; -import { EventEmitter, PrismaService } from '../../fundamentals'; +import { EventEmitter } from '../../fundamentals'; import { ScheduleManager } from './schedule'; import { InvoiceStatus, @@ -56,7 +57,7 @@ export class SubscriptionService { constructor( private readonly stripe: Stripe, - private readonly db: PrismaService, + private readonly db: PrismaClient, private readonly scheduleManager: ScheduleManager, private readonly event: EventEmitter, private readonly features: FeatureManagementService diff --git a/packages/backend/server/tests/app.e2e.ts b/packages/backend/server/tests/app.e2e.ts index 8c59029ddb..6fd112927c 100644 --- a/packages/backend/server/tests/app.e2e.ts +++ b/packages/backend/server/tests/app.e2e.ts @@ -4,14 +4,13 @@ import { randomUUID } from 'node:crypto'; import { Transformer } from '@napi-rs/image'; import type { INestApplication } from '@nestjs/common'; import { hashSync } from '@node-rs/argon2'; -import { type User } from '@prisma/client'; +import { PrismaClient, type User } from '@prisma/client'; import ava, { type TestFn } from 'ava'; import type { Express } from 'express'; import request from 'supertest'; import { AppModule } from '../src/app.module'; import { FeatureManagementService } from '../src/core/features'; -import { PrismaService } from '../src/fundamentals/prisma'; import { createTestingApp } from './utils'; const gql = '/graphql'; @@ -52,7 +51,7 @@ test.beforeEach(async t => { imports: [AppModule], tapModule(builder) { builder - .overrideProvider(PrismaService) + .overrideProvider(PrismaClient) .useClass(FakePrisma) .overrideProvider(FeatureManagementService) .useValue({ canEarlyAccess: () => true }); diff --git a/packages/backend/server/tests/mailer.e2e.ts b/packages/backend/server/tests/mailer.e2e.ts index ee8ac1ec38..8197c8befa 100644 --- a/packages/backend/server/tests/mailer.e2e.ts +++ b/packages/backend/server/tests/mailer.e2e.ts @@ -7,7 +7,6 @@ import { getLatestMailMessage, } from '@affine-test/kit/utils/cloud'; import { TestingModule } from '@nestjs/testing'; -import { PrismaClient } from '@prisma/client'; import ava, { type TestFn } from 'ava'; import { AuthService } from '../src/core/auth/service'; @@ -20,13 +19,6 @@ const test = ava as TestFn<{ skip: boolean; }>; -// cleanup database before each test -test.beforeEach(async () => { - const client = new PrismaClient(); - await client.$connect(); - await client.user.deleteMany({}); -}); - test.beforeEach(async t => { t.context.module = await createTestingModule({ imports: [ diff --git a/packages/backend/server/tests/mailer.spec.ts b/packages/backend/server/tests/mailer.spec.ts index cb4c4353bd..7b983649e2 100644 --- a/packages/backend/server/tests/mailer.spec.ts +++ b/packages/backend/server/tests/mailer.spec.ts @@ -1,126 +1,11 @@ -import { randomUUID } from 'node:crypto'; - import type { INestApplication } from '@nestjs/common'; -import { hashSync } from '@node-rs/argon2'; -import { type User } from '@prisma/client'; -import { PrismaClient } from '@prisma/client'; import ava, { type TestFn } from 'ava'; +import Sinon from 'sinon'; import { AppModule } from '../src/app.module'; -import { FeatureKind, FeatureManagementService } from '../src/core/features'; -import { Quotas } from '../src/core/quota'; +import { FeatureManagementService } from '../src/core/features'; import { MailService } from '../src/fundamentals/mailer'; -import { - createTestingApp, - createWorkspace, - getInviteInfo, - inviteUser, - signUp, -} from './utils'; - -const FakePrisma = { - fakeUser: { - id: randomUUID(), - name: 'Alex Yang', - avatarUrl: '', - email: 'alex.yang@example.org', - password: hashSync('123456'), - emailVerified: new Date(), - createdAt: new Date(), - } satisfies User, - - get user() { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const prisma = this; - return { - async findFirst() { - return null; - }, - async create({ data }: any) { - return { - ...prisma.fakeUser, - ...data, - }; - }, - async findUnique() { - return prisma.fakeUser; - }, - }; - }, - get workspace() { - return { - id: randomUUID(), - async create({ data }: any) { - return { - id: this.id, - public: data.public ?? false, - createdAt: new Date(), - }; - }, - }; - }, - snapshot: { - id: randomUUID(), - async create() {}, - async findFirstOrThrow() { - return { id: this.id, blob: Buffer.from([0, 0]) }; - }, - }, - get workspaceUserPermission() { - return { - id: randomUUID(), - prisma: this, - async count() { - return 1; - }, - async create() { - return { id: this.id }; - }, - async findUniqueOrThrow() { - return { id: this.id, workspaceId: this.prisma.workspace.id }; - }, - async findFirst() { - return { id: this.id }; - }, - async findFirstOrThrow() { - return { id: this.id, user: this.prisma.fakeUser }; - }, - async workspaceUserPermission() { - return { - id: randomUUID(), - createdAt: new Date(), - }; - }, - }; - }, - get features() { - return { - async findFirst() { - return { - id: 1, - type: FeatureKind.Quota, - feature: Quotas[0].feature, - configs: Quotas[0].configs, - version: Quotas[0].version, - createdAt: new Date(), - }; - }, - }; - }, - get userFeatures() { - return { - async findFirst() { - return { - createdAt: new Date(), - featureId: 1, - reason: '', - expiredAt: null, - }; - }, - }; - }, -}; - +import { createTestingApp, createWorkspace, inviteUser, signUp } from './utils'; const test = ava as TestFn<{ app: INestApplication; mail: MailService; @@ -130,15 +15,11 @@ test.beforeEach(async t => { const { module, app } = await createTestingApp({ imports: [AppModule], tapModule: module => { - module - .overrideProvider(PrismaClient) - .useValue(FakePrisma) - .overrideProvider(FeatureManagementService) - .useValue({ - hasWorkspaceFeature() { - return false; - }, - }); + module.overrideProvider(FeatureManagementService).useValue({ + hasWorkspaceFeature() { + return false; + }, + }); }, }); @@ -153,38 +34,34 @@ test.afterEach.always(async t => { test('should send invite email', async t => { const { mail, app } = t.context; + if (mail.hasConfigured()) { const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1'); const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1'); const workspace = await createWorkspace(app, u1.token.token); - const inviteId = await inviteUser( + + const stub = Sinon.stub(mail, 'sendMail'); + + await inviteUser( app, u1.token.token, workspace.id, u2.email, - 'Admin' + 'Admin', + true ); - const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId); + t.true(stub.calledOnce); - const resp = await mail.sendInviteEmail( - 'production@toeverything.info', - inviteId, - { - workspace: { - id: inviteInfo.workspace.id, - name: inviteInfo.workspace.name, - avatar: '', - }, - user: { - avatar: inviteInfo.user?.avatarUrl || '', - name: inviteInfo.user?.name || '', - }, - } + const args = stub.args[0][0]; + + t.is(args.to, u2.email); + t.true( + args.subject!.startsWith( + `${u1.name} invited you to join` /* we don't know the name of mocked workspace */ + ) ); - - t.is(resp.accepted.length, 1, 'failed to send invite email'); } t.pass(); });