Skip to content

Commit

Permalink
fix postprocessing
Browse files Browse the repository at this point in the history
add more tests
  • Loading branch information
omvmike committed Nov 14, 2023
1 parent 9262170 commit 724c5b6
Show file tree
Hide file tree
Showing 10 changed files with 432 additions and 70 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fake-entity-service",
"version": "0.7.0-alpha.0",
"version": "0.7.0-alpha.1",
"description": "A fake database entities service for testing",
"author": {
"email": "mike.onofrienko@clockwise.software",
Expand Down
2 changes: 0 additions & 2 deletions sequelize/migrations/0000000-initial.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ const migrationCommands = [
message: {
allowNull: false,
type: Sequelize.TEXT,
unique: true,
},
created_at: {
allowNull: false,
Expand Down Expand Up @@ -145,7 +144,6 @@ const migrationCommands = [
message: {
allowNull: false,
type: Sequelize.TEXT,
unique: true,
},
created_at: {
allowNull: false,
Expand Down
59 changes: 41 additions & 18 deletions src/sequelize-fake-entity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export class SequelizeFakeEntityService<TEntity extends Model> {


constructor(
protected repository: any) {}
public repository: any
) {}


protected getFakeFields(
Expand All @@ -77,7 +78,7 @@ export class SequelizeFakeEntityService<TEntity extends Model> {
/* You can override this method
to set default values for Entity fields
*/
setFakeFields(): Partial<TEntity> {
protected setFakeFields(): Partial<TEntity> {
return {} as Partial<TEntity>;
}

Expand All @@ -86,25 +87,29 @@ export class SequelizeFakeEntityService<TEntity extends Model> {
For example, when you are adding nested entity, you can mutate the parent entity
Can be called multiple times to add multiple states
*/
protected addStates(
public addStates(
states: Partial<TEntity> | Partial<TEntity>[] | (() => Partial<TEntity>) | (() => Partial<TEntity>)[],
): void {
): this
{
if (Array.isArray(states)) {
const statesArray: Partial<TEntity>[] = states.map(state => (typeof state === 'function') ? state() : state);
if (statesArray.length > 0) {
this.statesGenerators.push(this.circularArrayGenerator(statesArray));
}
return;
return this;
}
this.states = Object.assign(this.states || {}, (typeof states === 'function') ? states() : states);
return this;
}

setEntityPreprocessor(preprocessor: (fields: Partial<TEntity>, ) => (Partial<TEntity> | Promise<Partial<TEntity>>)): void {
public afterMakingCallback(preprocessor: (fields: Partial<TEntity>, index: number) => (Partial<TEntity> | Promise<Partial<TEntity>>)): this {
this.entityPreprocessor = preprocessor;
return this;
}

setEntityPostprocessor(postprocessor: (entity: TEntity, ) => (TEntity | Promise<TEntity>)): void {
public afterCreatingCallback(postprocessor: (entity: TEntity, index: number) => (TEntity | Promise<TEntity>)): this {
this.entityPostprocessor = postprocessor;
return this;
}

/* The same purpose as the states, but you can pass array of states
Expand Down Expand Up @@ -152,8 +157,8 @@ export class SequelizeFakeEntityService<TEntity extends Model> {
const entity = await this.repository.create(preprocessedFields, {returning: true});
this.entityIds.push(this.hasCompositeId() ? this.pickKeysFromObject(entity) : this.getId(entity));
await this.processNested(entity);
this.clearStates();
const postprocessed = await this.postprocessEntities([entity]);
this.clearStates();
return postprocessed.pop();
}

Expand Down Expand Up @@ -218,21 +223,38 @@ export class SequelizeFakeEntityService<TEntity extends Model> {
.fill(1)
.map((_, i) => {
const fields: any = this.getFakeFields(customFields);
return this.entityPreprocessor(fields, i);
})
return Promise.all(bulkInsertDataPromises);
return typeof this.entityPreprocessor === 'function'
? this.entityPreprocessor(fields, i)
: fields;
});
return this.sequentialResolver(bulkInsertDataPromises);
}

protected async postprocessEntities(entities: TEntity[]): Promise<TEntity[]> {
if(this.entityPostprocessor) {
const postprocessorEntitiesPromises = entities.map((entity, i) => {
return this.entityPostprocessor(entity, i);
});
return Promise.all(postprocessorEntitiesPromises);
if(typeof this.entityPostprocessor === 'function') {
const postprocessingEntitiesPromises = entities
.map((entity, i) => this.entityPostprocessor(entity, i));
return this.sequentialResolver(postprocessingEntitiesPromises);
}
return entities;
}

protected async sequentialResolver(promises: Promise<any>[] | any[]): Promise<any[]> {
const results = [];
for (const promise of promises) {
if (promise instanceof Promise) {
results.push(await promise);
continue;
}
if (typeof promise === 'function') {
results.push(await promise());
continue;
}
results.push(promise);
}
return results;
}


async createMany(
count: number,
Expand All @@ -246,10 +268,11 @@ export class SequelizeFakeEntityService<TEntity extends Model> {
: entities.map(e => this.getId(e));
this.entityIds.push(...ids);
if (this.nestedEntities.length) {
await Promise.all(entities.map(e => this.processNested(e)));
await this.sequentialResolver(entities.map(e => this.processNested(e)));
}
const processedEntities = this.postprocessEntities(entities);
this.clearStates();
return this.postprocessEntities(entities);
return processedEntities;
}

getIdFieldNames(): string[] {
Expand Down
166 changes: 161 additions & 5 deletions tests/sequelize-basics.int-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@ import {Role, RoleIds} from "./sequelize-models/role.entity";
import {FakeUserService} from "./sequelize-factories/fake-user.service";
import {FakeLeaderFollowerService} from "./sequelize-factories/fake-leader-follower.service";
import {LeaderFollower} from "./sequelize-models/leader-follower.entity";
import {FakeRoleService} from "./sequelize-factories/fake-role.service";
import {FakePostService} from "./sequelize-factories/fake-post.service";
import {Post} from "./sequelize-models/post.entity";
import {Comment} from "./sequelize-models/comment.entity";
import {FakeCommentService} from "./sequelize-factories/fake-comment.service";

const sequelize = new Sequelize({
database: 'test-db',
dialect: 'postgres',
username: 'tester',
password: 'test-pwd',

models: [User, Role, LeaderFollower],
models: [User, Role, LeaderFollower, Post, Comment],
});

const fakeUserService = new FakeUserService(sequelize.models.User as typeof User);
const fakeRoleService = new FakeRoleService(sequelize.models.Role as typeof Role);
const fakeLeaderFollowerService = new FakeLeaderFollowerService(sequelize.models.LeaderFollower as typeof LeaderFollower);
const fakePostService = new FakePostService(sequelize.models.Post as typeof Post);
const fakeCommentService = new FakeCommentService(sequelize.models.Comment as typeof Comment);

describe('Test SequelizeFakeEntityService can create and cleanup DB entities', () => {

Expand Down Expand Up @@ -46,7 +55,7 @@ describe('Test SequelizeFakeEntityService can create and cleanup DB entities', (
});

it('should create user with specific role', async () => {
const customer = await fakeUserService.asCustomer().create();
const customer = await fakeUserService.asRole(RoleIds.CUSTOMER).create();
expect(customer).toBeDefined();
expect(customer.id).toBeDefined();
expect(customer.roleId).toBe(RoleIds.CUSTOMER);
Expand All @@ -57,12 +66,11 @@ describe('Test SequelizeFakeEntityService can create and cleanup DB entities', (
});

it('should create and delete entities with composite key', async () => {
const customer = await fakeUserService.asCustomer().create();
const customer = await fakeUserService.asRole(RoleIds.CUSTOMER).create();
expect(customer).toBeDefined();
expect(customer.id).toBeDefined();
expect(customer.roleId).toBe(RoleIds.CUSTOMER);
const manager = await fakeUserService.asManager().create();

const manager = await fakeUserService.asRole(RoleIds.MANAGER).create();
expect(fakeLeaderFollowerService.getIdFieldNames()).toEqual(['leaderId', 'followerId']);


Expand All @@ -81,4 +89,152 @@ describe('Test SequelizeFakeEntityService can create and cleanup DB entities', (

await fakeLeaderFollowerService.cleanup();
});


it('should create parent and nested entities', async () => {
//const users = await fakeUserService.withCustomRole(fakeRoleService.addSequence('name', ['first', 'second', 'third'])).createMany(3);
//console.log(JSON.stringify(user), user.roleId);
const users = await fakeUserService.addFieldSequence('roleId',[RoleIds.ADMIN, RoleIds.CUSTOMER, RoleIds.MANAGER]).createMany(5);
users.forEach(user => console.log(JSON.stringify(user), user.roleId));

});

it('should create user and posts', async () => {
const user = await fakeUserService.asRole(RoleIds.CUSTOMER).create();
const posts = await fakePostService.createMany(5, {userId: user.id});
expect(posts).toBeDefined();
expect(posts.length).toBe(5);

await fakePostService.cleanup();
});

it('should create parent and nested entities', async () => {
const posts = await fakePostService.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER)).createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
// all posts should have the same user with CUSTOMER role
posts.forEach(post => {
expect(post.userId).toBe(posts[0].userId)
//expect(post.user.roleId).toBe(RoleIds.CUSTOMER
});
await fakePostService.cleanup();
});

it('should create unique parent for each nested entities', async () => {
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER),{}, true)
.createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
// all posts should have different user
posts.forEach((post, i) => {
posts.forEach((post2, j) => {
if (i !== j) {
expect(post.userId).not.toBe(post2.userId)
}
});
});
await fakePostService.cleanup();
});

it('should create field sequence', async () => {
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER))
.addFieldSequence('message', ['one', 'two', 'three'])
.createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
const messages = posts.map(post => post.message);
expect(messages).toEqual(['one', 'two', 'three', 'one', 'two']);
await fakePostService.cleanup();
});

it('should create array sequence', async () => {
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER))
.addStates([{message: 'one'}, {message: 'two'}, {message: 'three'}])
.afterMakingCallback((post, index) => {
console.log('afterMakingCallback', post, index);
return post;
})
.createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
const messages = posts.map(post => post.message);
expect(messages).toEqual(['one', 'two', 'three', 'one', 'two']);
await fakePostService.cleanup();
});

it('should create functional sequence', async () => {
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER))
.addStates([
() => ({message: 'one'}),
() => ({message: 'two'}),
() => ({message: 'three'}),
])
.afterMakingCallback((post, index) => {
console.log('afterMakingCallback', post, index);
return post;
})
.createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
const messages = posts.map(post => post.message);
expect(messages).toEqual(['one', 'two', 'three', 'one', 'two']);
await fakePostService.cleanup();
});

it('should create array sequence and afterMakingCallback', async () => {
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER))
.addStates([
{message: 'one'},
{message: 'two'},
{message: 'three'},
])
.afterMakingCallback((post, index) => {
return {
...post,
message: `${index + 1}. ${post.message}`,
};
})
.createMany(5);
expect(posts).toBeDefined();
expect(posts.length).toBe(5);
const messages = posts.map(post => post.message);
expect(messages).toEqual(['1. one', '2. two', '3. three', '4. one', '5. two']);
await fakePostService.cleanup();
});

it ('should create post with comments sequence', async () => {
const commenters = await fakeUserService.asRole(RoleIds.CUSTOMER).createMany(3);
const commentMessages = ['first comment', 'second comment', 'third comment'];
const posts = await fakePostService
.withParentUser(fakeUserService.asRole(RoleIds.CUSTOMER))
.withComments(
fakeCommentService
.addStates(commenters.map((c, i) => ({
userId: c.id,
message: `${i + 1}. ${commentMessages[i % commentMessages.length]}`,
}))), 3)
.afterCreatingCallback(async (post, index) => {
return post.reload({
include: [{model: Comment}, {model: User}],
});
})
.createMany(2);
expect(posts).toBeDefined();
expect(posts.length).toBe(2);
posts.map(post => {
expect(post.user.id).toBe(post.userId)
expect(post.comments.length).toBe(3);
expect(post.comments[0].message).toBe('1. first comment');
expect(post.comments[1].message).toBe('2. second comment');
expect(post.comments[2].message).toBe('3. third comment');
});
await fakeCommentService.cleanup();
await fakePostService.cleanup();
});

});
Loading

0 comments on commit 724c5b6

Please sign in to comment.