Skip to content

Commit 81221fd

Browse files
authored
Merge pull request #129 from import-ai/fix/e2e_test
Fix/e2e test
2 parents 2618e3c + 5dbc586 commit 81221fd

21 files changed

+370
-328
lines changed

.env.example

Lines changed: 0 additions & 32 deletions
This file was deleted.

.github/workflows/ci.yaml

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Registry push
1+
name: CI
22

33
on:
44
push:
@@ -15,10 +15,10 @@ concurrency:
1515
cancel-in-progress: true
1616

1717
jobs:
18-
runtime:
18+
check-dependencies:
1919
runs-on: ubuntu-latest
2020
outputs:
21-
runtime_built: ${{ steps.set_output.outputs.runtime_built }}
21+
changed: ${{ steps.dep-check.outputs.changed }}
2222
steps:
2323
- name: Checkout
2424
uses: actions/checkout@v4
@@ -27,16 +27,25 @@ jobs:
2727
run: git fetch origin main
2828

2929
- name: Check if dependencies changed
30-
id: depcheck
30+
id: dep-check
3131
run: |
3232
if git diff --name-only origin/main | grep -E 'package.json|pnpm-lock.yaml|pnpm-workspace.yaml|runtime.Dockerfile'; then
3333
echo "changed=true" >> $GITHUB_OUTPUT
3434
else
3535
echo "changed=false" >> $GITHUB_OUTPUT
3636
fi
3737
38+
runtime:
39+
runs-on: ubuntu-latest
40+
needs: [check-dependencies]
41+
if: needs.check-dependencies.outputs.changed == 'true'
42+
outputs:
43+
runtime_built: 'true'
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v4
47+
3848
- name: Docker meta (runtime)
39-
if: steps.depcheck.outputs.changed == 'true'
4049
id: meta_runtime
4150
uses: docker/metadata-action@v5
4251
with:
@@ -47,23 +56,19 @@ jobs:
4756
type=raw,value=latest
4857
4958
- name: Set up QEMU
50-
if: steps.depcheck.outputs.changed == 'true'
5159
uses: docker/setup-qemu-action@v3
5260

5361
- name: Set up Docker Buildx
54-
if: steps.depcheck.outputs.changed == 'true'
5562
uses: docker/setup-buildx-action@v3
5663

5764
- name: Login to ghcr.io Registry
58-
if: steps.depcheck.outputs.changed == 'true'
5965
uses: docker/login-action@v3
6066
with:
6167
registry: ghcr.io
6268
username: ${{ github.actor }}
6369
password: ${{ secrets.GITHUB_TOKEN }}
6470

6571
- name: Build and push runtime image
66-
if: steps.depcheck.outputs.changed == 'true'
6772
uses: docker/build-push-action@v6
6873
with:
6974
context: .
@@ -74,14 +79,70 @@ jobs:
7479
platforms: |
7580
linux/amd64
7681
linux/arm64
82+
cache-from: type=gha
83+
cache-to: type=gha,mode=max
7784

78-
- name: Set output
79-
id: set_output
80-
run: echo "runtime_built=${{ steps.depcheck.outputs.changed }}" >> $GITHUB_OUTPUT
85+
test:
86+
runs-on: ubuntu-latest
87+
needs: [check-dependencies, runtime]
88+
if: always() && !cancelled() && !failure()
89+
services:
90+
postgres:
91+
image: postgres:17.5
92+
env:
93+
POSTGRES_PASSWORD: omnibox
94+
POSTGRES_USER: omnibox
95+
POSTGRES_DB: omnibox
96+
ports:
97+
- 5432:5432
98+
options: >-
99+
--health-cmd pg_isready
100+
--health-interval 10s
101+
--health-timeout 5s
102+
--health-retries 5
103+
104+
minio:
105+
image: ghcr.io/import-ai/minio-server:RELEASE.2025-04-22T22-12-26Z
106+
env:
107+
MINIO_ROOT_USER: username
108+
MINIO_ROOT_PASSWORD: password
109+
ports:
110+
- 9000:9000
111+
- 9100:9001
112+
options: >-
113+
--health-cmd "curl -f http://localhost:9000/minio/health/live"
114+
--health-interval 30s
115+
--health-timeout 20s
116+
--health-retries 3
117+
118+
container:
119+
image: ghcr.io/import-ai/omnibox-backend-runtime:latest
120+
credentials:
121+
username: ${{ github.actor }}
122+
password: ${{ secrets.GITHUB_TOKEN }}
123+
124+
steps:
125+
- name: Checkout
126+
uses: actions/checkout@v4
127+
128+
- name: Install dependencies
129+
run: pnpm install --frozen-lockfile
130+
131+
- name: Prepare .env
132+
run: cp example.env .env
133+
134+
- name: Run unit tests
135+
run: pnpm run test
136+
137+
- name: Run e2e tests
138+
env:
139+
OBB_POSTGRES_URL: postgres://omnibox:omnibox@postgres:5432/omnibox
140+
OBB_MINIO_URL: http://username:password@minio:9000/omnibox
141+
run: pnpm run test:e2e
81142

82143
app:
83144
runs-on: ubuntu-latest
84-
needs: [ runtime ]
145+
needs: [test]
85146
steps:
86147
- name: Checkout
87148
uses: actions/checkout@v4
@@ -123,17 +184,24 @@ jobs:
123184
platforms: |
124185
linux/amd64
125186
linux/arm64
187+
cache-from: type=gha
188+
cache-to: type=gha,mode=max
126189

127190
webhook:
128191
runs-on: ubuntu-latest
129-
needs: [ app ]
192+
needs: [app]
130193
steps:
131194
- name: DEV Webhook
132-
if: github.event_name == 'push'
195+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
133196
run: |
134197
curl "${{ secrets.DEV_WEBHOOK_URL }}/dev" -H "Authorization: Bearer ${{ secrets.DEV_WEBHOOK_API_KEY }}"
135198
136199
- name: PR Webhook
137200
if: github.event_name == 'pull_request'
138201
run: |
139202
curl "${{ secrets.DEV_WEBHOOK_URL }}/pr?module=backend&pr=pr-${{ github.event.number }}" -H "Authorization: Bearer ${{ secrets.DEV_WEBHOOK_API_KEY }}"
203+
204+
- name: PROD Webhook
205+
if: false && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
206+
run: |
207+
curl "${{ secrets.PROD_WEBHOOK_URL }}/prod?tag=${{ github.ref }}" -H "Authorization: Bearer ${{ secrets.PROD_WEBHOOK_API_KEY }}"

base.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ services:
1010
OBB_DB_HOST: postgres
1111
OBB_MINIO_ENDPOINT: http://username:password@minio:9000
1212
OBB_WIZARD_BASE_URL: http://wizard:8000
13-
OBB_MEILI_HOST: http://meilisearch:7700
1413
depends_on:
1514
postgres:
1615
condition: service_healthy

example.env

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
OBB_PORT=8000
2+
3+
OBB_WIZARD_BASE_URL=http://wizard:8000
4+
OBB_LOG_LEVELS=error,warn,log
5+
6+
OBB_POSTGRES_URL=postgres://omnibox:omnibox@postgres:5432/omnibox
7+
OBB_DB_LOGGING=false
8+
OBB_DB_SYNC=false
9+
10+
OBB_MINIO_URL=http://username:password@minio:9000/omnibox
11+
12+
OBB_JWT_SECRET=your-secret-key
13+
OBB_JWT_EXPIRE=2678400s
14+
15+
OBB_MAIL_TRANSPORT=smtps://your-email@example.com:your-email-password@smtp.example.com:465
16+
OBB_MAIL_FROM="No Reply <no-reply@example.com>"
17+
18+
OBB_WECHAT_APP_ID=
19+
OBB_WECHAT_APP_SECRET=
20+
OBB_OPEN_WECHAT_APP_ID=
21+
OBB_OPEN_WECHAT_APP_SECRET=
22+
OBB_WECHAT_REDIRECT_URI=

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = {
55
transform: {
66
'^.+\\.ts$': 'ts-jest',
77
},
8-
setupFilesAfterEnv: ['<rootDir>/../jest.setup.js'],
8+
setupFilesAfterEnv: ['<rootDir>/../jest.setup.ts'],
99
collectCoverageFrom: ['**/*.(t|j)s'],
1010
coverageDirectory: '../coverage',
1111
testEnvironment: 'node',

jest.setup.js

Lines changed: 0 additions & 3 deletions
This file was deleted.

jest.setup.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function randomChoice(choices: string) {
2+
return choices[Math.floor(Math.random() * choices.length)];
3+
}
4+
5+
function customAlphabet(alphabet: string, size: number) {
6+
return () => {
7+
let result = '';
8+
for (let i = 0; i < size; i++) {
9+
result += randomChoice(alphabet);
10+
}
11+
return result;
12+
};
13+
}
14+
15+
jest.mock('nanoid', () => ({
16+
customAlphabet,
17+
}));

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44
"private": true,
55
"scripts": {
66
"build": "nest build",
7-
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
7+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
88
"start": "nest start",
99
"start:dev": "nest start --watch",
1010
"start:debug": "nest start --debug --watch",
1111
"start:prod": "node dist/main",
1212
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
1313
"test": "jest",
1414
"test:watch": "jest --watch",
15-
"test:cov": "jest --coverage",
1615
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
17-
"test:e2e": "jest --config test/jest-e2e.json"
16+
"test:e2e": "jest --config test/jest-e2e.json --runInBand"
1817
},
1918
"devDependencies": {
2019
"@eslint/eslintrc": "^3.3.1",

src/app/app.module.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { AttachmentsModule } from 'omniboxd/attachments/attachments.module';
3737

3838
@Module({})
3939
export class AppModule implements NestModule {
40-
static forRoot(extraMigrations: Function[]): DynamicModule {
40+
static forRoot(extraMigrations: Array<() => void>): DynamicModule {
4141
return {
4242
module: AppModule,
4343
controllers: [AppController],
@@ -87,11 +87,7 @@ export class AppModule implements NestModule {
8787
inject: [ConfigService],
8888
useFactory: (config: ConfigService) => ({
8989
type: 'postgres',
90-
host: config.get('OBB_DB_HOST', 'postgres'),
91-
port: config.get('OBB_DB_PORT', 5432),
92-
database: config.get('OBB_DB_DATABASE', 'omnibox'),
93-
username: config.get('OBB_DB_USERNAME', 'omnibox'),
94-
password: config.get('OBB_DB_PASSWORD', 'omnibox'),
90+
url: config.get('OBB_POSTGRES_URL'),
9591
logging: config.get('OBB_DB_LOGGING') === 'true',
9692
synchronize: config.get('OBB_DB_SYNC') === 'true',
9793
autoLoadEntities: true,

src/auth/auth.e2e-spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { TestClient } from 'test/test-client';
2+
3+
describe('UserController (e2e)', () => {
4+
let client: TestClient;
5+
6+
beforeAll(async () => {
7+
client = await TestClient.create();
8+
});
9+
10+
afterAll(async () => {
11+
await client.close();
12+
});
13+
14+
it('should login with valid credentials', async () => {
15+
const response = await client
16+
.post('/api/v1/login')
17+
.send({
18+
email: client.user.email,
19+
password: client.user.password,
20+
})
21+
.expect(200);
22+
23+
expect(response.body).toHaveProperty('id');
24+
expect(response.body).toHaveProperty('access_token');
25+
});
26+
27+
it('should access protected route with valid token', async () => {
28+
return await client
29+
.get(`/api/v1/user/${client.user.id}`)
30+
.expect(200)
31+
.expect((res) => {
32+
expect(res.body.username).toBe(client.user.username);
33+
});
34+
});
35+
36+
it('should reject invalid token', async () => {
37+
return await client
38+
.get(`/api/v1/user/${client.user.id}`)
39+
.set('Authorization', `Bearer fake-token`)
40+
.expect(401)
41+
.expect((res) => {
42+
expect(res.body).toHaveProperty('message');
43+
});
44+
});
45+
});

0 commit comments

Comments
 (0)