import { randomUUID } from 'node:crypto'; import type { INestApplication } from '@nestjs/common'; import { hashSync } from '@node-rs/argon2'; import { type User } from '@prisma/client'; import ava, { type TestFn } from 'ava'; import { AppModule } from '../src/app'; import { MailService } from '../src/modules/auth/mailer'; import { FeatureKind, FeatureManagementService } from '../src/modules/features'; import { Quotas } from '../src/modules/quota'; import { PrismaService } from '../src/prisma'; 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, }; }, }; }, }; const test = ava as TestFn<{ app: INestApplication; mail: MailService; }>; test.beforeEach(async t => { const { module, app } = await createTestingApp({ imports: [AppModule], tapModule: module => { module .overrideProvider(PrismaService) .useValue(FakePrisma) .overrideProvider(FeatureManagementService) .useValue({ hasWorkspaceFeature() { return false; }, }); }, }); const mail = module.get(MailService); t.context.app = app; t.context.mail = mail; }); test.afterEach.always(async t => { await t.context.app.close(); }); 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( app, u1.token.token, workspace.id, u2.email, 'Admin' ); const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId); 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 || '', }, } ); t.is(resp.accepted.length, 1, 'failed to send invite email'); } t.pass(); });