mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
test: email sending e2e (#4130)
This commit is contained in:
12
.github/workflows/build-server.yml
vendored
12
.github/workflows/build-server.yml
vendored
@@ -75,6 +75,11 @@ jobs:
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
mailer:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -112,6 +117,7 @@ jobs:
|
||||
env:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
ENABLE_LOCAL_EMAIL: true
|
||||
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v3
|
||||
@@ -139,6 +145,11 @@ jobs:
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
mailer:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -181,6 +192,7 @@ jobs:
|
||||
env:
|
||||
COVERAGE: true
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
ENABLE_LOCAL_EMAIL: true
|
||||
|
||||
- name: Collect code coverage report
|
||||
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
@@ -3,3 +3,4 @@ NEXTAUTH_URL="http://localhost:8080"
|
||||
OAUTH_EMAIL_SENDER="noreply@toeverything.info"
|
||||
OAUTH_EMAIL_LOGIN=""
|
||||
OAUTH_EMAIL_PASSWORD=""
|
||||
ENABLE_LOCAL_EMAIL="true"
|
||||
|
||||
18
apps/server/scripts/test-send-local-mail.ts
Normal file
18
apps/server/scripts/test-send-local-mail.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createTransport } from 'nodemailer';
|
||||
|
||||
const transport = createTransport({
|
||||
host: '0.0.0.0',
|
||||
port: 1025,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: 'himself65',
|
||||
pass: '123456',
|
||||
},
|
||||
});
|
||||
|
||||
await transport.sendMail({
|
||||
from: 'noreply@toeverything.info',
|
||||
to: 'himself65@outlook.com',
|
||||
subject: 'test',
|
||||
html: `<div>hello world</div>`,
|
||||
});
|
||||
@@ -308,6 +308,11 @@ export interface AFFiNEConfig {
|
||||
}
|
||||
>
|
||||
>;
|
||||
/**
|
||||
* whether to use local email service to send email
|
||||
* local debug only
|
||||
*/
|
||||
localEmail: boolean;
|
||||
email: {
|
||||
server: string;
|
||||
port: number;
|
||||
|
||||
@@ -86,6 +86,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
|
||||
'doc.manager.experimentalMergeWithJwstCodec',
|
||||
'boolean',
|
||||
],
|
||||
ENABLE_LOCAL_EMAIL: ['auth.localEmail', 'boolean'],
|
||||
} satisfies AFFiNEConfig['ENV_MAP'],
|
||||
affineEnv: 'dev',
|
||||
get affine() {
|
||||
@@ -152,6 +153,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
|
||||
return this.privateKey;
|
||||
},
|
||||
oauthProviders: {},
|
||||
localEmail: false,
|
||||
email: {
|
||||
server: 'smtp.gmail.com',
|
||||
port: 465,
|
||||
|
||||
@@ -15,6 +15,17 @@ export const MAILER: FactoryProvider<
|
||||
> = {
|
||||
provide: MAILER_SERVICE,
|
||||
useFactory: (config: Config) => {
|
||||
if (config.auth.localEmail) {
|
||||
return createTransport({
|
||||
host: '0.0.0.0',
|
||||
port: 1025,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: config.auth.email.login,
|
||||
pass: config.auth.email.password,
|
||||
},
|
||||
});
|
||||
}
|
||||
return createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
|
||||
@@ -4,3 +4,7 @@ import 'dotenv/config';
|
||||
import { getDefaultAFFiNEConfig } from './config/default';
|
||||
|
||||
globalThis.AFFiNE = getDefaultAFFiNEConfig();
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('AFFiNE Config:', globalThis.AFFiNE);
|
||||
}
|
||||
|
||||
105
apps/server/src/tests/mailer.e2e.ts
Normal file
105
apps/server/src/tests/mailer.e2e.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/// <reference types="../global.d.ts" />
|
||||
// This test case is for testing the mailer service.
|
||||
// Please use local SMTP server for testing.
|
||||
// See: https://github.com/mailhog/MailHog
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
|
||||
import { ConfigModule } from '../config';
|
||||
import { GqlModule } from '../graphql.module';
|
||||
import { MetricsModule } from '../metrics';
|
||||
import { AuthModule } from '../modules/auth';
|
||||
import { AuthService } from '../modules/auth/service';
|
||||
import { PrismaModule } from '../prisma';
|
||||
import { RateLimiterModule } from '../throttler';
|
||||
|
||||
let auth: AuthService;
|
||||
let module: TestingModule;
|
||||
let skip = false;
|
||||
|
||||
test.before(async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:8025/api/v2/messages');
|
||||
if (!response.ok && !process.env.CI) {
|
||||
console.warn('local mail not found, skip the mailer.e2e.ts');
|
||||
skip = true;
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
(error.cause as any)?.code === 'ECONNREFUSED' &&
|
||||
!process.env.CI
|
||||
) {
|
||||
console.warn('local mail not found, skip the mailer.e2e.ts');
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
module = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
auth: {
|
||||
accessTokenExpiresIn: 1,
|
||||
refreshTokenExpiresIn: 1,
|
||||
leeway: 1,
|
||||
},
|
||||
}),
|
||||
PrismaModule,
|
||||
GqlModule,
|
||||
AuthModule,
|
||||
MetricsModule,
|
||||
RateLimiterModule,
|
||||
],
|
||||
}).compile();
|
||||
auth = module.get(AuthService);
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await module.close();
|
||||
});
|
||||
|
||||
const getCurrentMailMessageCount = async () => {
|
||||
const response = await fetch('http://localhost:8025/api/v2/messages');
|
||||
const data = await response.json();
|
||||
return data.total;
|
||||
};
|
||||
|
||||
const getLatestMailMessage = async () => {
|
||||
const response = await fetch('http://localhost:8025/api/v2/messages');
|
||||
const data = await response.json();
|
||||
return data.items[0];
|
||||
};
|
||||
|
||||
test('should include callbackUrl in sending email', async t => {
|
||||
if (skip) {
|
||||
return t.pass();
|
||||
}
|
||||
await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
||||
for (const fn of [
|
||||
'sendSetPasswordEmail',
|
||||
'sendChangeEmail',
|
||||
'sendChangePasswordEmail',
|
||||
] as const) {
|
||||
const prev = await getCurrentMailMessageCount();
|
||||
await auth[fn]('alexyang@example.org', 'https://test.com/callback');
|
||||
const current = await getCurrentMailMessageCount();
|
||||
const mail = await getLatestMailMessage();
|
||||
t.regex(
|
||||
mail.Content.Body,
|
||||
/https:\/\/test.com\/callback/,
|
||||
`should include callbackUrl when calling ${fn}`
|
||||
);
|
||||
t.is(current, prev + 1, `calling ${fn}`);
|
||||
}
|
||||
return;
|
||||
});
|
||||
@@ -50,6 +50,7 @@ const config: PlaywrightTestConfig = {
|
||||
DEBUG: 'affine:*',
|
||||
FORCE_COLOR: 'true',
|
||||
DEBUG_COLORS: 'true',
|
||||
ENABLE_LOCAL_EMAIL: process.env.ENABLE_LOCAL_EMAIL ?? 'true',
|
||||
NEXTAUTH_URL: 'http://localhost:8080',
|
||||
OAUTH_EMAIL_SENDER: 'noreply@toeverything.info',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user