refactor(server): import prisma from @prisma/client (#5863)

This commit is contained in:
liuyi
2024-02-22 07:46:57 +00:00
parent f2c5786dd0
commit 4d421a324f
24 changed files with 82 additions and 218 deletions

View File

@@ -8,13 +8,11 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import type { NextAuthOptions } from 'next-auth'; import type { NextAuthOptions } from 'next-auth';
import { AuthHandler } from 'next-auth/core'; import { AuthHandler } from 'next-auth/core';
import { import { getRequestResponseFromContext } from '../../fundamentals';
getRequestResponseFromContext,
PrismaService,
} from '../../fundamentals';
import { NextAuthOptionsProvide } from './next-auth-options'; import { NextAuthOptionsProvide } from './next-auth-options';
import { AuthService } from './service'; import { AuthService } from './service';
@@ -57,7 +55,7 @@ class AuthGuard implements CanActivate {
@Inject(NextAuthOptionsProvide) @Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions, private readonly nextAuthOptions: NextAuthOptions,
private readonly auth: AuthService, private readonly auth: AuthService,
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly reflector: Reflector private readonly reflector: Reflector
) {} ) {}

View File

@@ -1,6 +1,7 @@
import { PrismaAdapter } from '@auth/prisma-adapter'; import { PrismaAdapter } from '@auth/prisma-adapter';
import { FactoryProvider, Logger } from '@nestjs/common'; import { FactoryProvider, Logger } from '@nestjs/common';
import { verify } from '@node-rs/argon2'; import { verify } from '@node-rs/argon2';
import { PrismaClient } from '@prisma/client';
import { assign, omit } from 'lodash-es'; import { assign, omit } from 'lodash-es';
import { NextAuthOptions } from 'next-auth'; import { NextAuthOptions } from 'next-auth';
import Credentials from 'next-auth/providers/credentials'; 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 Github from 'next-auth/providers/github';
import Google from 'next-auth/providers/google'; import Google from 'next-auth/providers/google';
import { import { Config, MailService, SessionService } from '../../fundamentals';
Config,
MailService,
PrismaService,
SessionService,
} from '../../fundamentals';
import { FeatureType } from '../features'; import { FeatureType } from '../features';
import { Quota_FreePlanV1_1 } from '../quota'; import { Quota_FreePlanV1_1 } from '../quota';
import { import {
@@ -31,7 +27,7 @@ export const NextAuthOptionsProvider: FactoryProvider<NextAuthOptions> = {
provide: NextAuthOptionsProvide, provide: NextAuthOptionsProvide,
useFactory( useFactory(
config: Config, config: Config,
prisma: PrismaService, prisma: PrismaClient,
mailer: MailService, mailer: MailService,
session: SessionService session: SessionService
) { ) {
@@ -284,5 +280,5 @@ export const NextAuthOptionsProvider: FactoryProvider<NextAuthOptions> = {
}; };
return nextAuthOptions; return nextAuthOptions;
}, },
inject: [Config, PrismaService, MailService, SessionService], inject: [Config, PrismaClient, MailService, SessionService],
}; };

View File

@@ -15,7 +15,7 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2'; 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 type { NextFunction, Request, Response } from 'express';
import { pick } from 'lodash-es'; import { pick } from 'lodash-es';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
@@ -26,7 +26,6 @@ import {
AuthThrottlerGuard, AuthThrottlerGuard,
Config, Config,
metrics, metrics,
PrismaService,
SessionService, SessionService,
Throttle, Throttle,
} from '../../fundamentals'; } from '../../fundamentals';
@@ -45,7 +44,7 @@ export class NextAuthController {
constructor( constructor(
readonly config: Config, readonly config: Config,
readonly prisma: PrismaService, readonly prisma: PrismaClient,
private readonly authService: AuthService, private readonly authService: AuthService,
@Inject(NextAuthOptionsProvide) @Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions, private readonly nextAuthOptions: NextAuthOptions,

View File

@@ -8,13 +8,12 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2'; import { hash, verify } from '@node-rs/argon2';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken'; 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 { nanoid } from 'nanoid';
import { import {
Config, Config,
MailService, MailService,
PrismaService,
verifyChallengeResponse, verifyChallengeResponse,
} from '../../fundamentals'; } from '../../fundamentals';
import { Quota_FreePlanV1_1 } from '../quota'; import { Quota_FreePlanV1_1 } from '../quota';
@@ -32,7 +31,7 @@ export const getUtcTimestamp = () => Math.floor(Date.now() / 1000);
export class AuthService { export class AuthService {
constructor( constructor(
private readonly config: Config, private readonly config: Config,
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly mailer: MailService private readonly mailer: MailService
) {} ) {}

View File

@@ -2,14 +2,15 @@ import { randomUUID } from 'node:crypto';
import { BadRequestException } from '@nestjs/common'; import { BadRequestException } from '@nestjs/common';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken'; import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import { JWT } from 'next-auth/jwt'; import { JWT } from 'next-auth/jwt';
import { Config, PrismaService } from '../../../fundamentals'; import { Config } from '../../../fundamentals';
import { getUtcTimestamp, UserClaim } from '../service'; import { getUtcTimestamp, UserClaim } from '../service';
export const jwtEncode = async ( export const jwtEncode = async (
config: Config, config: Config,
prisma: PrismaService, prisma: PrismaClient,
token: JWT | undefined, token: JWT | undefined,
maxAge: number | undefined maxAge: number | undefined
) => { ) => {

View File

@@ -2,13 +2,13 @@ import { isDeepStrictEqual } from 'node:util';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule'; import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaClient } from '@prisma/client';
import { import {
Config, Config,
type EventPayload, type EventPayload,
metrics, metrics,
OnEvent, OnEvent,
PrismaService,
} from '../../fundamentals'; } from '../../fundamentals';
import { QuotaService } from '../quota'; import { QuotaService } from '../quota';
import { Permission } from '../workspaces/types'; import { Permission } from '../workspaces/types';
@@ -19,7 +19,7 @@ export class DocHistoryManager {
private readonly logger = new Logger(DocHistoryManager.name); private readonly logger = new Logger(DocHistoryManager.name);
constructor( constructor(
private readonly config: Config, private readonly config: Config,
private readonly db: PrismaService, private readonly db: PrismaClient,
private readonly quota: QuotaService private readonly quota: QuotaService
) {} ) {}

View File

@@ -5,7 +5,7 @@ import {
OnModuleInit, OnModuleInit,
} from '@nestjs/common'; } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule'; 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 { chunk } from 'lodash-es';
import { defer, retry } from 'rxjs'; import { defer, retry } from 'rxjs';
import { import {
@@ -25,7 +25,6 @@ import {
mergeUpdatesInApplyWay as jwstMergeUpdates, mergeUpdatesInApplyWay as jwstMergeUpdates,
metrics, metrics,
OnEvent, OnEvent,
PrismaService,
} from '../../fundamentals'; } from '../../fundamentals';
function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean { function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean {
@@ -72,7 +71,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
private busy = false; private busy = false;
constructor( constructor(
private readonly db: PrismaService, private readonly db: PrismaClient,
private readonly config: Config, private readonly config: Config,
private readonly cache: Cache, private readonly cache: Cache,
private readonly event: EventEmitter private readonly event: EventEmitter

View File

@@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common'; 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 { FeatureService } from './service';
import { FeatureType } from './types'; import { FeatureType } from './types';
@@ -12,7 +13,7 @@ export class FeatureManagementService {
constructor( constructor(
private readonly feature: FeatureService, private readonly feature: FeatureService,
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly config: Config private readonly config: Config
) {} ) {}

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaService } from '../../fundamentals';
import { UserType } from '../users/types'; import { UserType } from '../users/types';
import { WorkspaceType } from '../workspaces/types'; import { WorkspaceType } from '../workspaces/types';
import { FeatureConfigType, getFeature } from './feature'; import { FeatureConfigType, getFeature } from './feature';
@@ -8,7 +8,7 @@ import { FeatureKind, FeatureType } from './types';
@Injectable() @Injectable()
export class FeatureService { export class FeatureService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaClient) {}
async getFeaturesVersion() { async getFeaturesVersion() {
const features = await this.prisma.features.findMany({ const features = await this.prisma.features.findMany({

View File

@@ -1,4 +1,5 @@
import { PrismaService } from '../../fundamentals'; import { PrismaClient } from '@prisma/client';
import { formatDate, formatSize, Quota, QuotaSchema } from './types'; import { formatDate, formatSize, Quota, QuotaSchema } from './types';
const QuotaCache = new Map<number, QuotaConfig>(); const QuotaCache = new Map<number, QuotaConfig>();
@@ -6,7 +7,7 @@ const QuotaCache = new Map<number, QuotaConfig>();
export class QuotaConfig { export class QuotaConfig {
readonly config: Quota; readonly config: Quota;
static async get(prisma: PrismaService, featureId: number) { static async get(prisma: PrismaClient, featureId: number) {
const cachedQuota = QuotaCache.get(featureId); const cachedQuota = QuotaCache.get(featureId);
if (cachedQuota) { if (cachedQuota) {

View File

@@ -1,15 +1,16 @@
import { Injectable } from '@nestjs/common'; 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 { FeatureKind } from '../features';
import { QuotaConfig } from './quota'; import { QuotaConfig } from './quota';
import { QuotaType } from './types'; import { QuotaType } from './types';
type Transaction = Parameters<Parameters<PrismaService['$transaction']>[0]>[0]; type Transaction = Parameters<Parameters<PrismaClient['$transaction']>[0]>[0];
@Injectable() @Injectable()
export class QuotaService { export class QuotaService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaClient) {}
// get activated user quota // get activated user quota
async getUserQuota(userId: string) { async getUserQuota(userId: string) {

View File

@@ -7,7 +7,7 @@ import {
ResolveField, ResolveField,
Resolver, Resolver,
} from '@nestjs/graphql'; } from '@nestjs/graphql';
import type { User } from '@prisma/client'; import { PrismaClient, type User } from '@prisma/client';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { import {
@@ -15,7 +15,6 @@ import {
EventEmitter, EventEmitter,
type FileUpload, type FileUpload,
PaymentRequiredException, PaymentRequiredException,
PrismaService,
Throttle, Throttle,
} from '../../fundamentals'; } from '../../fundamentals';
import { Auth, CurrentUser, Public, Publicable } from '../auth/guard'; import { Auth, CurrentUser, Public, Publicable } from '../auth/guard';
@@ -40,7 +39,7 @@ import { UsersService } from './users';
@Resolver(() => UserType) @Resolver(() => UserType)
export class UserResolver { export class UserResolver {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly storage: AvatarStorage, private readonly storage: AvatarStorage,
private readonly users: UsersService, private readonly users: UsersService,
private readonly feature: FeatureManagementService, private readonly feature: FeatureManagementService,

View File

@@ -1,10 +1,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaService } from '../../fundamentals';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaClient) {}
async findUserByEmail(email: string) { async findUserByEmail(email: string) {
return this.prisma.user.findFirst({ return this.prisma.user.findFirst({

View File

@@ -7,9 +7,10 @@ import {
Param, Param,
Res, Res,
} from '@nestjs/common'; } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import type { Response } from 'express'; import type { Response } from 'express';
import { CallTimer, PrismaService } from '../../fundamentals'; import { CallTimer } from '../../fundamentals';
import { Auth, CurrentUser, Publicable } from '../auth'; import { Auth, CurrentUser, Publicable } from '../auth';
import { DocHistoryManager, DocManager } from '../doc'; import { DocHistoryManager, DocManager } from '../doc';
import { WorkspaceBlobStorage } from '../storage'; import { WorkspaceBlobStorage } from '../storage';
@@ -26,7 +27,7 @@ export class WorkspacesController {
private readonly permission: PermissionService, private readonly permission: PermissionService,
private readonly docManager: DocManager, private readonly docManager: DocManager,
private readonly historyManager: DocHistoryManager, private readonly historyManager: DocHistoryManager,
private readonly prisma: PrismaService private readonly prisma: PrismaClient
) {} ) {}
// get workspace blob // get workspace blob

View File

@@ -1,7 +1,6 @@
import { ForbiddenException, Injectable } from '@nestjs/common'; 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'; import { Permission } from './types';
export enum PublicPageMode { export enum PublicPageMode {
@@ -11,7 +10,7 @@ export enum PublicPageMode {
@Injectable() @Injectable()
export class PermissionService { export class PermissionService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaClient) {}
/// Start regin: workspace permission /// Start regin: workspace permission
async get(ws: string, user: string) { async get(ws: string, user: string) {

View File

@@ -9,9 +9,12 @@ import {
ResolveField, ResolveField,
Resolver, Resolver,
} from '@nestjs/graphql'; } 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 { Auth, CurrentUser } from '../../auth';
import { UserType } from '../../users'; import { UserType } from '../../users';
import { DocID } from '../../utils/doc'; import { DocID } from '../../utils/doc';
@@ -43,7 +46,7 @@ class WorkspacePage implements Partial<PrismaWorkspacePage> {
@Resolver(() => WorkspaceType) @Resolver(() => WorkspaceType)
export class PagePermissionResolver { export class PagePermissionResolver {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly permission: PermissionService private readonly permission: PermissionService
) {} ) {}

View File

@@ -15,7 +15,7 @@ import {
ResolveField, ResolveField,
Resolver, Resolver,
} from '@nestjs/graphql'; } from '@nestjs/graphql';
import type { User } from '@prisma/client'; import { PrismaClient, type User } from '@prisma/client';
import { getStreamAsBuffer } from 'get-stream'; import { getStreamAsBuffer } from 'get-stream';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { applyUpdate, Doc } from 'yjs'; import { applyUpdate, Doc } from 'yjs';
@@ -25,7 +25,6 @@ import {
EventEmitter, EventEmitter,
type FileUpload, type FileUpload,
MailService, MailService,
PrismaService,
Throttle, Throttle,
} from '../../../fundamentals'; } from '../../../fundamentals';
import { Auth, CurrentUser, Public } from '../../auth'; import { Auth, CurrentUser, Public } from '../../auth';
@@ -57,7 +56,7 @@ export class WorkspaceResolver {
constructor( constructor(
private readonly auth: AuthService, private readonly auth: AuthService,
private readonly mailer: MailService, private readonly mailer: MailService,
private readonly prisma: PrismaService, private readonly prisma: PrismaClient,
private readonly permissions: PermissionService, private readonly permissions: PermissionService,
private readonly quota: QuotaManagementService, private readonly quota: QuotaManagementService,
private readonly users: UsersService, private readonly users: UsersService,

View File

@@ -21,7 +21,6 @@ export {
GlobalExceptionFilter, GlobalExceptionFilter,
OptionalModule, OptionalModule,
} from './nestjs'; } from './nestjs';
export { PrismaService } from './prisma';
export { SessionService } from './session'; export { SessionService } from './session';
export * from './storage'; export * from './storage';
export { type StorageProvider, StorageProviderFactory } from './storage'; export { type StorageProvider, StorageProviderFactory } from './storage';

View File

@@ -3,16 +3,16 @@ import { PrismaClient } from '@prisma/client';
import { PrismaService } from './service'; import { PrismaService } from './service';
// both `PrismaService` and `PrismaClient` can be injected // only `PrismaClient` can be injected
const clientProvider: Provider = { const clientProvider: Provider = {
provide: PrismaClient, provide: PrismaClient,
useExisting: PrismaService, useClass: PrismaService,
}; };
@Global() @Global()
@Module({ @Module({
providers: [PrismaService, clientProvider], providers: [clientProvider],
exports: [PrismaService, clientProvider], exports: [clientProvider],
}) })
export class PrismaModule {} export class PrismaModule {}
export { PrismaService } from './service'; export { PrismaService } from './service';

View File

@@ -18,11 +18,12 @@ import {
Resolver, Resolver,
} from '@nestjs/graphql'; } from '@nestjs/graphql';
import type { User, UserInvoice, UserSubscription } from '@prisma/client'; import type { User, UserInvoice, UserSubscription } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { groupBy } from 'lodash-es'; import { groupBy } from 'lodash-es';
import { Auth, CurrentUser, Public } from '../../core/auth'; import { Auth, CurrentUser, Public } from '../../core/auth';
import { UserType } from '../../core/users'; import { UserType } from '../../core/users';
import { Config, PrismaService } from '../../fundamentals'; import { Config } from '../../fundamentals';
import { decodeLookupKey, SubscriptionService } from './service'; import { decodeLookupKey, SubscriptionService } from './service';
import { import {
InvoiceStatus, InvoiceStatus,
@@ -303,7 +304,7 @@ export class SubscriptionResolver {
export class UserSubscriptionResolver { export class UserSubscriptionResolver {
constructor( constructor(
private readonly config: Config, private readonly config: Config,
private readonly db: PrismaService private readonly db: PrismaClient
) {} ) {}
@ResolveField(() => UserSubscriptionType, { nullable: true }) @ResolveField(() => UserSubscriptionType, { nullable: true })

View File

@@ -7,10 +7,11 @@ import type {
UserStripeCustomer, UserStripeCustomer,
UserSubscription, UserSubscription,
} from '@prisma/client'; } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import Stripe from 'stripe'; import Stripe from 'stripe';
import { FeatureManagementService } from '../../core/features'; import { FeatureManagementService } from '../../core/features';
import { EventEmitter, PrismaService } from '../../fundamentals'; import { EventEmitter } from '../../fundamentals';
import { ScheduleManager } from './schedule'; import { ScheduleManager } from './schedule';
import { import {
InvoiceStatus, InvoiceStatus,
@@ -56,7 +57,7 @@ export class SubscriptionService {
constructor( constructor(
private readonly stripe: Stripe, private readonly stripe: Stripe,
private readonly db: PrismaService, private readonly db: PrismaClient,
private readonly scheduleManager: ScheduleManager, private readonly scheduleManager: ScheduleManager,
private readonly event: EventEmitter, private readonly event: EventEmitter,
private readonly features: FeatureManagementService private readonly features: FeatureManagementService

View File

@@ -4,14 +4,13 @@ import { randomUUID } from 'node:crypto';
import { Transformer } from '@napi-rs/image'; import { Transformer } from '@napi-rs/image';
import type { INestApplication } from '@nestjs/common'; import type { INestApplication } from '@nestjs/common';
import { hashSync } from '@node-rs/argon2'; 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 ava, { type TestFn } from 'ava';
import type { Express } from 'express'; import type { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import { AppModule } from '../src/app.module'; import { AppModule } from '../src/app.module';
import { FeatureManagementService } from '../src/core/features'; import { FeatureManagementService } from '../src/core/features';
import { PrismaService } from '../src/fundamentals/prisma';
import { createTestingApp } from './utils'; import { createTestingApp } from './utils';
const gql = '/graphql'; const gql = '/graphql';
@@ -52,7 +51,7 @@ test.beforeEach(async t => {
imports: [AppModule], imports: [AppModule],
tapModule(builder) { tapModule(builder) {
builder builder
.overrideProvider(PrismaService) .overrideProvider(PrismaClient)
.useClass(FakePrisma) .useClass(FakePrisma)
.overrideProvider(FeatureManagementService) .overrideProvider(FeatureManagementService)
.useValue({ canEarlyAccess: () => true }); .useValue({ canEarlyAccess: () => true });

View File

@@ -7,7 +7,6 @@ import {
getLatestMailMessage, getLatestMailMessage,
} from '@affine-test/kit/utils/cloud'; } from '@affine-test/kit/utils/cloud';
import { TestingModule } from '@nestjs/testing'; import { TestingModule } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import ava, { type TestFn } from 'ava'; import ava, { type TestFn } from 'ava';
import { AuthService } from '../src/core/auth/service'; import { AuthService } from '../src/core/auth/service';
@@ -20,13 +19,6 @@ const test = ava as TestFn<{
skip: boolean; 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 => { test.beforeEach(async t => {
t.context.module = await createTestingModule({ t.context.module = await createTestingModule({
imports: [ imports: [

View File

@@ -1,126 +1,11 @@
import { randomUUID } from 'node:crypto';
import type { INestApplication } from '@nestjs/common'; 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 ava, { type TestFn } from 'ava';
import Sinon from 'sinon';
import { AppModule } from '../src/app.module'; import { AppModule } from '../src/app.module';
import { FeatureKind, FeatureManagementService } from '../src/core/features'; import { FeatureManagementService } from '../src/core/features';
import { Quotas } from '../src/core/quota';
import { MailService } from '../src/fundamentals/mailer'; import { MailService } from '../src/fundamentals/mailer';
import { import { createTestingApp, createWorkspace, inviteUser, signUp } from './utils';
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,
};
},
};
},
};
const test = ava as TestFn<{ const test = ava as TestFn<{
app: INestApplication; app: INestApplication;
mail: MailService; mail: MailService;
@@ -130,15 +15,11 @@ test.beforeEach(async t => {
const { module, app } = await createTestingApp({ const { module, app } = await createTestingApp({
imports: [AppModule], imports: [AppModule],
tapModule: module => { tapModule: module => {
module module.overrideProvider(FeatureManagementService).useValue({
.overrideProvider(PrismaClient) hasWorkspaceFeature() {
.useValue(FakePrisma) return false;
.overrideProvider(FeatureManagementService) },
.useValue({ });
hasWorkspaceFeature() {
return false;
},
});
}, },
}); });
@@ -153,38 +34,34 @@ test.afterEach.always(async t => {
test('should send invite email', async t => { test('should send invite email', async t => {
const { mail, app } = t.context; const { mail, app } = t.context;
if (mail.hasConfigured()) { if (mail.hasConfigured()) {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1'); const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1'); const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token); const workspace = await createWorkspace(app, u1.token.token);
const inviteId = await inviteUser(
const stub = Sinon.stub(mail, 'sendMail');
await inviteUser(
app, app,
u1.token.token, u1.token.token,
workspace.id, workspace.id,
u2.email, u2.email,
'Admin' 'Admin',
true
); );
const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId); t.true(stub.calledOnce);
const resp = await mail.sendInviteEmail( const args = stub.args[0][0];
'production@toeverything.info',
inviteId, t.is(args.to, u2.email);
{ t.true(
workspace: { args.subject!.startsWith(
id: inviteInfo.workspace.id, `${u1.name} invited you to join` /* we don't know the name of mocked workspace */
name: inviteInfo.workspace.name, )
avatar: '',
},
user: {
avatar: inviteInfo.user?.avatarUrl || '',
name: inviteInfo.user?.name || '',
},
}
); );
t.is(resp.accepted.length, 1, 'failed to send invite email');
} }
t.pass(); t.pass();
}); });