Skip to content

Commit

Permalink
Merge pull request #448 from jiho-kr/feature/listeners
Browse files Browse the repository at this point in the history
  • Loading branch information
jiho-kr authored Feb 11, 2024
2 parents 9720777 + 1735341 commit 7a9174b
Show file tree
Hide file tree
Showing 12 changed files with 412 additions and 21 deletions.
167 changes: 167 additions & 0 deletions spec/listeners/allow-listeners.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/* eslint-disable max-classes-per-file */
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Controller, Injectable, Module } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { InjectRepository, TypeOrmModule } from '@nestjs/typeorm';
import { IsOptional } from 'class-validator';
import request from 'supertest';
import {
Entity,
BaseEntity,
Repository,
PrimaryColumn,
DeleteDateColumn,
CreateDateColumn,
UpdateDateColumn,
Column,
BeforeRemove,
BeforeInsert,
BeforeUpdate,
BeforeSoftRemove,
BeforeRecover,
} from 'typeorm';

import { Crud } from '../../src/lib/crud.decorator';
import { CrudService } from '../../src/lib/crud.service';
import { CrudController } from '../../src/lib/interface';
import { TestHelper } from '../test.helper';

@Entity('listeners-on')
class TestEntity extends BaseEntity {
@PrimaryColumn()
@IsOptional({ always: true })
col1: number;

@Column({ nullable: true })
@IsOptional({ always: true })
col2: string;

@Column({ nullable: true })
status: string;

@CreateDateColumn()
createdAt?: Date;

@UpdateDateColumn()
updatedAt?: Date;

@DeleteDateColumn()
deletedAt?: Date;

@BeforeInsert()
beforeInsert() {
this.status = 'created';
}

@BeforeUpdate()
beforeUpdate() {
this.status = 'updated';
}

@BeforeRemove()
beforeRemove() {
this.status = 'removed';
}

@BeforeSoftRemove()
beforeSoftRemove() {
this.status = 'softRemoved';
}

@BeforeRecover()
beforeRecover() {
this.status = 'recovered';
}
}

@Injectable()
class TestService extends CrudService<TestEntity> {
constructor(@InjectRepository(TestEntity) repository: Repository<TestEntity>) {
super(repository);
}
}

@Crud({
entity: TestEntity,
})
@Controller('base')
class TestController implements CrudController<TestEntity> {
constructor(public readonly crudService: TestService) {}
}

@Module({
imports: [TypeOrmModule.forFeature([TestEntity])],
controllers: [TestController],
providers: [TestService],
})
class TestModule {}

describe('allow Listeners', () => {
let app: INestApplication;

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [TestModule, TestHelper.getTypeOrmPgsqlModule([TestEntity])],
}).compile();
app = moduleFixture.createNestApplication();
await TestEntity.delete({});
await app.init();
});

afterAll(async () => {
await app?.close();
});

it('should be changed status by trigging', async () => {
const { body: createdBody } = await request(app.getHttpServer())
.post('/base')
.send({ col1: 1, col2: 'created' })
.expect(HttpStatus.CREATED);
expect(createdBody).toEqual({
col1: 1,
col2: 'created',
status: 'created',
createdAt: expect.any(String),
updatedAt: expect.any(String),
deletedAt: null,
});

const { body: updatedBody } = await request(app.getHttpServer())
.patch(`/base/${createdBody.col1}`)
.send({ col2: 'updated' })
.expect(HttpStatus.OK);
expect(updatedBody).toEqual({
col1: 1,
col2: 'updated',
status: 'updated',
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: null,
});
expect(updatedBody.updatedAt).not.toEqual(createdBody.updatedAt);

const { body: deletedBody } = await request(app.getHttpServer()).delete(`/base/${createdBody.col1}`).expect(HttpStatus.OK);
expect(deletedBody).toEqual({
col1: 1,
col2: 'updated',
status: 'softRemoved',
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
expect(deletedBody.updatedAt).not.toEqual(updatedBody.updatedAt);

const { body: recoverBody } = await request(app.getHttpServer())
.post(`/base/${createdBody.col1}/recover`)
.expect(HttpStatus.CREATED);
expect(recoverBody).toEqual({
col1: 1,
col2: 'updated',
status: 'recovered',
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: null,
});
expect(recoverBody.updatedAt).not.toEqual(deletedBody.updatedAt);
});
});
173 changes: 173 additions & 0 deletions spec/listeners/disallow-listeners.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* eslint-disable max-classes-per-file */
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Controller, Injectable, Module } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { InjectRepository, TypeOrmModule } from '@nestjs/typeorm';
import { IsOptional } from 'class-validator';
import request from 'supertest';
import {
Entity,
BaseEntity,
Repository,
PrimaryColumn,
DeleteDateColumn,
CreateDateColumn,
UpdateDateColumn,
Column,
BeforeRemove,
BeforeInsert,
BeforeUpdate,
BeforeSoftRemove,
BeforeRecover,
} from 'typeorm';

import { Crud } from '../../src/lib/crud.decorator';
import { CrudService } from '../../src/lib/crud.service';
import { CrudController } from '../../src/lib/interface';
import { TestHelper } from '../test.helper';

@Entity('listeners-off')
class TestEntity extends BaseEntity {
@PrimaryColumn()
@IsOptional({ always: true })
col1: number;

@Column({ nullable: true })
@IsOptional({ always: true })
col2: string;

@Column({ nullable: true })
status: string;

@CreateDateColumn()
createdAt?: Date;

@UpdateDateColumn()
updatedAt?: Date;

@DeleteDateColumn()
deletedAt?: Date;

@BeforeInsert()
beforeInsert() {
this.status = 'created';
}

@BeforeUpdate()
beforeUpdate() {
this.status = 'updated';
}

@BeforeRemove()
beforeRemove() {
this.status = 'removed';
}

@BeforeSoftRemove()
beforeSoftRemove() {
this.status = 'softRemoved';
}

@BeforeRecover()
beforeRecover() {
this.status = 'recovered';
}
}

@Injectable()
class TestService extends CrudService<TestEntity> {
constructor(@InjectRepository(TestEntity) repository: Repository<TestEntity>) {
super(repository);
}
}

@Crud({
entity: TestEntity,
routes: {
create: { listeners: false },
update: { listeners: false },
delete: { listeners: false },
upsert: { listeners: false },
recover: { listeners: false },
},
})
@Controller('base')
class TestController implements CrudController<TestEntity> {
constructor(public readonly crudService: TestService) {}
}

@Module({
imports: [TypeOrmModule.forFeature([TestEntity])],
controllers: [TestController],
providers: [TestService],
})
class TestModule {}

describe('disallow Listeners', () => {
let app: INestApplication;

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [TestModule, TestHelper.getTypeOrmPgsqlModule([TestEntity])],
}).compile();
app = moduleFixture.createNestApplication();
await TestEntity.delete({});
await app.init();
});

afterAll(async () => {
await app?.close();
});

it('should not be changed status by trigging', async () => {
const { body: createdBody } = await request(app.getHttpServer())
.post('/base')
.send({ col1: 1, col2: 'created' })
.expect(HttpStatus.CREATED);
expect(createdBody).toEqual({
col1: 1,
col2: 'created',
status: null,
createdAt: expect.any(String),
updatedAt: expect.any(String),
deletedAt: null,
});

const { body: updatedBody } = await request(app.getHttpServer())
.patch(`/base/${createdBody.col1}`)
.send({ col2: 'updated' })
.expect(HttpStatus.OK);
expect(updatedBody).toEqual({
col1: 1,
col2: 'updated',
status: null,
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: null,
});
expect(updatedBody.updatedAt).not.toEqual(createdBody.updatedAt);
const { body: deletedBody } = await request(app.getHttpServer()).delete(`/base/${createdBody.col1}`).expect(HttpStatus.OK);
expect(deletedBody).toEqual({
col1: 1,
col2: 'updated',
status: null,
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
expect(deletedBody.updatedAt).not.toEqual(updatedBody.updatedAt);

const { body: recoverBody } = await request(app.getHttpServer())
.post(`/base/${createdBody.col1}/recover`)
.expect(HttpStatus.CREATED);
expect(recoverBody).toEqual({
col1: 1,
col2: 'updated',
status: null,
createdAt: updatedBody.createdAt,
updatedAt: expect.any(String),
deletedAt: null,
});
expect(recoverBody.updatedAt).not.toEqual(deletedBody.updatedAt);
});
});
6 changes: 5 additions & 1 deletion spec/logging/logging.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('Logging', () => {
await request(app.getHttpServer()).post('/base').send({
col1: 1,
});
expect(loggerSpy).toHaveBeenNthCalledWith(1, { body: { col1: 1 }, exclude: new Set() }, 'CRUD POST /base');
expect(loggerSpy).toHaveBeenNthCalledWith(1, { body: { col1: 1 }, exclude: new Set(), saveOptions: {} }, 'CRUD POST /base');

await request(app.getHttpServer()).get('/base');
expect(loggerSpy).toHaveBeenNthCalledWith(
Expand Down Expand Up @@ -119,6 +119,7 @@ describe('Logging', () => {
col2: 'test',
},
exclude: new Set(),
saveOptions: {},
},
'CRUD PATCH /base/1',
);
Expand All @@ -132,6 +133,7 @@ describe('Logging', () => {
},
body: {},
exclude: new Set(),
saveOptions: {},
},
'CRUD PUT /base/2',
);
Expand All @@ -145,6 +147,7 @@ describe('Logging', () => {
},
softDeleted: true,
exclude: new Set(),
saveOptions: {},
},
'CRUD DELETE /base/1',
);
Expand All @@ -157,6 +160,7 @@ describe('Logging', () => {
col1: '1',
},
exclude: new Set(),
saveOptions: {},
},
'CRUD POST /base/1/recover',
);
Expand Down
1 change: 1 addition & 0 deletions src/lib/crud.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('CrudService', () => {
params: {},
softDeleted: false,
exclude: new Set(),
saveOptions: {},
}),
).rejects.toThrow(ConflictException);
});
Expand Down
Loading

0 comments on commit 7a9174b

Please sign in to comment.