Skip to content

Commit 6760855

Browse files
authoredMay 15, 2023
test(workflow.controller.internal): add tests for workflow.controller… (#285)
* test: add tests for workflow-runner * fix(workflow-runner): corrent transition plugins call time * docs: add changeset * style: change vars * Revert "fix(workflow-runner): corrent transition plugins call time" This reverts commit f70cef5. * test: improve tests * fix(workflow-browser-sdk): fix waits and sleeps * fix(workflow-core): fix send event interface * fix(workflow-core): fix send event interface * build(.lintstagedrc.json): exclude backoffice v2 from .lintstagedrc.json * refactor: fixed PR comments * test(workflow.controller.internal): add tests for workflow.controller.internal endpoint * refactor: fix pr comments * ci: mv format check exclusion * fix(workflow.service.ts): add await for sendEvent * refactor: fix cr comments + moving to ts * style: remove linting for test files * refactor: pR fixes * refactor: pR fixes * refactor: fix lint * refactor: rm unused nest-cli.json + fix build issues * refactor: cleanup test expectation * refactor: rm redundant line

9 files changed

+250
-25
lines changed
 

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"playwright:install": "nx run-many --target=playwright:install",
3535
"dev": "nx run-many --target=dev --projects=@ballerine/workflows-service,@ballerine/backoffice-v2",
3636
"start": "nx run-many --target=start --projects=@ballerine/web-sdk",
37-
"build": "nx run-many --target=build --projects=@ballerine/workflow-browser-sdk,@ballerine/web-sdk,@ballerine/workflow-core,@ballerine/workflow-node-sdk,@ballerine/rules-engine-lib,@ballerine/common",
37+
"build": "nx run-many --target=build --projects=@ballerine/workflow-browser-sdk,@ballerine/web-sdk,@ballerine/workflow-core,@ballerine/workflow-node-sdk,@ballerine/rules-engine-lib,@ballerine/common,@ballerine/workflows-service",
3838
"web-sdk:dev": "nx run @ballerine/web-sdk:dev",
3939
"web-sdk:start": "nx run @ballerine/web-sdk:start",
4040
"workflows-service:start": "nx run @ballerine/workflows-service:start",

‎services/workflows-service/jest.config.cjs

+5
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ module.exports = {
55
moduleNameMapper: {
66
'^@/(.*)$': '<rootDir>/src/$1',
77
},
8+
globals: {
9+
'ts-jest': {
10+
tsconfig: './tsconfig.test.json',
11+
},
12+
},
813
};

‎services/workflows-service/nest-cli.json

-6
This file was deleted.

‎services/workflows-service/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"dev": "npm run start:watch",
1313
"start:watch": "nest start --watch",
1414
"start:debug": "nest start --debug --watch",
15-
"build": "nest build",
15+
"build": "nest build --path='./tsconfig.build.json'",
1616
"test": "jest --verbose",
1717
"test:watch": "jest --verbose --watch",
1818
"seed": "ts-node scripts/seed.ts",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-call */
2+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
3+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
4+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
5+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
6+
7+
import { BaseFakeRepository } from '../../../../test-utils/src/base-fake-repository';
8+
9+
import { WorkflowControllerInternal } from './workflow.controller.internal';
10+
import { WorkflowService } from './workflow.service';
11+
import { WorkflowDefinitionModel } from './workflow-definition.model';
12+
import { WorkflowEventEmitterService } from './workflow-event-emitter.service';
13+
14+
class FakeWorkflowRuntimeDataRepo extends BaseFakeRepository {
15+
constructor() {
16+
super(Object);
17+
}
18+
}
19+
20+
class FakeWorkflowDefinitionRepo extends BaseFakeRepository {
21+
constructor() {
22+
super(WorkflowDefinitionModel);
23+
}
24+
}
25+
26+
function buildWorkflowDeifintion(sequenceNum) {
27+
return {
28+
id: sequenceNum.toString(),
29+
name: `name ${sequenceNum}`,
30+
version: sequenceNum,
31+
definition: {
32+
initial: 'initial',
33+
states: {
34+
initial: {
35+
on: {
36+
COMPLETE: 'completed',
37+
},
38+
},
39+
completed: {
40+
type: 'final',
41+
},
42+
},
43+
},
44+
definitionType: `definitionType ${sequenceNum}`,
45+
createdAt: new Date(),
46+
updatedAt: new Date(),
47+
};
48+
}
49+
50+
describe('WorkflowControllerInternal', () => {
51+
let controller;
52+
let workflowRuntimeDataRepo;
53+
let eventEmitterSpy;
54+
const numbUserInfo = Symbol();
55+
56+
beforeEach(() => {
57+
const workflowDefinitionRepo = new FakeWorkflowDefinitionRepo();
58+
workflowRuntimeDataRepo = new FakeWorkflowRuntimeDataRepo();
59+
60+
eventEmitterSpy = {
61+
emitted: [],
62+
63+
emit(status, data) {
64+
this.emitted.push({ status, data });
65+
},
66+
};
67+
const service = new WorkflowService(
68+
workflowDefinitionRepo as any,
69+
workflowRuntimeDataRepo,
70+
{} as any,
71+
eventEmitterSpy,
72+
);
73+
74+
controller = new WorkflowControllerInternal(service, {} as any);
75+
});
76+
77+
describe('.listWorkflowDefinitions', () => {
78+
it('returns workflows by query', async () => {
79+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(2));
80+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(3));
81+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(4));
82+
83+
const definitions = await controller.listWorkflowDefinitions(numbUserInfo, {
84+
query: {
85+
where: {
86+
name: 'name 3',
87+
},
88+
},
89+
});
90+
91+
expect(definitions).toHaveLength(1);
92+
expect(definitions[0]).toMatchObject({ id: '3', name: 'name 3' });
93+
expect(definitions[0]).not.toHaveProperty('updatedAt');
94+
});
95+
96+
it('filters out certain fields', async () => {
97+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(3));
98+
99+
const definitions = await controller.listWorkflowDefinitions(numbUserInfo, {
100+
query: {
101+
where: {
102+
name: 'name 3',
103+
},
104+
},
105+
});
106+
107+
expect(definitions[0]).not.toHaveProperty('updatedAt');
108+
});
109+
});
110+
111+
describe('.event', () => {
112+
describe('reaching to a state of type "final"', () => {
113+
it('updates runtime data status to "completed"', async () => {
114+
const initialRuntimeData = {
115+
id: '2',
116+
workflowDefinitionId: '2',
117+
context: {
118+
numb: 'context',
119+
},
120+
};
121+
await workflowRuntimeDataRepo.create({
122+
data: initialRuntimeData,
123+
});
124+
125+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(2));
126+
await controller.event({ id: '2' }, { name: 'COMPLETE' });
127+
128+
const runtimeData = await workflowRuntimeDataRepo.findById('2');
129+
130+
expect(runtimeData.state).toEqual('completed');
131+
expect(runtimeData.status).toEqual('completed');
132+
});
133+
134+
it('emits an event', async () => {
135+
const initialRuntimeData = {
136+
id: '2',
137+
workflowDefinitionId: '2',
138+
context: {
139+
numb: 'context',
140+
},
141+
};
142+
await workflowRuntimeDataRepo.create({
143+
data: initialRuntimeData,
144+
});
145+
146+
await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(2));
147+
await controller.event({ id: '2' }, { name: 'COMPLETE' });
148+
149+
expect(eventEmitterSpy.emitted).toEqual([
150+
{
151+
status: 'workflow.completed',
152+
data: {
153+
runtimeData: initialRuntimeData,
154+
state: 'completed',
155+
context: {
156+
numb: 'context',
157+
},
158+
},
159+
},
160+
]);
161+
});
162+
});
163+
});
164+
});

‎services/workflows-service/src/workflow/workflow.controller.internal.ts

+1-16
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,7 @@ export class WorkflowControllerInternal {
3535
async createWorkflowDefinition(
3636
@UserData() userInfo: UserInfo,
3737
@common.Body() data: WorkflowDefinitionCreateDto,
38-
): Promise<
39-
Pick<
40-
WorkflowDefinitionModel,
41-
| 'id'
42-
| 'name'
43-
| 'version'
44-
| 'state'
45-
| 'context'
46-
| 'definition'
47-
| 'definitionType'
48-
| 'backend'
49-
| 'extensions'
50-
| 'persistStates'
51-
| 'submitStates'
52-
>
53-
> {
38+
) {
5439
return await this.service.createWorkflowDefinition({
5540
data,
5641
select: {
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"extends": "./tsconfig.json",
3-
"exclude": ["node_modules", "prisma", "test", "dist", "**/*spec.ts", "admin"]
3+
"exclude": ["node_modules", "prisma", "test", "dist", "**/*.test.ts", "**/*spec.ts", "admin"]
44
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"noImplicitAny": false
5+
}
6+
}
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const { validate } = require('class-validator');
2+
3+
export class BaseFakeRepository {
4+
#__rows: Array<any> = [];
5+
#__ModelClass: any;
6+
7+
constructor(ModelClass: any) {
8+
this.#__ModelClass = ModelClass;
9+
}
10+
11+
async create(args: any) {
12+
const model = await this.__initModel(args.data);
13+
this.#__rows.push(model);
14+
15+
return __deepCopy(model, args.select);
16+
}
17+
18+
async __initModel(data: any) {
19+
const model = new this.#__ModelClass();
20+
Object.assign(model, data);
21+
22+
const errors = await validate(model);
23+
if (errors.length) throw new Error('\n' + errors);
24+
25+
return model;
26+
}
27+
28+
async findMany(args: any) {
29+
return this.__findMany(args).map(row => __deepCopy(row, args.select));
30+
}
31+
32+
__findMany(args: any) {
33+
return this.#__rows.filter(row => {
34+
for (const key in args.where) {
35+
if (row[key] !== args.where[key]) return false;
36+
}
37+
return true;
38+
});
39+
}
40+
41+
__findById(id: string) {
42+
const rows = this.__findMany({ where: { id: id } });
43+
if (rows.length === 0) throw new Error(`could not find id ${id}`);
44+
return rows[0];
45+
}
46+
47+
async findById(id: string) {
48+
const row = this.__findById(id);
49+
return __deepCopy(row);
50+
}
51+
52+
async updateById(id: string, args: any) {
53+
const row = this.__findById(id);
54+
Object.assign(row, args.data);
55+
return __deepCopy(row);
56+
}
57+
}
58+
59+
function __deepCopy(row: Object, select?: Object) {
60+
let modelCopy = JSON.parse(JSON.stringify(row));
61+
62+
if (select) {
63+
for (let key of Object.keys(modelCopy)) {
64+
if (select?.[key] !== true) {
65+
delete modelCopy[key];
66+
}
67+
}
68+
}
69+
70+
return modelCopy;
71+
}

0 commit comments

Comments
 (0)
Please sign in to comment.