Skip to content

Commit

Permalink
Test Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed May 11, 2024
1 parent 73e9343 commit 6b1db05
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export class OperationEmitter {
}

private emitOperations() {
console.log('operation');
if (isEmpty(this.operationBuffer)) return;
if (this.operationBuffer.length > this.chunkSize) {
this.emitChunked();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ function emit(str: string, data: any) {
const socket = namespaces[`/${namespace}`];
if (!socket) throw new Error('Invalid namespace');
socket.emit(event, data);
console.log(namespace, event, data);
}

function on(eventHandlers: SocketEventHandlers) {
Expand Down
1 change: 0 additions & 1 deletion code/client/src/ui/pages/workspace/hooks/useWorkspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ function useWorkspaces() {
}

async function createWorkspace(values: { [key: string]: string }) {
console.log('Creating workspace', values);
if (!values.name) throw new Error('Workspace name is required');
// ... validate other fields
const workspace = await http.post('/workspaces', values);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ function resourcesHandlers(service: ResourcesService, io: Server) {
* @param res
*/
const createResource = async (req: Request, res: Response) => {
const { wid } = req.params;
const resource = req.body as ResourceInputModel;
if (!resource) throw new InvalidParameterError('Body is required');
const { workspace, type, name, parent } = resource;
if (!workspace) throw new InvalidParameterError('Workspace id is required');
const { type, name, parent } = resource;
if (!wid) throw new InvalidParameterError('Workspace id is required');
if (!type) throw new InvalidParameterError('Resource type is required');
if (!name) throw new InvalidParameterError('Resource name is required');
if (!parent) throw new InvalidParameterError('Resource parent is required');
const id = await service.createResource(workspace, name, type, parent);
io.of('/workspaces').in(workspace).emit('resources:create', { id, name, type, parent });
const id = await service.createResource(wid, name, type, parent);
io.of('/workspaces').in(wid).emit('resources:create', { id, name, type, parent });
httpResponse.created(res).json({ id });
};

Expand All @@ -45,6 +45,8 @@ function resourcesHandlers(service: ResourcesService, io: Server) {
*/
const updateResource = async (req: Request, res: Response) => {
const { wid, id } = req.params;
if (!wid) throw new InvalidParameterError('Workspace id is required');
if (!id) throw new InvalidParameterError('Resource id is required');
const resource = req.body as Partial<WorkspaceResource>;
if (!resource) throw new InvalidParameterError('Body is required');
await service.updateResource(id, resource);
Expand All @@ -59,6 +61,8 @@ function resourcesHandlers(service: ResourcesService, io: Server) {
*/
const deleteResource = async (req: Request, res: Response) => {
const { wid, id } = req.params;
if (!wid) throw new InvalidParameterError('Workspace id is required');
if (!id) throw new InvalidParameterError('Resource id is required');
await service.deleteResource(id);
io.of('/workspaces').in(wid).emit('resources:delete', { id });
httpResponse.noContent(res).send();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Request, Response } from 'express';
import { WorkspaceMetaData } from '@notespace/shared/src/workspace/types/workspace';
import { Services } from '@services/Services';
import { Server } from 'socket.io';
import { InvalidParameterError } from '@domain/errors/errors';

function workspaceHandlers(services: Services, io: Server) {
/**
Expand All @@ -14,6 +15,7 @@ function workspaceHandlers(services: Services, io: Server) {
*/
const createWorkspace = async (req: Request, res: Response) => {
const { name } = req.body as WorkspaceMetaData;
if (!name) throw new InvalidParameterError('Workspace name is required');
const id = await services.workspace.createWorkspace(name);
httpResponse.created(res).json({ id });
};
Expand Down Expand Up @@ -46,6 +48,8 @@ function workspaceHandlers(services: Services, io: Server) {
*/
const updateWorkspace = async (req: Request, res: Response) => {
const { id, name } = req.body as WorkspaceMetaData;
if (!id) throw new InvalidParameterError('Workspace id is required');
if (!name) throw new InvalidParameterError('Workspace name is required');
await services.workspace.updateWorkspace(id, name);
io.of('/workspaces').in(id).emit('workspaces:update', { id, name });
httpResponse.noContent(res).send();
Expand All @@ -58,6 +62,7 @@ function workspaceHandlers(services: Services, io: Server) {
*/
const deleteWorkspace = async (req: Request, res: Response) => {
const { wid } = req.params;
if (!wid) throw new InvalidParameterError('Workspace id is required');
await services.workspace.deleteWorkspace(wid);
io.of('/workspaces').in(wid).emit('workspaces:delete', { id: wid });
httpResponse.noContent(res).send();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { InvalidParameterError, NotFoundError } from '@domain/errors/errors';
import sql from '@database/postgres/config';

export class PostgresResourceDatabase implements ResourceRepository {
async createResource(wid: string, name: string, type: ResourceType, parent: string): Promise<string> {
const resource = { workspace: wid, name, type, parent };
async createResource(wid: string, name: string, type: ResourceType, parent?: string): Promise<string> {
const resource = { workspace: wid, name, type, parent: parent || 'IDKYET' };
const results = await sql`
INSERT INTO resource ${sql(resource)}
RETURNING id
Expand Down
2 changes: 1 addition & 1 deletion code/server/src/ts/database/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface DocumentRepository {
* Resource Repository - Interface for handling resources metadata management
*/
export interface ResourceRepository {
createResource: (wid: string, name: string, type: ResourceType, parent: string) => Promise<string>;
createResource: (wid: string, name: string, type: ResourceType, parent?: string) => Promise<string>;
getResource: (id: string) => Promise<WorkspaceResource>;
updateResource: (id: string, newProps: Partial<WorkspaceResource>) => Promise<void>;
deleteResource: (id: string) => Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion code/server/src/ts/services/ResourcesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class ResourcesService {
this.documents = documents;
}

async createResource(wid: string, name: string, type: ResourceType, parent: string): Promise<string> {
async createResource(wid: string, name: string, type: ResourceType, parent?: string): Promise<string> {
return await this.resources.createResource(wid, name, type, parent);
}

Expand Down
4 changes: 2 additions & 2 deletions code/server/src/ts/services/WorkspaceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export class WorkspaceService {
this.database = database;
}

async createWorkspace(title: string): Promise<string> {
return await this.database.createWorkspace(title);
async createWorkspace(name: string): Promise<string> {
return await this.database.createWorkspace(name);
}

async getWorkspaces(): Promise<WorkspaceMetaData[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as http from 'http';
import request = require('supertest');
import { Server } from 'socket.io';
import { setup } from '../server.test';
import { applyOperations } from './utils';
import { Express } from 'express';
import { io, Socket } from 'socket.io-client';
import { InsertOperation, DeleteOperation, Operation } from '@notespace/shared/src/document/types/operations';
import { InsertOperation, DeleteOperation } from '@notespace/shared/src/document/types/operations';
import { FugueTree } from '@notespace/shared/src/document/FugueTree';
import { requests as requestOperations } from '../utils/requests';
import { randomString } from '../utils';

const PORT = process.env.PORT || 8080;
const BASE_URL = `http://localhost:${PORT}/document`;
let ioServer: Server;
let httpServer: http.Server;
let app: Express;
let requests: ReturnType<typeof requestOperations>;
let client1: Socket;
let client2: Socket;
let tree = new FugueTree<string>();
Expand All @@ -21,7 +21,7 @@ beforeAll(done => {
const { _http, _io, _app } = setup();
httpServer = _http;
ioServer = _io;
app = _app;
requests = requestOperations(_app);

// ioServer.on('connection', onConnectionHandler);
httpServer.listen(PORT, () => {
Expand All @@ -47,6 +47,15 @@ beforeEach(() => {

describe('Operations must be commutative', () => {
test('insert operations should be commutative', async () => {
// setup document
const wid = await requests.workspace.createWorkspace(randomString());
const id = await requests.document.createDocument(wid);

// clients join the document
client1.emit('join', id);
client2.emit('join', id);

// create insert operations
const insert1: InsertOperation = {
type: 'insert',
id: { sender: 'A', counter: 0 },
Expand All @@ -61,14 +70,6 @@ describe('Operations must be commutative', () => {
parent: { sender: 'root', counter: 0 },
side: 'R',
};
// create a document
const createdResponse = await request(app).post('/documents');
expect(createdResponse.status).toBe(201);
const id = createdResponse.body.id;

// clients join the document
client1.emit('join', id);
client2.emit('join', id);

// client 1 inserts 'a' and client 2 inserts 'b'
client1.emit('operation', [insert1]);
Expand All @@ -77,19 +78,26 @@ describe('Operations must be commutative', () => {
await new Promise(resolve => setTimeout(resolve, 500));

// get the document
const response = await request(app).get('/documents/' + id);
expect(response.status).toBe(200);
const operations = response.body.operations as Operation[];
const document = await requests.document.getDocument(wid, id);

// apply the operations to the tree
applyOperations(tree, operations);
applyOperations(tree, document.content);

expect(tree.toString()).toBe('ab');
});
});

describe('Operations must be idempotent', () => {
test('delete operations should be idempotent', async () => {
// setup document
const wid = await requests.workspace.createWorkspace(randomString());
const id = await requests.document.createDocument(wid);

// clients join the document
client1.emit('join', id);
client2.emit('join', id);

// create insert operations
const insert1: InsertOperation = {
type: 'insert',
id: { sender: 'A', counter: 0 },
Expand All @@ -105,15 +113,6 @@ describe('Operations must be idempotent', () => {
side: 'R',
};

// create a document
const createdResponse = await request(app).post('/documents');
expect(createdResponse.status).toBe(201);
const id = createdResponse.body.id;

// clients join the document
client1.emit('join', id);
client2.emit('join', id);

// both clients insert 'a'
client1.emit('operation', [insert1]);
client2.emit('operation', [insert2]);
Expand All @@ -130,9 +129,8 @@ describe('Operations must be idempotent', () => {

await new Promise(resolve => setTimeout(resolve, 500));

const response = await request(app).get('/documents/' + id);
const operations = response.body.operations as Operation[];
applyOperations(tree, operations);
const document = await requests.document.getDocument(wid, id);
applyOperations(tree, document.content);
expect(tree.toString()).toBe('a');
});
});
File renamed without changes.
5 changes: 5 additions & 0 deletions code/server/test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function randomString(length: number = 10): string {
return Math.random()
.toString(36)
.substring(2, 2 + length);
}
35 changes: 35 additions & 0 deletions code/server/test/utils/documentRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Express } from 'express';
import request = require('supertest');
import { DocumentResource, ResourceInputModel, ResourceType } from '@notespace/shared/src/workspace/types/resource';

export function documentRequests(app: Express) {
async function createDocument(wid: string, name?: string): Promise<string> {
const resource: ResourceInputModel = {
name: name || 'Untitled',
type: ResourceType.DOCUMENT,
parent: undefined,
};
const response = await request(app).post(`/workspaces/${wid}`).send(resource);
expect(response.status).toBe(201);
return response.body.id;
}

async function getDocument(wid: string, id: string): Promise<DocumentResource> {
const response = await request(app).get(`/workspaces/${wid}/${id}`);
expect(response.status).toBe(200);
if (response.body.type !== ResourceType.DOCUMENT) throw new Error('Resource is not a document');
return response.body;
}

async function updateDocument(wid: string, id: string, name: string) {
const response = await request(app).put(`/workspaces/${wid}/${id}`).send({ name });
expect(response.status).toBe(204);
}

async function deleteDocument(wid: string, id: string) {
const response = await request(app).delete(`/workspaces/${wid}/${id}`);
expect(response.status).toBe(204);
}

return { createDocument, getDocument, updateDocument, deleteDocument };
}
10 changes: 10 additions & 0 deletions code/server/test/utils/requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Express } from 'express';
import { documentRequests } from './documentRequests';
import { workspaceRequests } from './workspaceRequests';

export function requests(app: Express) {
return {
document: documentRequests(app),
workspace: workspaceRequests(app),
};
}
35 changes: 35 additions & 0 deletions code/server/test/utils/workspaceRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Express } from 'express';
import request = require('supertest');
import { WorkspaceMetaData } from '@notespace/shared/src/workspace/types/workspace';

export function workspaceRequests(app: Express) {
async function createWorkspace(name: string): Promise<string> {
const response = await request(app).post('/workspaces').send({ name });
expect(response.status).toBe(201);
return response.body.id;
}

async function getWorkspaces(): Promise<WorkspaceMetaData[]> {
const response = await request(app).get('/workspaces');
expect(response.status).toBe(200);
return response.body;
}

async function getWorkspace(id: string, metaOnly: boolean): Promise<WorkspaceMetaData> {
const response = await request(app).get(`/workspaces/${id}?metaOnly=${metaOnly}`);
expect(response.status).toBe(200);
return response.body;
}

async function updateWorkspace(id: string, name: string) {
const response = await request(app).put(`/workspaces/${id}`).send({ name });
expect(response.status).toBe(204);
}

async function deleteWorkspace(id: string) {
const response = await request(app).delete(`/workspaces/${id}`);
expect(response.status).toBe(204);
}

return { createWorkspace, getWorkspaces, getWorkspace, updateWorkspace, deleteWorkspace };
}
3 changes: 1 addition & 2 deletions code/shared/src/workspace/types/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ export interface WorkspaceResource {
}

export interface ResourceInputModel {
workspace: string;
name: string;
type: ResourceType;
parent: string;
parent?: string;
}

export enum ResourceType {
Expand Down

0 comments on commit 6b1db05

Please sign in to comment.