Skip to content

Commit 62ecbc2

Browse files
authored
Merge pull request #1853 from humanprotocol/develop
Release Sprint 8 - W1
2 parents dd6c3c5 + 6e633f5 commit 62ecbc2

File tree

18 files changed

+736
-131
lines changed

18 files changed

+736
-131
lines changed

packages/apps/job-launcher/server/docker-compose.yml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
version: '3.8'
22
services:
33
postgres:
4+
container_name: postgres
45
image: postgres:latest
56
restart: always
67
logging:
@@ -11,11 +12,10 @@ services:
1112
- 5432:5432
1213
# volumes:
1314
# - ./db:/var/lib/postgresql/data
14-
env_file:
15-
- path: .env
16-
required: true # default
1715
environment:
1816
POSTGRES_DB: ${POSTGRES_DATABASE}
17+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
18+
POSTGRES_USER: ${POSTGRES_USER}
1919
minio:
2020
container_name: minio
2121
image: minio/minio:RELEASE.2022-05-26T05-48-41Z
@@ -30,9 +30,6 @@ services:
3030
interval: 5s
3131
timeout: 5s
3232
retries: 3
33-
env_file:
34-
- path: .env
35-
required: true # default
3633
minio-mc:
3734
container_name: minio-mc
3835
image: minio/mc
@@ -45,9 +42,6 @@ services:
4542
/usr/bin/mc mb myminio/manifests;
4643
/usr/bin/mc anonymous set public myminio/manifests;
4744
"
48-
env_file:
49-
- path: .env
50-
required: true # default
5145
# job-launcher:
5246
# container_name: job-launcher
5347
# restart: unless-stopped

packages/apps/job-launcher/server/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test:watch": "jest --watch",
2626
"test:cov": "jest --coverage",
2727
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
28-
"test:e2e": "jest --config ./test/jest-e2e.json"
28+
"test:e2e": "export POSTGRES_DATABASE=job-launcher POSTGRES_PASSWORD=qwerty POSTGRES_USER=default POSTGRES_HOST=0.0.0.0 NODE_ENV=test-e2e && docker compose up -d postgres && yarn migration:run && jest --config ./test/jest-e2e.json; testStatus=$?; docker rm -f postgres; exit $testStatus"
2929
},
3030
"dependencies": {
3131
"@human-protocol/sdk": "*",
@@ -58,7 +58,7 @@
5858
"pg": "8.11.3",
5959
"reflect-metadata": "^0.1.13",
6060
"rxjs": "^7.2.0",
61-
"stripe": "^14.20.0",
61+
"stripe": "^14.24.0",
6262
"typeorm": "^0.3.17",
6363
"typeorm-naming-strategies": "^4.1.0",
6464
"zxcvbn": "^4.4.2"

packages/apps/job-launcher/server/src/app.module.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Module } from '@nestjs/common';
22
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
33
import { ConfigModule } from '@nestjs/config';
4-
import { ScheduleModule } from '@nestjs/schedule';
54
import { AppController } from './app.controller';
65
import { DatabaseModule } from './database/database.module';
76
import { JwtAuthGuard } from './common/guards';
@@ -21,6 +20,7 @@ import { SnakeCaseInterceptor } from './common/interceptors/snake-case';
2120
import { DatabaseExceptionFilter } from './common/exceptions/database.filter';
2221
import { WebhookModule } from './modules/webhook/webhook.module';
2322
import { EnvConfigModule } from './common/config/config.module';
23+
import { E2E_TEST_ENV } from './common/constants';
2424

2525
@Module({
2626
providers: [
@@ -43,10 +43,15 @@ import { EnvConfigModule } from './common/config/config.module';
4343
],
4444
imports: [
4545
ConfigModule.forRoot({
46-
envFilePath: process.env.NODE_ENV
47-
? `.env.${process.env.NODE_ENV as string}`
48-
: '.env',
49-
validationSchema: envValidator,
46+
ignoreEnvFile: process.env.NODE_ENV === E2E_TEST_ENV,
47+
...(process.env.NODE_ENV !== E2E_TEST_ENV && {
48+
envFilePath: process.env.NODE_ENV
49+
? `.env.${process.env.NODE_ENV as string}`
50+
: '.env',
51+
}),
52+
...(process.env.NODE_ENV !== E2E_TEST_ENV && {
53+
validationSchema: envValidator,
54+
}),
5055
}),
5156
DatabaseModule,
5257
HealthModule,

packages/apps/job-launcher/server/src/common/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ export const HCAPTCHA_ORACLE_STAKE = 0.05;
6565
export const HCAPTCHA_NOT_PRESENTED_LABEL = 'Not presented';
6666

6767
export const RESEND_EMAIL_VERIFICATION_PATH = '/auth/resend-email-verification';
68+
69+
export const E2E_TEST_ENV = 'test-e2e';

packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class ResendEmailVerificationDto {
4646

4747
export class RestorePasswordDto extends ValidatePasswordDto {
4848
@ApiProperty()
49-
@IsString()
49+
@IsUUID()
5050
public token: string;
5151

5252
@ApiProperty({ name: 'h_captcha_token' })
@@ -56,7 +56,7 @@ export class RestorePasswordDto extends ValidatePasswordDto {
5656

5757
export class VerifyEmailDto {
5858
@ApiProperty()
59-
@IsString()
59+
@IsUUID()
6060
public token: string;
6161
}
6262

packages/apps/job-launcher/server/src/modules/sendgrid/sendgrid.service.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,20 @@ export class SendGridService {
1313

1414
private readonly defaultFromEmail: string;
1515
private readonly defaultFromName: string;
16-
private readonly apiKey: string;
1716

1817
constructor(
1918
private readonly mailService: MailService,
2019
private readonly sendgridConfigService: SendgridConfigService,
2120
) {
22-
const apiKey = this.sendgridConfigService.apiKey;
23-
24-
if (this.apiKey === SENDGRID_API_KEY_DISABLED) {
21+
if (this.sendgridConfigService.apiKey === SENDGRID_API_KEY_DISABLED) {
2522
return;
2623
}
2724

28-
if (!SENDGRID_API_KEY_REGEX.test(apiKey)) {
25+
if (!SENDGRID_API_KEY_REGEX.test(this.sendgridConfigService.apiKey)) {
2926
throw new Error(ErrorSendGrid.InvalidApiKey);
3027
}
3128

32-
this.mailService.setApiKey(apiKey);
29+
this.mailService.setApiKey(this.sendgridConfigService.apiKey);
3330

3431
this.defaultFromEmail = this.sendgridConfigService.fromEmail;
3532
this.defaultFromName = this.sendgridConfigService.fromName;
@@ -45,7 +42,7 @@ export class SendGridService {
4542
...emailData
4643
}: Partial<MailDataRequired>): Promise<void> {
4744
try {
48-
if (this.apiKey === SENDGRID_API_KEY_DISABLED) {
45+
if (this.sendgridConfigService.apiKey === SENDGRID_API_KEY_DISABLED) {
4946
this.logger.debug(personalizations);
5047
return;
5148
}

packages/apps/job-launcher/server/test/app.e2e-spec.ts renamed to packages/apps/job-launcher/server/test/e2e/app.e2e-spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { INestApplication } from '@nestjs/common';
3-
import * as request from 'supertest';
4-
import { AppModule } from './../src/app.module';
3+
import request from 'supertest';
4+
import { AppModule } from '../../src/app.module';
5+
import setupE2eEnvironment from './env-setup';
56

67
describe('AppController (e2e)', () => {
78
let app: INestApplication;
9+
beforeAll(async () => {
10+
setupE2eEnvironment();
11+
});
812

913
beforeEach(async () => {
1014
const moduleFixture: TestingModule = await Test.createTestingModule({
@@ -18,7 +22,7 @@ describe('AppController (e2e)', () => {
1822
it('/ (GET)', () => {
1923
return request(app.getHttpServer())
2024
.get('/')
21-
.expect(200)
22-
.expect('Hello World!');
25+
.expect(301)
26+
.expect('Moved Permanently. Redirecting to /swagger');
2327
});
2428
});
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import request from 'supertest';
2+
import * as crypto from 'crypto';
3+
import { INestApplication } from '@nestjs/common';
4+
import { Test, TestingModule } from '@nestjs/testing';
5+
import { AppModule } from '../../src/app.module';
6+
import { UserRepository } from '../../src/modules/user/user.repository';
7+
import { TokenRepository } from '../../src/modules/auth/token.repository';
8+
import { TokenType } from '../../src/modules/auth/token.entity';
9+
import { UserStatus } from '../../src/common/enums/user';
10+
import setupE2eEnvironment from './env-setup';
11+
12+
describe('AuthService E2E', () => {
13+
let app: INestApplication;
14+
let userRepository: UserRepository;
15+
let tokenRepository: TokenRepository;
16+
17+
beforeAll(async () => {
18+
setupE2eEnvironment();
19+
const moduleFixture: TestingModule = await Test.createTestingModule({
20+
imports: [AppModule],
21+
}).compile();
22+
23+
app = moduleFixture.createNestApplication();
24+
await app.init();
25+
26+
userRepository = moduleFixture.get<UserRepository>(UserRepository);
27+
tokenRepository = moduleFixture.get<TokenRepository>(TokenRepository);
28+
});
29+
30+
afterAll(async () => {
31+
await app.close();
32+
});
33+
34+
it('should test authentication workflow', async () => {
35+
const email = `${crypto.randomBytes(16).toString('hex')}@hmt.ai`;
36+
const password = 'Password1!';
37+
const hCaptchaToken = 'string';
38+
39+
// User Registration
40+
const signUpResponse = await request(app.getHttpServer())
41+
.post('/auth/signup')
42+
.send({
43+
email,
44+
password,
45+
h_captcha_token: hCaptchaToken,
46+
});
47+
expect(signUpResponse.status).toBe(201);
48+
49+
// Verify user registration
50+
const userEntity = await userRepository.findByEmail(email);
51+
expect(userEntity).toBeDefined();
52+
53+
// Email Verification
54+
expect(userEntity!.status).toBe(UserStatus.PENDING);
55+
56+
// Invalid verification
57+
const invalidVerifyTokenResponse = await request(app.getHttpServer())
58+
.post(`/auth/email-verification`)
59+
.send({ token: 123 });
60+
expect(invalidVerifyTokenResponse.status).toBe(400);
61+
62+
const tokenEntity = await tokenRepository.findOneByUserIdAndType(
63+
userEntity!.id,
64+
TokenType.EMAIL,
65+
);
66+
67+
// Valid verification
68+
const verifyTokenResponse = await request(app.getHttpServer())
69+
.post(`/auth/email-verification`)
70+
.send({ token: tokenEntity!.uuid });
71+
72+
expect(verifyTokenResponse.status).toBe(200);
73+
74+
const updatedUserEntity = await userRepository.findByEmail(email);
75+
expect(updatedUserEntity!.status).toBe(UserStatus.ACTIVE);
76+
77+
// User Authentication
78+
const signInResponse = await request(app.getHttpServer())
79+
.post('/auth/signin')
80+
.send({
81+
email,
82+
password,
83+
h_captcha_token: hCaptchaToken,
84+
});
85+
expect(signInResponse.status).toBe(200);
86+
87+
const refreshedUserEntity = await userRepository.findByEmail(email);
88+
const refreshTokenEntity = await tokenRepository.findOneByUserIdAndType(
89+
refreshedUserEntity!.id,
90+
TokenType.REFRESH,
91+
);
92+
expect(refreshTokenEntity).toBeDefined();
93+
94+
// Invalid Authentication
95+
const invalidSignInResponse = await request(app.getHttpServer())
96+
.post('/auth/signin')
97+
.send({
98+
email,
99+
password: 'Incorrect',
100+
h_captcha_token: hCaptchaToken,
101+
});
102+
expect(invalidSignInResponse.status).toBe(403);
103+
104+
// Refresh Token
105+
const refreshTokenResponse = await request(app.getHttpServer())
106+
.post('/auth/refresh')
107+
.send({ refresh_token: refreshTokenEntity!.uuid }); // Add appropriate data if needed
108+
109+
expect(refreshTokenResponse.status).toBe(200);
110+
expect(refreshTokenResponse.body).toHaveProperty('access_token');
111+
expect(refreshTokenResponse.body).toHaveProperty('refresh_token');
112+
113+
const accessToken = refreshTokenResponse.body.access_token;
114+
const refreshToken = refreshTokenResponse.body.refresh_token;
115+
116+
// Resend Email Verification
117+
const resendEmailVerificationResponse = await request(app.getHttpServer())
118+
.post('/auth/resend-email-verification')
119+
.set('Authorization', `Bearer ${accessToken}`)
120+
.send({ email });
121+
expect(resendEmailVerificationResponse.status).toBe(204);
122+
});
123+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
export default function setupE2eEnvironment() {
2+
process.env.NODE_ENV = 'test-e2e';
3+
process.env.S3_ACCESS_KEY = 'value';
4+
5+
process.env.POSTGRES_DATABASE = 'job-launcher';
6+
process.env.POSTGRES_PASSWORD = 'qwerty';
7+
process.env.POSTGRES_USER = 'default';
8+
process.env.POSTGRES_HOST = '0.0.0.0';
9+
10+
process.env.SESSION_SECRET = 'test';
11+
process.env.POSTGRES_HOST = '0.0.0.0';
12+
process.env.POSTGRES_USER = 'default';
13+
process.env.POSTGRES_PASSWORD = 'qwerty';
14+
process.env.POSTGRES_DATABASE = 'job-launcher';
15+
process.env.POSTGRES_PORT = '5432';
16+
process.env.POSTGRES_SSL = 'false';
17+
18+
process.env.WEB3_ENV = 'localhost';
19+
process.env.WEB3_PRIVATE_KEY =
20+
'0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d';
21+
process.env.GAS_PRICE_MULTIPLIER = '1';
22+
process.env.PGP_ENCRYPT = 'false';
23+
process.env.FORTUNE_EXCHANGE_ORACLE_ADDRESS =
24+
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC';
25+
process.env.FORTUNE_RECORDING_ORACLE_ADDRESS =
26+
'0x90F79bf6EB2c4f870365E785982E1f101E93b906';
27+
process.env.CVAT_EXCHANGE_ORACLE_ADDRESS =
28+
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC';
29+
process.env.CVAT_RECORDING_ORACLE_ADDRESS =
30+
'0x90F79bf6EB2c4f870365E785982E1f101E93b906';
31+
process.env.REPUTATION_ORACLE_ADDRESS =
32+
'0x90F79bf6EB2c4f870365E785982E1f101E93b906';
33+
process.env.HCAPTCHA_RECORDING_ORACLE_URI = 'a';
34+
process.env.HCAPTCHA_REPUTATION_ORACLE_URI = 'a';
35+
process.env.HCAPTCHA_ORACLE_ADDRESS = 'a';
36+
process.env.HCAPTCHA_SITE_KEY = 'a';
37+
38+
process.env.HASH_SECRET = 'a328af3fc1dad15342cc3d68936008fa';
39+
process.env.JWT_PRIVATE_KEY = `-----BEGIN EC PARAMETERS-----
40+
BggqhkjOPQMBBw==
41+
-----END EC PARAMETERS-----
42+
-----BEGIN EC PRIVATE KEY-----
43+
MHcCAQEEID2jVcOtjupW4yqNTz70nvmt1GSvqET5G7lpC0Gp31LFoAoGCCqGSM49
44+
AwEHoUQDQgAEUznVCoagfRCuMA3TfG51xWShNrMJt86lkzfAep9bfBxbaCBbUhJ1
45+
s9+9eeLMG/nUMAaGxWeOwJ92L/KvzN6RFw==
46+
-----END EC PRIVATE KEY-----`;
47+
48+
process.env.JWT_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
49+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUznVCoagfRCuMA3TfG51xWShNrMJ
50+
t86lkzfAep9bfBxbaCBbUhJ1s9+9eeLMG/nUMAaGxWeOwJ92L/KvzN6RFw==
51+
-----END PUBLIC KEY-----`;
52+
process.env.JWT_ACCESS_TOKEN_EXPIRES_IN = '600';
53+
process.env.JWT_REFRESH_TOKEN_EXPIRES_IN = '1d';
54+
55+
process.env.S3_ENDPOINT = 'localhost';
56+
process.env.S3_PORT = '9000';
57+
process.env.S3_ACCESS_KEY = 'access-key';
58+
process.env.S3_SECRET_KEY = 'secret-key';
59+
process.env.S3_BUCKET = 'bucket';
60+
process.env.S3_USE_SSL = 'false';
61+
62+
process.env.MINIO_ROOT_USER = 'access-key';
63+
process.env.MINIO_ROOT_PASSWORD = 'secrets-key';
64+
65+
process.env.STRIPE_SECRET_KEY =
66+
'sk_test_GvCQRGFcSwDzNE9wBSaH6MbBzk6yUntQp5GSGjDU7VzMpkJpVq32kYx52EBP9t4Gin5UQ9nYwprq7ZRNPTQG4gvdUuTwYEGy7rp';
67+
process.env.STRIPE_API_VERSION = '2022-11-15';
68+
process.env.STRIPE_APP_NAME = 'Staging Launcher Server';
69+
process.env.STRIPE_APP_VERSION = '1.0.0';
70+
process.env.STRIPE_APP_INFO_URL =
71+
'https://github.com/humanprotocol/human-protocol/tree/main/packages/apps/job-launcher/server';
72+
73+
process.env.SENDGRID_API_KEY = 'sendgrid-disabled';
74+
75+
process.env.CRON_SECRET = 'test';
76+
}

0 commit comments

Comments
 (0)