Skip to content

Commit

Permalink
Merge pull request #27 from yurenju/feat/e2e-server
Browse files Browse the repository at this point in the history
Feat/e2e server
  • Loading branch information
yurenju authored Sep 21, 2023
2 parents 9c619bf + d43148a commit 8b21aae
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 18 deletions.
116 changes: 116 additions & 0 deletions apps/server/src/ethereum/ethereum.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { SiweMessage } from 'siwe';
import { setupMongoDb, setupTestApplication } from '../../tests/helper';
import { getRandomHexString } from '../utils';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getDomain(server: any): string {
server.listen();
const address = server.address();
return `127.0.0.1:${address.port}`;
}

function generateSiweMessage(domain: string, address: string, nonce: string) {
const rawMessage = new SiweMessage({
domain,
address,
statement: 'Sign in with Ethereum to the app.',
uri: `http://${domain}`,
version: '1',
chainId: 1,
nonce,
});
const message = rawMessage.prepareMessage() as `0x${string}`;

return message;
}

describe('EthereumModule', () => {
let app: INestApplication;
let mongod: MongoMemoryServer;

beforeEach(async () => {
mongod = await setupMongoDb();
app = await setupTestApplication(mongod.getUri());
});

afterEach(async () => {
await mongod.stop();
await app.close();
});

it('should login with an ethereum account', async () => {
const server = app.getHttpServer();
const domain = getDomain(server);

const res = await request(server)
.post('/api/auth/national/register')
.send({ username: 'username', password: 'password' });

const { id, token } = res.body;

const challengeRes = await request(server)
.post('/api/auth/ethereum/challenge')
.set('Authorization', `Bearer ${token}`)
.expect(201);

const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
const message = generateSiweMessage(
domain,
account.address,
challengeRes.body.value
);
const signature = await account.signMessage({ message });
const payload = {
id,
account: account.address,
message,
signature,
};

await request(server)
.post('/api/auth/ethereum/login')
.send(payload)
.set('Authorization', `Bearer ${token}`)
.expect(201)
.expect((res) => {
expect(res.body).toEqual({ address: account.address });
});
});

it('should login failed with incorrect nonce', async () => {
const server = app.getHttpServer();
const domain = getDomain(server);

const res = await request(server)
.post('/api/auth/national/register')
.send({ username: 'username', password: 'password' });

const { id, token } = res.body;

const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
const message = generateSiweMessage(
domain,
account.address,
getRandomHexString()
);
const signature = await account.signMessage({ message });
const payload = {
id,
account: account.address,
message,
signature,
};

request(server)
.post('/api/auth/ethereum/login')
.send(payload)
.set('Authorization', `Bearer ${token}`)
.expect(401);
});
});
7 changes: 6 additions & 1 deletion apps/server/src/ethereum/nonce.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ export class NonceService {
.findOneAndDelete({ value: nonceValue })
.exec();

// this callback hander is for compatible of passport-siwe
if (!nonce) {
throw new NotFoundException('Nonce not found');
const error = new NotFoundException('Nonce not found');
if (cb) {
cb(error, false);
}
throw error;
}

if (cb) {
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/national/national.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { UsersService } from '../user/user.service';
import { NationalService } from './national.service';
import { RegisterNationalDto } from './register-national.dto';
import { LocalAuthGuard } from './guards/local-auth.guard';
import { JwtAuthGuard } from './guards/jwt-auth.guard';

@Controller('auth/national')
export class NationalController {
Expand All @@ -31,6 +32,7 @@ export class NationalController {
return this.nationalService.login(user);
}

@UseGuards(JwtAuthGuard)
@Get('check')
check(@Request() req) {
const { user } = req;
Expand Down
61 changes: 44 additions & 17 deletions apps/server/src/national/national.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersModule } from '../user/user.module';
import { AuthModule } from '../auth/auth.module';
import { setupMongoDb, setupTestApplication } from '../../tests/helper';

describe('NationalModule', () => {
let app: INestApplication;
let mongod: MongoMemoryServer;

beforeAll(async () => {
mongod = await MongoMemoryServer.create();
const uri = mongod.getUri();

const testModule: TestingModule = await Test.createTestingModule({
imports: [MongooseModule.forRoot(uri), UsersModule, AuthModule],
}).compile();

app = testModule.createNestApplication();
await app.init();
beforeEach(async () => {
mongod = await setupMongoDb();
app = await setupTestApplication(mongod.getUri());
});

afterAll(async () => {
afterEach(async () => {
await mongod.stop();
await app.close();
});

it('should register a new user', async () => {
return request(app.getHttpServer())
.post('/auth/national/register')
const server = app.getHttpServer();
const res = await request(server)
.post('/api/auth/national/register')
.send({ username: 'username', password: 'password' })
.expect(201);

return request(server)
.get(`/api/users/${res.body.id}`)
.set('Authorization', `Bearer ${res.body.token}`)
.expect(200);
});

it('should login an existing user', async () => {
const server = app.getHttpServer();

await request(server)
.post('/api/auth/national/register')
.send({ username: 'username', password: 'password' });

await request(server)
.post('/api/auth/national/login')
.send({ username: 'username', password: 'password' })
.expect(201)
.expect((res) => {
expect(res.body).toHaveProperty('token');
});
});

it('should check an existing user', async () => {
const server = app.getHttpServer();
const res = await request(server)
.post('/api/auth/national/register')
.send({ username: 'username', password: 'password' });

await request(server)
.get('/api/auth/national/check')
.set('Authorization', `Bearer ${res.body.token}`)
.expect(200)
.expect((res) => {
expect(res.body).toHaveProperty('user');
});
});
});
23 changes: 23 additions & 0 deletions apps/server/tests/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { INestApplication } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { UsersModule } from '../src/user/user.module';
import { AuthModule } from '../src/auth/auth.module';

export function setupMongoDb(): Promise<MongoMemoryServer> {
return MongoMemoryServer.create();
}

export async function setupTestApplication(
mongoUri: string
): Promise<INestApplication> {
const testModule: TestingModule = await Test.createTestingModule({
imports: [MongooseModule.forRoot(mongoUri), UsersModule, AuthModule],
}).compile();

const app = testModule.createNestApplication();
app.setGlobalPrefix('api');
await app.init();
return app;
}
1 change: 1 addition & 0 deletions apps/server/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"include": [
"jest.config.ts",
"tests/**.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.e2e-spec.ts",
Expand Down

0 comments on commit 8b21aae

Please sign in to comment.