Skip to content

Commit

Permalink
feat: Sending error in acknowledgement and e2e socket test
Browse files Browse the repository at this point in the history
  • Loading branch information
theblockstalk committed Sep 7, 2023
1 parent d6a5fbe commit 0f900be
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 13 deletions.
42 changes: 31 additions & 11 deletions src/communication/communication.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ import {
UsePipes,
} from '@nestjs/common';
import { TransformVcPipe } from './transform-vc/transform-vc.pipe';
import { MessageDto, MessageRto } from './dto/message.dto';
import { MessageDto } from './dto/message.dto';
import { Client } from './dto/client.dto';
import { WsExceptionFilter } from './ws-exception/ws-exception.filter';
import { AuthenticationMessage } from '@tonomy/tonomy-id-sdk';
import { CommunicationGuard } from './communication.guard';

export type WebsocketReturnType = {
status: HttpStatus;
details?: any;

Check warning on line 27 in src/communication/communication.gateway.ts

View workflow job for this annotation

GitHub Actions / tests

Unexpected any. Specify a different type
error?: any;

Check warning on line 28 in src/communication/communication.gateway.ts

View workflow job for this annotation

GitHub Actions / tests

Unexpected any. Specify a different type
};

@UseFilters(WsExceptionFilter)
@UsePipes(new TransformVcPipe())
@WebSocketGateway({
Expand All @@ -44,18 +50,25 @@ export class CommunicationGateway implements OnGatewayDisconnect {
* @returns void
*/
@SubscribeMessage('login')
connectUser(
async connectUser(
@MessageBody() message: MessageDto,
@ConnectedSocket() client: Client,
) {
if (message.getType() !== AuthenticationMessage.getType()) {
throw new HttpException(
"Message type must be 'AuthenticationMessage'",
HttpStatus.BAD_REQUEST,
);
}
try {
if (message.getType() !== AuthenticationMessage.getType()) {
throw new HttpException(
"Message type must be 'AuthenticationMessage'",
HttpStatus.BAD_REQUEST,
);
}

return this.usersService.login(message.getSender(), client);
return {
status: HttpStatus.OK,
details: await this.usersService.login(message.getSender(), client),
};
} catch (e) {
return this.usersService.handleError(e);
}
}

/**
Expand All @@ -66,11 +79,18 @@ export class CommunicationGateway implements OnGatewayDisconnect {
*/
@SubscribeMessage('message')
@UseGuards(CommunicationGuard)
relayMessage(
async relayMessage(
@MessageBody() message: MessageDto,
@ConnectedSocket() client: Client,
) {
return this.usersService.sendMessage(client, message);
try {
return {
status: HttpStatus.OK,
details: await this.usersService.sendMessage(client, message),
};
} catch (e) {
return this.usersService.handleError(e);
}
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/communication/communication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Socket } from 'socket.io';
import { Client } from './dto/client.dto';
import { MessageDto } from './dto/message.dto';
import settings from '../settings';
import { WebsocketReturnType } from './communication.gateway';

@Injectable()
export class CommunicationService {
Expand Down Expand Up @@ -67,4 +68,20 @@ export class CommunicationService {

return true;
}

handleError(e): WebsocketReturnType {
this.logger.error(e);

if (e instanceof HttpException) {
return {
status: e.getStatus(),
error: e.getResponse(),
};
}

return {
status: HttpStatus.INTERNAL_SERVER_ERROR,
error: e.message,
};
}
}
4 changes: 2 additions & 2 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EosioUtil } from '@tonomy/tonomy-id-sdk';

const env = process.env.NODE_ENV || 'development';

console.log(`NODE_ENV=${env}`);
if (env !== 'test') console.log(`NODE_ENV=${env}`);

Check warning on line 8 in src/settings.ts

View workflow job for this annotation

GitHub Actions / tests

Unexpected console statement

type ConfigType = {
blockchainUrl: string;
Expand Down Expand Up @@ -74,7 +74,7 @@ if (process.env.BLOCKCHAIN_URL) {
settings.config.blockchainUrl = process.env.BLOCKCHAIN_URL;
}

console.log('settings', settings);
if (env !== 'test') console.log('settings', settings);

Check warning on line 77 in src/settings.ts

View workflow job for this annotation

GitHub Actions / tests

Unexpected console statement

settings.secrets = {
createAccountPrivateKey: EosioUtil.defaultAntelopePrivateKey.toString(),
Expand Down
62 changes: 62 additions & 0 deletions test/communication.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { AppModule } from '../src/app.module';
import { connectSocket, emitMessage } from './ws-client.helper';
import { Socket } from 'socket.io-client';
import {
AuthenticationMessage,
ES256KSigner,
generateRandomKeyPair,
setSettings,
} from '@tonomy/tonomy-id-sdk';
// @ts-expect-error - cannot find module or its corresponding type declarations
import { createJWK, toDid } from '@tonomy/tonomy-id-sdk/util';

setSettings({
blockchainUrl: 'http://localhost:8888',
});

describe('CommunicationGateway (e2e)', () => {
let app: INestApplication;
let socket: Socket;

beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();
app.listen(5000);

socket = await connectSocket();
});

afterEach(async () => {
await socket.disconnect();
await socket.close();
await app.close();
});

describe('login event', () => {
it('succeeds for did:jwk message', async () => {
const { privateKey, publicKey } = generateRandomKeyPair();
const signer = ES256KSigner(privateKey.data.array, true);
const jwk = await createJWK(publicKey);
const did = toDid(jwk);

const issuer = {
did,
signer,
alg: 'ES256K-R',
};
const message = await AuthenticationMessage.signMessageWithoutRecipient(
{ data: 'test' },
issuer,
);

const response = await emitMessage(socket, 'login', { message });

expect(response).toBeTruthy();
});
});
});
87 changes: 87 additions & 0 deletions test/ws-client.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { io, Socket } from 'socket.io-client';
import { HttpStatus } from '@nestjs/common';
import { WebsocketReturnType } from 'src/communication/communication.gateway';

export const SOCKET_TIMEOUT = 1000;

export async function connectSocket(): Promise<Socket> {
const socket = io('http://localhost:5000', {
autoConnect: false,
transports: ['websocket'],
});

socket.connect();
let resolved = false;

await new Promise<void>((resolve, reject) => {
socket.on('connect', () => {
resolved = true;
resolve();
});

setTimeout(() => {
if (resolved) return;
reject(new Error('Websocket connection event timed out'));
}, SOCKET_TIMEOUT);
});
return socket;
}

export async function emitMessage(
socket: Socket,
event: string,
body: any,
): Promise<any> {
const res: WebsocketReturnType = await new Promise((resolve, reject) => {
socket
.timeout(SOCKET_TIMEOUT)
.emit(event, body, (error: any, response: any) => {
if (error) {
reject(error);
return;
}

resolve(response);
});
});

if (res.status !== HttpStatus.OK) {
const err = new Error(res.error);

// @ts-expect-error - code is not a property of Error
err.code = res.status;
throw err;
} else {
return res.details;
}
}

// export async function emitMessage(
// socket: Socket,
// event: string,
// body: any,
// ): Promise<any> {
// return await new Promise((resolve, reject) => {
// let resolved = false;

// const res = socket.emit(event, body, (response: any) => {
// console.log('response', response);

// if (response.error) {
// reject(response.error);
// return;
// }

// resolved = true;
// resolve(response);
// return;
// });

// // console.log('res', res);

// setTimeout(() => {
// if (resolved) return;
// reject(new Error('Websocket event timed out'));
// }, 1000);
// });
// }

0 comments on commit 0f900be

Please sign in to comment.