From 6a90db1d19a96f8bf9c9b9c1258423f734121157 Mon Sep 17 00:00:00 2001 From: blazejpass Date: Mon, 16 Oct 2023 16:19:23 +0200 Subject: [PATCH] Code improvements according to PR comments in review. --- apps/server/src/modules/tldraw/config.ts | 5 +- .../controller/api-test/tldraw.ws.api.spec.ts | 41 +++++---------- .../modules/tldraw/controller/tldraw.ws.ts | 11 ++-- .../tldraw/domain/ws-shared-doc.do.spec.ts | 35 +++---------- .../modules/tldraw/domain/ws-shared-doc.do.ts | 41 ++++++++++----- .../src/modules/tldraw/helper/test-helper.ts | 22 ++++++++ .../tldraw/repo/tldraw-board.repo.spec.ts | 52 +++++-------------- .../modules/tldraw/repo/tldraw-board.repo.ts | 10 +--- .../tldraw/service/tldraw.ws.service.spec.ts | 30 ++++------- .../tldraw/service/tldraw.ws.service.ts | 3 +- .../tldraw/types/ws-close-code-enum.ts | 2 +- 11 files changed, 106 insertions(+), 146 deletions(-) create mode 100644 apps/server/src/modules/tldraw/helper/test-helper.ts diff --git a/apps/server/src/modules/tldraw/config.ts b/apps/server/src/modules/tldraw/config.ts index d64107d1837..a892ee6c843 100644 --- a/apps/server/src/modules/tldraw/config.ts +++ b/apps/server/src/modules/tldraw/config.ts @@ -1,16 +1,15 @@ import { Configuration } from '@hpi-schul-cloud/commons'; -import { NodeEnvType } from '@src/modules/server'; export interface TldrawConfig { NEST_LOG_LEVEL: string; INCOMING_REQUEST_TIMEOUT: number; - NODE_ENV: string; TLDRAW_DB_COLLECTION_NAME: string; TLDRAW_DB_FLUSH_SIZE: string; TLDRAW_DB_MULTIPLE_COLLECTIONS: boolean; CONNECTION_STRING: string; FEATURE_TLDRAW_ENABLED: boolean; TLDRAW_PING_TIMEOUT: number; + TLDRAW_GC_ENABLED: number; } const tldrawConnectionString: string = Configuration.get('TLDRAW_DB_URL') as string; @@ -18,13 +17,13 @@ const tldrawConnectionString: string = Configuration.get('TLDRAW_DB_URL') as str const tldrawConfig = { NEST_LOG_LEVEL: Configuration.get('NEST_LOG_LEVEL') as string, INCOMING_REQUEST_TIMEOUT: Configuration.get('INCOMING_REQUEST_TIMEOUT_API') as number, - NODE_ENV: Configuration.get('NODE_ENV') as NodeEnvType, TLDRAW_DB_COLLECTION_NAME: Configuration.get('TLDRAW__DB_COLLECTION_NAME') as string, TLDRAW_DB_FLUSH_SIZE: Configuration.get('TLDRAW__DB_FLUSH_SIZE') as number, TLDRAW_DB_MULTIPLE_COLLECTIONS: Configuration.get('TLDRAW__DB_MULTIPLE_COLLECTIONS') as boolean, FEATURE_TLDRAW_ENABLED: Configuration.get('FEATURE_TLDRAW_ENABLED') as boolean, CONNECTION_STRING: tldrawConnectionString, TLDRAW_PING_TIMEOUT: Configuration.get('TLDRAW__PING_TIMEOUT') as number, + TLDRAW_GC_ENABLED: Configuration.get('TLDRAW__GC_ENABLED') as boolean, }; export const SOCKET_PORT = Configuration.get('TLDRAW__SOCKET_PORT') as number; diff --git a/apps/server/src/modules/tldraw/controller/api-test/tldraw.ws.api.spec.ts b/apps/server/src/modules/tldraw/controller/api-test/tldraw.ws.api.spec.ts index 9559a31d526..febef440e62 100644 --- a/apps/server/src/modules/tldraw/controller/api-test/tldraw.ws.api.spec.ts +++ b/apps/server/src/modules/tldraw/controller/api-test/tldraw.ws.api.spec.ts @@ -6,10 +6,11 @@ import { INestApplication } from '@nestjs/common'; import { CoreModule } from '@src/core'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@src/config'; -import { config } from '@src/modules/tldraw/config'; -import { TldrawWsService } from '@src/modules/tldraw/service'; -import { TldrawBoardRepo } from '@src/modules/tldraw/repo'; +import { config } from '../../config'; +import { TldrawWsService } from '../../service'; +import { TldrawBoardRepo } from '../../repo'; import { TldrawWs } from '../tldraw.ws'; +import { TestHelper } from '../../helper/test-helper'; describe('WebSocketController (WsAdapter)', () => { let app: INestApplication; @@ -17,23 +18,12 @@ describe('WebSocketController (WsAdapter)', () => { let ws: WebSocket; const gatewayPort = 3346; - const wsUrl = `ws://localhost:${gatewayPort}`; + const wsUrl = TestHelper.getWsUrl(gatewayPort); const testMessage = 'AZQBAaCbuLANBIsBeyJ0ZFVzZXIiOnsiaWQiOiJkNGIxZThmYi0yMWUwLTQ3ZDAtMDI0Y' + 'S0zZGEwYjMzNjQ3MjIiLCJjb2xvciI6IiNGMDRGODgiLCJwb2ludCI6WzAsMF0sInNlbGVjdGVkSWRzIjpbXSwiYWN' + '0aXZlU2hhcGVzIjpbXSwic2Vzc2lvbiI6ZmFsc2V9fQ=='; - const setupWs = async (docName?: string) => { - if (docName) { - ws = new WebSocket(`${wsUrl}/${docName}`); - } else { - ws = new WebSocket(`${wsUrl}`); - } - await new Promise((resolve) => { - ws.on('open', resolve); - }); - }; - const delay = (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); @@ -70,7 +60,7 @@ describe('WebSocketController (WsAdapter)', () => { const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); jest.spyOn(Uint8Array.prototype, 'reduce').mockReturnValueOnce(1); - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const { buffer } = getMessage(); @@ -84,7 +74,7 @@ describe('WebSocketController (WsAdapter)', () => { expect(handleConnectionSpy).toHaveBeenCalled(); expect(handleConnectionSpy).toHaveBeenCalledTimes(1); - await delay(2000); + await delay(50); ws.close(); }); @@ -98,7 +88,7 @@ describe('WebSocketController (WsAdapter)', () => { }); }); - await delay(200); + await delay(50); ws.close(); }); }); @@ -106,11 +96,8 @@ describe('WebSocketController (WsAdapter)', () => { describe('when tldraw doc has multiple clients', () => { const setup = async () => { const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); - await setupWs('TEST2'); - const ws2 = new WebSocket(`${wsUrl}/TEST2`); - await new Promise((resolve) => { - ws2.on('open', resolve); - }); + ws = await TestHelper.setupWs(wsUrl, 'TEST2'); + const ws2 = await TestHelper.setupWs(wsUrl, 'TEST2'); const { buffer } = getMessage(); @@ -129,17 +116,17 @@ describe('WebSocketController (WsAdapter)', () => { expect(handleConnectionSpy).toHaveBeenCalled(); expect(handleConnectionSpy).toHaveBeenCalledTimes(2); - await delay(200); + await delay(50); ws.close(); ws2.close(); - }, 120000); + }, 2000); }); describe('when tldraw is not correctly setup', () => { const setup = async () => { const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); return { handleConnectionSpy, @@ -156,7 +143,7 @@ describe('WebSocketController (WsAdapter)', () => { expect(handleConnectionSpy).toHaveBeenCalled(); expect(handleConnectionSpy).toHaveBeenCalledTimes(1); - await delay(200); + await delay(50); ws.close(); }); }); diff --git a/apps/server/src/modules/tldraw/controller/tldraw.ws.ts b/apps/server/src/modules/tldraw/controller/tldraw.ws.ts index 6fe7a8dc166..f4ded2be4a5 100644 --- a/apps/server/src/modules/tldraw/controller/tldraw.ws.ts +++ b/apps/server/src/modules/tldraw/controller/tldraw.ws.ts @@ -1,9 +1,9 @@ import { WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection } from '@nestjs/websockets'; import { Server, WebSocket } from 'ws'; import { ConfigService } from '@nestjs/config'; -import { TldrawConfig, SOCKET_PORT } from '@src/modules/tldraw/config'; -import { WsCloseCodeEnum } from '@src/modules/tldraw/types/ws-close-code-enum'; -import { TldrawWsService } from '@src/modules/tldraw/service'; +import { TldrawConfig, SOCKET_PORT } from '../config'; +import { WsCloseCodeEnum } from '../types/ws-close-code-enum'; +import { TldrawWsService } from '../service'; @WebSocketGateway(SOCKET_PORT) export class TldrawWs implements OnGatewayInit, OnGatewayConnection { @@ -22,7 +22,7 @@ export class TldrawWs implements OnGatewayInit, OnGatewayConnection { this.tldrawWsService.setupWSConnection(client, docName); } else { client.close( - WsCloseCodeEnum.WS_CUSTOM_CLIENT_CLOSE_CODE, + WsCloseCodeEnum.WS_CLIENT_BAD_REQUEST_CODE, 'Document name is mandatory in url or Tldraw Tool is turned off.' ); } @@ -42,6 +42,7 @@ export class TldrawWs implements OnGatewayInit, OnGatewayConnection { } private getDocNameFromRequest(request: Request): string { - return request.url.slice(1).split('?')[0].replace('tldraw-server/', ''); + const urlStripped = request.url.replace('/', ''); + return urlStripped; } } diff --git a/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.spec.ts b/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.spec.ts index 2381bee9087..1acd088e92a 100644 --- a/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.spec.ts +++ b/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.spec.ts @@ -5,30 +5,22 @@ import { WsAdapter } from '@nestjs/platform-ws'; import { CoreModule } from '@src/core'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@src/config'; -import { config } from '@src/modules/tldraw/config'; -import { TldrawBoardRepo } from '@src/modules/tldraw/repo/tldraw-board.repo'; import { createMock } from '@golevelup/ts-jest'; -import { WsSharedDocDo } from '@src/modules/tldraw/domain/ws-shared-doc.do'; -import { TldrawWsService } from '@src/modules/tldraw/service'; import * as AwarenessProtocol from 'y-protocols/awareness'; +import { config } from '../config'; +import { TldrawBoardRepo } from '../repo/tldraw-board.repo'; +import { TldrawWsService } from '../service'; +import { WsSharedDocDo } from './ws-shared-doc.do'; import { TldrawWs } from '../controller'; +import { TestHelper } from '../helper/test-helper'; -jest.mock('../utils', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return { - __esModule: true, - ...jest.requireActual('../utils'), - calculateDiff: jest.fn(), - }; -}); - -describe('TldrawBoardRepo', () => { +describe('WsSharedDocDo', () => { let app: INestApplication; let ws: WebSocket; let service: TldrawWsService; const gatewayPort = 3346; - const wsUrl = `ws://localhost:${gatewayPort}`; + const wsUrl = TestHelper.getWsUrl(gatewayPort); jest.useFakeTimers(); @@ -57,20 +49,9 @@ describe('TldrawBoardRepo', () => { await app.close(); }); - const setupWs = async (docName?: string) => { - if (docName) { - ws = new WebSocket(`${wsUrl}/${docName}`); - } else { - ws = new WebSocket(`${wsUrl}`); - } - await new Promise((resolve) => { - ws.on('open', resolve); - }); - }; - describe('when awareness change was called', () => { const setup = async () => { - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); class MockAwareness { on = jest.fn(); diff --git a/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.ts b/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.ts index 8053aa5d639..3cb2a00eb27 100644 --- a/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.ts +++ b/apps/server/src/modules/tldraw/domain/ws-shared-doc.do.ts @@ -2,12 +2,8 @@ import { Doc } from 'yjs'; import WebSocket from 'ws'; import { Awareness, encodeAwarenessUpdate } from 'y-protocols/awareness'; import { encoding } from 'lib0'; -import { Configuration } from '@hpi-schul-cloud/commons'; -import { WSMessageType } from '@src/modules/tldraw/types/connection-enum'; -import { TldrawWsService } from '@src/modules/tldraw/service'; - -// disable gc when using snapshots! -const gcEnabled: boolean = Configuration.get('TLDRAW__GC_ENABLED') as boolean; +import { WSMessageType } from '../types/connection-enum'; +import { TldrawWsService } from '../service'; export class WsSharedDocDo extends Doc { name: string; @@ -19,8 +15,9 @@ export class WsSharedDocDo extends Doc { /** * @param {string} name * @param {TldrawWsService} tldrawService + * @param {boolean} gcEnabled */ - constructor(name: string, private tldrawService: TldrawWsService) { + constructor(name: string, private tldrawService: TldrawWsService, gcEnabled = true) { super({ gc: gcEnabled }); this.name = name; this.conns = new Map(); @@ -35,15 +32,24 @@ export class WsSharedDocDo extends Doc { /** * @param {{ added: Array, updated: Array, removed: Array }} changes - * @param {WebSocket | null} conn Origin is the connection that made the change + * @param {WebSocket | null} wsConnection Origin is the connection that made the change */ public awarenessChangeHandler = ( { added, updated, removed }: { added: Array; updated: Array; removed: Array }, - conn: WebSocket | null + wsConnection: WebSocket | null ) => { + const changedClients = this.manageClientsConnections({ added, updated, removed }, wsConnection); + const buff = this.prepareAwarenessMessage(changedClients); + this.sendAwarenessMessage(buff); + }; + + private manageClientsConnections( + { added, updated, removed }: { added: Array; updated: Array; removed: Array }, + wsConnection: WebSocket | null + ) { const changedClients = added.concat(updated, removed); - if (conn !== null) { - const connControlledIDs = this.conns.get(conn) as Set; + if (wsConnection !== null) { + const connControlledIDs = this.conns.get(wsConnection); if (connControlledIDs !== undefined) { added.forEach((clientID) => { connControlledIDs.add(clientID); @@ -53,13 +59,20 @@ export class WsSharedDocDo extends Doc { }); } } - // broadcast awareness update + return changedClients; + } + + private prepareAwarenessMessage(changedClients: number[]) { const encoder = encoding.createEncoder(); encoding.writeVarUint(encoder, WSMessageType.AWARENESS); encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, changedClients)); - const buff = encoding.toUint8Array(encoder); + const message = encoding.toUint8Array(encoder); + return message; + } + + private sendAwarenessMessage(buff: Uint8Array) { this.conns.forEach((_, c) => { this.tldrawService.send(this, c, buff); }); - }; + } } diff --git a/apps/server/src/modules/tldraw/helper/test-helper.ts b/apps/server/src/modules/tldraw/helper/test-helper.ts new file mode 100644 index 00000000000..01a1a9b0742 --- /dev/null +++ b/apps/server/src/modules/tldraw/helper/test-helper.ts @@ -0,0 +1,22 @@ +import WebSocket from 'ws'; + +export class TestHelper { + public static getWsUrl = (gatewayPort: number): string => { + const wsUrl = `ws://localhost:${gatewayPort}`; + return wsUrl; + }; + + public static setupWs = async (wsUrl: string, docName?: string): Promise => { + let ws: WebSocket; + if (docName) { + ws = new WebSocket(`${wsUrl}/${docName}`); + } else { + ws = new WebSocket(`${wsUrl}`); + } + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + return ws; + }; +} diff --git a/apps/server/src/modules/tldraw/repo/tldraw-board.repo.spec.ts b/apps/server/src/modules/tldraw/repo/tldraw-board.repo.spec.ts index f8f2bd11a38..2bde56efb75 100644 --- a/apps/server/src/modules/tldraw/repo/tldraw-board.repo.spec.ts +++ b/apps/server/src/modules/tldraw/repo/tldraw-board.repo.spec.ts @@ -5,22 +5,13 @@ import { WsAdapter } from '@nestjs/platform-ws'; import { CoreModule } from '@src/core'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@src/config'; -import { config } from '@src/modules/tldraw/config'; -import { TldrawBoardRepo } from '@src/modules/tldraw/repo/tldraw-board.repo'; import { createMock } from '@golevelup/ts-jest'; -import { WsSharedDocDo } from '@src/modules/tldraw/domain/ws-shared-doc.do'; -import { TldrawWsService } from '@src/modules/tldraw/service'; +import { config } from '../config'; +import { TldrawBoardRepo } from './tldraw-board.repo'; +import { WsSharedDocDo } from '../domain/ws-shared-doc.do'; +import { TldrawWsService } from '../service'; import { TldrawWs } from '../controller'; -import * as YDocUtils from '../utils'; - -jest.mock('../utils', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return { - __esModule: true, - ...jest.requireActual('../utils'), - calculateDiff: jest.fn(), - }; -}); +import { TestHelper } from '../helper/test-helper'; describe('TldrawBoardRepo', () => { let app: INestApplication; @@ -29,7 +20,7 @@ describe('TldrawBoardRepo', () => { let service: TldrawWsService; const gatewayPort = 3346; - const wsUrl = `ws://localhost:${gatewayPort}`; + const wsUrl = TestHelper.getWsUrl(gatewayPort); const testMessage = 'AZQBAaCbuLANBIsBeyJpZCI6ImU1YTYwZGVjLTJkMzktNDAxZS0xMDVhLWIwMmM0N2JkYjFhMiIsInRkVXNlciI6eyJp' + 'ZCI6ImU1YTYwZGVjLTJkMzktNDAxZS0xMDVhLWIwMmM0N2JkYjFhMiIsImNvbG9yIjoiI0JENTRDNiIsInBvaW50Ijpb' + @@ -68,17 +59,6 @@ describe('TldrawBoardRepo', () => { await app.close(); }); - const setupWs = async (docName?: string) => { - if (docName) { - ws = new WebSocket(`${wsUrl}/${docName}`); - } else { - ws = new WebSocket(`${wsUrl}`); - } - await new Promise((resolve) => { - ws.on('open', resolve); - }); - }; - it('should repo properties be defined', () => { expect(repo).toBeDefined(); expect(repo.mdb).toBeDefined(); @@ -92,7 +72,7 @@ describe('TldrawBoardRepo', () => { describe('when document receive empty update', () => { const setup = async () => { const doc = new WsSharedDocDo('TEST2', service); - await setupWs('TEST2'); + ws = await TestHelper.setupWs(wsUrl, 'TEST2'); const wsSet = new Set(); wsSet.add(ws); // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -102,24 +82,21 @@ describe('TldrawBoardRepo', () => { .spyOn(repo.mdb, 'getYDoc') .mockImplementation(() => new WsSharedDocDo('TEST', service)); const storeUpdateSpy = jest.spyOn(repo.mdb, 'storeUpdate').mockImplementation(() => {}); - const calculateDiffSpy = jest.spyOn(YDocUtils, 'calculateDiff').mockImplementationOnce(() => 0); return { doc, storeUpdateSpy, storeGetYDocSpy, - calculateDiffSpy, }; }; it('should not update db with diff', async () => { - const { doc, storeUpdateSpy, calculateDiffSpy, storeGetYDocSpy } = await setup(); + const { doc, storeUpdateSpy, storeGetYDocSpy } = await setup(); await repo.updateDocument('TEST2', doc); - await delay(200); + await delay(100); expect(storeUpdateSpy).toHaveBeenCalledTimes(0); storeUpdateSpy.mockRestore(); - calculateDiffSpy.mockRestore(); storeGetYDocSpy.mockRestore(); ws.close(); }); @@ -128,7 +105,7 @@ describe('TldrawBoardRepo', () => { describe('when document receive update', () => { const setup = async () => { const doc = new WsSharedDocDo('TEST', service); - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const wsSet = new Set(); wsSet.add(ws); // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -139,27 +116,24 @@ describe('TldrawBoardRepo', () => { .spyOn(repo.mdb, 'getYDoc') .mockImplementation(() => new WsSharedDocDo('TEST', service)); const byteArray = new TextEncoder().encode(testMessage); - const calculateDiffSpy = jest.spyOn(YDocUtils, 'calculateDiff').mockImplementationOnce(() => 1); return { doc, byteArray, storeUpdateSpy, storeGetYDocSpy, - calculateDiffSpy, }; }; it('should store on db', async () => { - const { doc, byteArray, storeUpdateSpy, storeGetYDocSpy, calculateDiffSpy } = await setup(); + const { doc, byteArray, storeUpdateSpy, storeGetYDocSpy } = await setup(); await repo.updateDocument('TEST', doc); doc.emit('update', [byteArray, undefined, doc]); - await delay(200); + await delay(100); expect(storeUpdateSpy).toHaveBeenCalled(); - expect(storeUpdateSpy).toHaveBeenCalledTimes(2); + expect(storeUpdateSpy).toHaveBeenCalledTimes(1); storeUpdateSpy.mockRestore(); - calculateDiffSpy.mockRestore(); storeGetYDocSpy.mockRestore(); ws.close(); }); diff --git a/apps/server/src/modules/tldraw/repo/tldraw-board.repo.ts b/apps/server/src/modules/tldraw/repo/tldraw-board.repo.ts index 0ac07f75ed8..5f72f02f973 100644 --- a/apps/server/src/modules/tldraw/repo/tldraw-board.repo.ts +++ b/apps/server/src/modules/tldraw/repo/tldraw-board.repo.ts @@ -3,8 +3,8 @@ import { MongodbPersistence } from 'y-mongodb-provider'; import { ConfigService } from '@nestjs/config'; import { TldrawConfig } from '@src/modules/tldraw/config'; import { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from 'yjs'; -import { calculateDiff } from '@src/modules/tldraw/utils'; -import { WsSharedDocDo } from '@src/modules/tldraw/types'; +import { calculateDiff } from '../utils'; +import { WsSharedDocDo } from '../types'; @Injectable() export class TldrawBoardRepo { @@ -53,14 +53,10 @@ export class TldrawBoardRepo { async updateDocument(docName: string, ydoc: WsSharedDocDo) { const persistedYdoc = await this.getYDocFromMdb(docName); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore const persistedStateVector = encodeStateVector(persistedYdoc); const diff = encodeStateAsUpdate(ydoc, persistedStateVector); this.updateStoredDocWithDiff(docName, diff); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore applyUpdate(ydoc, encodeStateAsUpdate(persistedYdoc)); ydoc.on('update', (update) => { @@ -68,8 +64,6 @@ export class TldrawBoardRepo { this.mdb.storeUpdate(docName, update); }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore persistedYdoc.destroy(); } diff --git a/apps/server/src/modules/tldraw/service/tldraw.ws.service.spec.ts b/apps/server/src/modules/tldraw/service/tldraw.ws.service.spec.ts index a52f2d3a8df..564ec4ba55c 100644 --- a/apps/server/src/modules/tldraw/service/tldraw.ws.service.spec.ts +++ b/apps/server/src/modules/tldraw/service/tldraw.ws.service.spec.ts @@ -14,6 +14,7 @@ import { encoding } from 'lib0'; import { TldrawBoardRepo } from '@src/modules/tldraw/repo'; import { TldrawWs } from '@src/modules/tldraw/controller'; import { TldrawWsService } from '.'; +import { TestHelper } from '../helper/test-helper'; describe('TldrawWS', () => { let app: INestApplication; @@ -21,7 +22,7 @@ describe('TldrawWS', () => { let service: TldrawWsService; const gatewayPort = 3346; - const wsUrl = `ws://localhost:${gatewayPort}`; + const wsUrl = TestHelper.getWsUrl(gatewayPort); const testMessage = 'AZQBAaCbuLANBIsBeyJpZCI6ImU1YTYwZGVjLTJkMzktNDAxZS0xMDVhLWIwMmM0N2JkYjFhMiIsInRkVXNlciI6eyJp' + 'ZCI6ImU1YTYwZGVjLTJkMzktNDAxZS0xMDVhLWIwMmM0N2JkYjFhMiIsImNvbG9yIjoiI0JENTRDNiIsInBvaW50Ijpb' + @@ -65,17 +66,6 @@ describe('TldrawWS', () => { }; }; - const setupWs = async (docName?: string) => { - if (docName) { - ws = new WebSocket(`${wsUrl}/${docName}`); - } else { - ws = new WebSocket(`${wsUrl}`); - } - await new Promise((resolve) => { - ws.on('open', resolve); - }); - }; - it('should service properties be defined', () => { expect(service).toBeDefined(); expect(service.pingTimeout).toBeDefined(); @@ -84,7 +74,7 @@ describe('TldrawWS', () => { describe('when client is not connected to WS', () => { const setup = async () => { - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); const closeConSpy = jest.spyOn(service, 'closeConn').mockImplementationOnce(() => {}); const sendSpy = jest.spyOn(service, 'send'); @@ -146,7 +136,7 @@ describe('TldrawWS', () => { describe('when websocket has ready state 0', () => { const setup = async () => { - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); const sendSpy = jest.spyOn(service, 'send'); const doc: { conns: Map> } = { conns: new Map() }; @@ -178,7 +168,7 @@ describe('TldrawWS', () => { describe('when received message of specific type', () => { const setup = async (messageValues: number[]) => { - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const sendSpy = jest.spyOn(service, 'send'); const applyAwarenessUpdateSpy = jest.spyOn(AwarenessProtocol, 'applyAwarenessUpdate'); @@ -235,7 +225,7 @@ describe('TldrawWS', () => { describe('when message is sent', () => { const setup = async (messageValues: number[]) => { - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const messageHandlerSpy = jest.spyOn(service, 'messageHandler'); jest.spyOn(SyncProtocols, 'readSyncMessage').mockImplementationOnce((dec, enc) => { @@ -265,7 +255,7 @@ describe('TldrawWS', () => { describe('when error is thrown', () => { const setup = async () => { - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); const sendSpy = jest.spyOn(service, 'send'); jest.spyOn(SyncProtocols, 'readSyncMessage').mockImplementationOnce(() => { @@ -295,7 +285,7 @@ describe('TldrawWS', () => { describe('when trying to close already closed connection', () => { const setup = async () => { - await setupWs(); + ws = await TestHelper.setupWs(wsUrl); jest.spyOn(ws, 'close').mockImplementationOnce(() => { throw new Error('some error'); @@ -317,7 +307,7 @@ describe('TldrawWS', () => { describe('when ping failed', () => { const setup = async () => { - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const messageHandlerSpy = jest.spyOn(service, 'messageHandler').mockImplementationOnce(() => {}); const closeConnSpy = jest.spyOn(service, 'closeConn'); @@ -348,7 +338,7 @@ describe('TldrawWS', () => { describe('when awareness states size greater then one', () => { const setup = async () => { - await setupWs('TEST'); + ws = await TestHelper.setupWs(wsUrl, 'TEST'); const doc = new WsSharedDocDo('TEST', service); doc.awareness.states = new Map(); diff --git a/apps/server/src/modules/tldraw/service/tldraw.ws.service.ts b/apps/server/src/modules/tldraw/service/tldraw.ws.service.ts index b8ecbee4562..d11eccb6c67 100644 --- a/apps/server/src/modules/tldraw/service/tldraw.ws.service.ts +++ b/apps/server/src/modules/tldraw/service/tldraw.ws.service.ts @@ -100,8 +100,7 @@ export class TldrawWsService { */ getYDoc(docname: string, gc = true) { return map.setIfUndefined(this.docs, docname, () => { - const doc = new WsSharedDocDo(docname, this); - doc.gc = gc; + const doc = new WsSharedDocDo(docname, this, gc); if (this.persistence !== null) { this.persistence.bindState(docname, doc).catch(() => {}); } diff --git a/apps/server/src/modules/tldraw/types/ws-close-code-enum.ts b/apps/server/src/modules/tldraw/types/ws-close-code-enum.ts index 14e6b08e6a5..274fa99a6ae 100644 --- a/apps/server/src/modules/tldraw/types/ws-close-code-enum.ts +++ b/apps/server/src/modules/tldraw/types/ws-close-code-enum.ts @@ -1,3 +1,3 @@ export enum WsCloseCodeEnum { - WS_CUSTOM_CLIENT_CLOSE_CODE = 4000, + WS_CLIENT_BAD_REQUEST_CODE = 4400, }