parent
92db9a693a
commit
9b5d12dc71
@ -77,8 +77,6 @@ spec:
|
||||
value: "{{ .Values.app.https }}"
|
||||
- name: ENABLE_R2_OBJECT_STORAGE
|
||||
value: "{{ .Values.global.objectStorage.r2.enabled }}"
|
||||
- name: FEATURES_EARLY_ACCESS_PREVIEW
|
||||
value: "{{ .Values.app.features.earlyAccessPreview }}"
|
||||
- name: FEATURES_SYNC_CLIENT_VERSION_CHECK
|
||||
value: "{{ .Values.app.features.syncClientVersionCheck }}"
|
||||
- name: MAILER_HOST
|
||||
|
74
.github/workflows/build-test.yml
vendored
74
.github/workflows/build-test.yml
vendored
@ -452,7 +452,6 @@ jobs:
|
||||
total_nodes: [3]
|
||||
env:
|
||||
NODE_ENV: test
|
||||
DISTRIBUTION: web
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
REDIS_SERVER_HOST: localhost
|
||||
services:
|
||||
@ -511,6 +510,66 @@ jobs:
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
server-e2e-test:
|
||||
# the new version of server e2e test should be super fast, so sharding testing is not needed
|
||||
name: Server E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- optimize_ci
|
||||
- build-server-native
|
||||
if: needs.optimize_ci.outputs.skip == 'false'
|
||||
env:
|
||||
NODE_ENV: test
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
REDIS_SERVER_HOST: localhost
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16
|
||||
env:
|
||||
POSTGRES_PASSWORD: affine
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
full-cache: true
|
||||
|
||||
- name: Download server-native.node
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: server-native.node
|
||||
path: ./packages/backend/server
|
||||
|
||||
- name: Prepare Server Test Environment
|
||||
uses: ./.github/actions/server-test-env
|
||||
|
||||
- name: Run server tests
|
||||
run: yarn affine @affine/server e2e:coverage --forbid-only
|
||||
env:
|
||||
COPILOT_OPENAI_API_KEY: 'use_fake_openai_api_key'
|
||||
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/backend/server/.coverage/lcov.info
|
||||
flags: server-test
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
rust-test:
|
||||
name: Run native tests
|
||||
runs-on: ubuntu-latest
|
||||
@ -702,7 +761,7 @@ jobs:
|
||||
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
perplexity-key: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
|
||||
server-e2e-test:
|
||||
cloud-e2e-test:
|
||||
name: ${{ matrix.tests.name }}
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
@ -719,16 +778,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tests:
|
||||
- name: 'Server E2E Test 1/3'
|
||||
- name: 'Cloud E2E Test 1/3'
|
||||
shard: 1
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/3
|
||||
- name: 'Server E2E Test 2/3'
|
||||
- name: 'Cloud E2E Test 2/3'
|
||||
shard: 2
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/3
|
||||
- name: 'Server E2E Test 3/3'
|
||||
- name: 'Cloud E2E Test 3/3'
|
||||
shard: 3
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=3/3
|
||||
- name: 'Server Desktop E2E Test'
|
||||
- name: 'Cloud Desktop E2E Test'
|
||||
shard: desktop
|
||||
script: |
|
||||
yarn affine @affine/electron build:dev
|
||||
@ -951,11 +1010,12 @@ jobs:
|
||||
- build-electron-renderer
|
||||
- native-unit-test
|
||||
- server-test
|
||||
- server-e2e-test
|
||||
- rust-test
|
||||
- copilot-api-test
|
||||
- copilot-e2e-test
|
||||
- server-e2e-test
|
||||
- desktop-test
|
||||
- cloud-e2e-test
|
||||
- test-build-mobile-app
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
|
36
.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch
Normal file
36
.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch
Normal file
@ -0,0 +1,36 @@
|
||||
diff --git a/testing-module.d.ts b/testing-module.d.ts
|
||||
index 0b08dfe24534c605f58f2104255eb2a20c3fb566..8fad3ab267decccca2d4d9a8e208ace2cd811e92 100644
|
||||
--- a/testing-module.d.ts
|
||||
+++ b/testing-module.d.ts
|
||||
@@ -13,6 +13,7 @@ export declare class TestingModule extends NestApplicationContext {
|
||||
protected readonly graphInspector: GraphInspector;
|
||||
constructor(container: NestContainer, graphInspector: GraphInspector, contextModule: Module, applicationConfig: ApplicationConfig, scope?: Type<any>[]);
|
||||
private isHttpServer;
|
||||
+ useCustomApplicationConstructor(Ctor: Type<INestApplication>): void;
|
||||
createNestApplication<T extends INestApplication = INestApplication>(httpAdapter: HttpServer | AbstractHttpAdapter, options?: NestApplicationOptions): T;
|
||||
createNestApplication<T extends INestApplication = INestApplication>(options?: NestApplicationOptions): T;
|
||||
createNestMicroservice<T extends object>(options: NestMicroserviceOptions & T): INestMicroservice;
|
||||
diff --git a/testing-module.js b/testing-module.js
|
||||
index 17957b409b224bc43c7e40a1071cf08061366063..6bc4e8a694fdec02df91e512131ffd70259d8859 100644
|
||||
--- a/testing-module.js
|
||||
+++ b/testing-module.js
|
||||
@@ -15,6 +15,9 @@ class TestingModule extends core_1.NestApplicationContext {
|
||||
this.applicationConfig = applicationConfig;
|
||||
this.graphInspector = graphInspector;
|
||||
}
|
||||
+ useCustomApplicationConstructor(Ctor) {
|
||||
+ this.applicationConstructor = Ctor;
|
||||
+ }
|
||||
isHttpServer(serverOrOptions) {
|
||||
return !!(serverOrOptions && serverOrOptions.patch);
|
||||
}
|
||||
@@ -24,7 +27,8 @@ class TestingModule extends core_1.NestApplicationContext {
|
||||
: [this.createHttpAdapter(), serverOrOptions];
|
||||
this.applyLogger(appOptions);
|
||||
this.container.setHttpAdapter(httpAdapter);
|
||||
- const instance = new core_1.NestApplication(this.container, httpAdapter, this.applicationConfig, this.graphInspector, appOptions);
|
||||
+ const Ctor = this.applicationConstructor || core_1.NestApplication;
|
||||
+ const instance = new Ctor(this.container, httpAdapter, this.applicationConfig, this.graphInspector, appOptions);
|
||||
return this.createAdapterProxy(instance, httpAdapter);
|
||||
}
|
||||
createNestMicroservice(options) {
|
31
packages/backend/server/ava.config.js
Normal file
31
packages/backend/server/ava.config.js
Normal file
@ -0,0 +1,31 @@
|
||||
const newE2E = process.env.TEST_MODE === 'e2e';
|
||||
const newE2ETests = './src/__tests__/e2e/**/*.spec.ts';
|
||||
|
||||
const preludes = ['./src/prelude.ts'];
|
||||
|
||||
if (newE2E) {
|
||||
preludes.push('./src/__tests__/e2e/prelude.ts');
|
||||
}
|
||||
|
||||
export default {
|
||||
timeout: '1m',
|
||||
extensions: {
|
||||
ts: 'module',
|
||||
},
|
||||
watchMode: {
|
||||
ignoreChanges: ['**/*.gen.*'],
|
||||
},
|
||||
files: newE2E
|
||||
? [newE2ETests]
|
||||
: ['**/*.spec.ts', '**/*.e2e.ts', '!' + newE2ETests],
|
||||
require: preludes,
|
||||
environmentVariables: {
|
||||
NODE_ENV: 'test',
|
||||
DEPLOYMENT_TYPE: 'affine',
|
||||
MAILER_HOST: '0.0.0.0',
|
||||
MAILER_PORT: '1025',
|
||||
MAILER_USER: 'noreply@toeverything.info',
|
||||
MAILER_PASSWORD: 'affine',
|
||||
MAILER_SENDER: 'noreply@toeverything.info',
|
||||
},
|
||||
};
|
@ -15,6 +15,8 @@
|
||||
"test:copilot": "ava \"src/__tests__/**/copilot-*.spec.ts\"",
|
||||
"test:coverage": "c8 ava --concurrency 1 --serial",
|
||||
"test:copilot:coverage": "c8 ava --timeout=5m \"src/__tests__/**/copilot-*.spec.ts\"",
|
||||
"e2e": "cross-env TEST_MODE=e2e ava",
|
||||
"e2e:coverage": "cross-env TEST_MODE=e2e c8 ava",
|
||||
"data-migration": "cross-env NODE_ENV=development r ./src/data/index.ts",
|
||||
"init": "yarn prisma migrate dev && yarn data-migration run",
|
||||
"seed": "r ./src/seed/index.ts",
|
||||
@ -111,7 +113,7 @@
|
||||
"@affine-tools/utils": "workspace:*",
|
||||
"@affine/server-native": "workspace:*",
|
||||
"@faker-js/faker": "^9.6.0",
|
||||
"@nestjs/testing": "^10.4.15",
|
||||
"@nestjs/testing": "patch:@nestjs/testing@npm%3A10.4.15#~/.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch",
|
||||
"@types/cookie-parser": "^1.4.8",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/graphql-upload": "^17.0.0",
|
||||
@ -134,39 +136,6 @@
|
||||
"sinon": "^19.0.2",
|
||||
"supertest": "^7.0.0"
|
||||
},
|
||||
"ava": {
|
||||
"timeout": "1m",
|
||||
"extensions": {
|
||||
"ts": "module"
|
||||
},
|
||||
"workerThreads": false,
|
||||
"nodeArguments": [
|
||||
"--trace-sigint"
|
||||
],
|
||||
"watchMode": {
|
||||
"ignoreChanges": [
|
||||
"static/**",
|
||||
"**/*.gen.*"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"**/__tests__/**/*.spec.ts",
|
||||
"**/__tests__/**/*.e2e.ts"
|
||||
],
|
||||
"require": [
|
||||
"./src/prelude.ts"
|
||||
],
|
||||
"environmentVariables": {
|
||||
"NODE_ENV": "test",
|
||||
"MAILER_HOST": "0.0.0.0",
|
||||
"MAILER_PORT": "1025",
|
||||
"MAILER_USER": "noreply@toeverything.info",
|
||||
"MAILER_PASSWORD": "affine",
|
||||
"MAILER_SENDER": "noreply@toeverything.info",
|
||||
"FEATURES_EARLY_ACCESS_PREVIEW": "false",
|
||||
"DEPLOYMENT_TYPE": "affine"
|
||||
}
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"exec": "node",
|
||||
"ignore": [
|
||||
@ -185,7 +154,7 @@
|
||||
},
|
||||
"c8": {
|
||||
"reporter": [
|
||||
"text",
|
||||
"text-summary",
|
||||
"lcov"
|
||||
],
|
||||
"report-dir": ".coverage",
|
||||
|
49
packages/backend/server/src/__tests__/create-module.ts
Normal file
49
packages/backend/server/src/__tests__/create-module.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { ModuleMetadata } from '@nestjs/common';
|
||||
import {
|
||||
Test,
|
||||
TestingModule as NestjsTestingModule,
|
||||
TestingModuleBuilder,
|
||||
} from '@nestjs/testing';
|
||||
|
||||
import { FunctionalityModules } from '../app.module';
|
||||
import { AFFiNELogger } from '../base';
|
||||
import { TEST_LOG_LEVEL } from './utils';
|
||||
|
||||
interface TestingModuleMetadata extends ModuleMetadata {
|
||||
tapModule?(m: TestingModuleBuilder): void;
|
||||
}
|
||||
|
||||
export interface TestingModule extends NestjsTestingModule {
|
||||
[Symbol.asyncDispose](): Promise<void>;
|
||||
}
|
||||
|
||||
export async function createModule(
|
||||
metadata: TestingModuleMetadata
|
||||
): Promise<TestingModule> {
|
||||
const { tapModule, ...meta } = metadata;
|
||||
|
||||
const builder = Test.createTestingModule({
|
||||
...meta,
|
||||
imports: [...FunctionalityModules, ...(meta.imports ?? [])],
|
||||
});
|
||||
|
||||
// when custom override happens
|
||||
if (tapModule) {
|
||||
tapModule(builder);
|
||||
}
|
||||
|
||||
const module = (await builder.compile()) as TestingModule;
|
||||
|
||||
const logger = new AFFiNELogger();
|
||||
// we got a lot smoking tests try to break nestjs
|
||||
// can't tolerate the noisy logs
|
||||
logger.setLogLevels([TEST_LOG_LEVEL]);
|
||||
module.useLogger(logger);
|
||||
|
||||
await module.init();
|
||||
module[Symbol.asyncDispose] = async () => {
|
||||
await module.close();
|
||||
};
|
||||
|
||||
return module;
|
||||
}
|
5
packages/backend/server/src/__tests__/e2e/app.spec.ts
Normal file
5
packages/backend/server/src/__tests__/e2e/app.spec.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { app, e2e } from './test';
|
||||
|
||||
e2e('should create test app correctly', async t => {
|
||||
t.truthy(app);
|
||||
});
|
79
packages/backend/server/src/__tests__/e2e/create-app.ts
Normal file
79
packages/backend/server/src/__tests__/e2e/create-app.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { NestApplication } from '@nestjs/core';
|
||||
import { Test, TestingModuleBuilder } from '@nestjs/testing';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
|
||||
import {
|
||||
AFFiNELogger,
|
||||
CacheInterceptor,
|
||||
CloudThrottlerGuard,
|
||||
GlobalExceptionFilter,
|
||||
OneMB,
|
||||
} from '../../base';
|
||||
import { SocketIoAdapter } from '../../base/websocket';
|
||||
import { AuthGuard } from '../../core/auth';
|
||||
import { TEST_LOG_LEVEL } from '../utils';
|
||||
|
||||
interface TestingAppMetadata {
|
||||
tapModule?(m: TestingModuleBuilder): void;
|
||||
tapApp?(app: INestApplication): void;
|
||||
}
|
||||
|
||||
export class TestingApp extends NestApplication {
|
||||
async [Symbol.asyncDispose]() {
|
||||
await this.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function createApp(
|
||||
metadata: TestingAppMetadata = {}
|
||||
): Promise<TestingApp> {
|
||||
const { buildAppModule } = await import('../../app.module');
|
||||
const { tapModule, tapApp } = metadata;
|
||||
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [buildAppModule()],
|
||||
});
|
||||
|
||||
// when custom override happens
|
||||
if (tapModule) {
|
||||
tapModule(builder);
|
||||
}
|
||||
|
||||
const module = await builder.compile();
|
||||
|
||||
module.useCustomApplicationConstructor(TestingApp);
|
||||
|
||||
const app = module.createNestApplication<TestingApp>({
|
||||
cors: true,
|
||||
bodyParser: true,
|
||||
rawBody: true,
|
||||
});
|
||||
|
||||
const logger = new AFFiNELogger();
|
||||
logger.setLogLevels([TEST_LOG_LEVEL]);
|
||||
app.useLogger(logger);
|
||||
app.use(cookieParser());
|
||||
app.useBodyParser('raw', { limit: 1 * OneMB });
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * OneMB,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
|
||||
app.useGlobalGuards(app.get(AuthGuard), app.get(CloudThrottlerGuard));
|
||||
app.useGlobalInterceptors(app.get(CacheInterceptor));
|
||||
app.useGlobalFilters(new GlobalExceptionFilter(app.getHttpAdapter()));
|
||||
|
||||
const adapter = new SocketIoAdapter(app);
|
||||
app.useWebSocketAdapter(adapter);
|
||||
app.enableShutdownHooks();
|
||||
|
||||
if (tapApp) {
|
||||
tapApp(app);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
4
packages/backend/server/src/__tests__/e2e/prelude.ts
Normal file
4
packages/backend/server/src/__tests__/e2e/prelude.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { createApp } from './create-app';
|
||||
|
||||
// @ts-expect-error testing
|
||||
globalThis.app = await createApp();
|
9
packages/backend/server/src/__tests__/e2e/test.ts
Normal file
9
packages/backend/server/src/__tests__/e2e/test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import test, { registerCompletionHandler } from 'ava';
|
||||
|
||||
export const e2e = test;
|
||||
// @ts-expect-error created in prelude.ts
|
||||
export const app = globalThis.app;
|
||||
|
||||
registerCompletionHandler(() => {
|
||||
app.close();
|
||||
});
|
@ -13,6 +13,9 @@ import { AFFiNELogger, Runtime } from '../../base';
|
||||
import { GqlModule } from '../../base/graphql';
|
||||
import { AuthGuard, AuthModule } from '../../core/auth';
|
||||
import { ModelsModule } from '../../models';
|
||||
// for jsdoc inference
|
||||
// oxlint-disable-next-line no-unused-vars
|
||||
import type { createModule } from '../create-module';
|
||||
import { createFactory } from '../mocks';
|
||||
import { initTestingDB, TEST_LOG_LEVEL } from './utils';
|
||||
|
||||
@ -48,6 +51,9 @@ class MockResolver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link createModule} instead
|
||||
*/
|
||||
export async function createTestingModule(
|
||||
moduleDef: TestingModuleMeatdata = {},
|
||||
autoInitialize = true
|
||||
|
23
yarn.lock
23
yarn.lock
@ -861,7 +861,7 @@ __metadata:
|
||||
"@nestjs/platform-express": "npm:^10.4.15"
|
||||
"@nestjs/platform-socket.io": "npm:^10.4.15"
|
||||
"@nestjs/schedule": "npm:^4.1.2"
|
||||
"@nestjs/testing": "npm:^10.4.15"
|
||||
"@nestjs/testing": "patch:@nestjs/testing@npm%3A10.4.15#~/.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch"
|
||||
"@nestjs/throttler": "npm:6.4.0"
|
||||
"@nestjs/websockets": "npm:^10.4.15"
|
||||
"@node-rs/argon2": "npm:^2.0.2"
|
||||
@ -8194,7 +8194,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nestjs/testing@npm:^10.4.15":
|
||||
"@nestjs/testing@npm:10.4.15":
|
||||
version: 10.4.15
|
||||
resolution: "@nestjs/testing@npm:10.4.15"
|
||||
dependencies:
|
||||
@ -8213,6 +8213,25 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nestjs/testing@patch:@nestjs/testing@npm%3A10.4.15#~/.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch":
|
||||
version: 10.4.15
|
||||
resolution: "@nestjs/testing@patch:@nestjs/testing@npm%3A10.4.15#~/.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch::version=10.4.15&hash=9a94fb"
|
||||
dependencies:
|
||||
tslib: "npm:2.8.1"
|
||||
peerDependencies:
|
||||
"@nestjs/common": ^10.0.0
|
||||
"@nestjs/core": ^10.0.0
|
||||
"@nestjs/microservices": ^10.0.0
|
||||
"@nestjs/platform-express": ^10.0.0
|
||||
peerDependenciesMeta:
|
||||
"@nestjs/microservices":
|
||||
optional: true
|
||||
"@nestjs/platform-express":
|
||||
optional: true
|
||||
checksum: 10/0eb2fcfda4515c66b31820f1cf426f0bd4ba38bbf21fbbac35886943787bbb8f59edb0f4301723913de34831e19237c4d2878a5f3a60c26e2d0cc55dd2e870de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nestjs/throttler@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@nestjs/throttler@npm:6.4.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user