Skip to content

Commit

Permalink
feat(misc): function scan examples (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmstss authored Jul 15, 2024
1 parent 925979d commit 7d26edf
Show file tree
Hide file tree
Showing 18 changed files with 1,176 additions and 145 deletions.
645 changes: 509 additions & 136 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --report-unused-disable-directives --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
Expand All @@ -71,7 +71,8 @@
"@nestjs/swagger": "^7.4.0",
"dotenv": "^16.4.5",
"swagger-ui-express": "^5.0.1",
"tslib": "~2.6.3"
"tslib": "~2.6.3",
"xml2js": "^0.6.2"
},
"devDependencies": {
"@commitlint/cli": "^19.3.0",
Expand All @@ -80,17 +81,17 @@
"@nestjs/cli": "^10.4.2",
"@nestjs/schematics": "^10.1.2",
"@nestjs/testing": "^10.3.10",
"@sectester/core": "^0.32.0",
"@sectester/repeater": "^0.32.0",
"@sectester/reporter": "^0.32.0",
"@sectester/runner": "^0.32.0",
"@sectester/scan": "^0.32.0",
"@sectester/core": "^0.33.1",
"@sectester/repeater": "^0.33.1",
"@sectester/reporter": "^0.33.1",
"@sectester/runner": "^0.33.1",
"@sectester/scan": "^0.33.1",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/node": "^18.19.39",
"@types/supertest": "^6.0.2",
"@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.16.0",
"detect-port": "^1.6.1",
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { UsersModule } from './users';
import { MikroOrmConfigFactory } from './config';
import { MiscModule } from './misc';
import { Module } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [
UsersModule,
MiscModule,
ConfigModule.forRoot(),
MikroOrmModule.forRootAsync({
useClass: MikroOrmConfigFactory
Expand Down
10 changes: 9 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { AppModule } from './app.module';
import { NestFactory } from '@nestjs/core';
import { logger } from '@mikro-orm/nestjs';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';

const bootstrap = async () => {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
rawBody: true
});

app.enableShutdownHooks();

app.useBodyParser('text', {
type: ['text/xml', 'application/xml'],
limit: '1mb'
});

const config = new DocumentBuilder()
.setTitle('SecTester JS Demo')
.setDescription(
Expand Down
1 change: 1 addition & 0 deletions src/misc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MiscModule } from './misc.module';
108 changes: 108 additions & 0 deletions src/misc/misc.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { MiscController } from './misc.controller';
import { DateService, FileService, XmlService } from './services';
import { Test, TestingModule } from '@nestjs/testing';
import { RawBodyRequest } from '@nestjs/common';
import { IncomingMessage } from 'http';

describe('MiscController', () => {
let miscController: MiscController;
let dateService: jest.Mocked<DateService>;
let fileService: jest.Mocked<FileService>;
let xmlService: jest.Mocked<XmlService>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [MiscController],
providers: [
{
provide: DateService,
useValue: {
calculateWeekdays: jest.fn()
}
},
{
provide: FileService,
useValue: {
fetch: jest.fn()
}
},
{
provide: XmlService,
useValue: {
parse: jest.fn()
}
}
]
}).compile();

miscController = module.get<MiscController>(MiscController);
dateService = module.get(DateService);
fileService = module.get(FileService);
xmlService = module.get(XmlService);
});

it('should be defined', () => expect(miscController).toBeDefined());

describe('fetch', () => {
it('should call fileService fetch() with the provided URL', async () => {
const url = 'https://example.com';
const expectedResult = 'fetched content';
fileService.fetch.mockResolvedValue(expectedResult);

const result = await miscController.fetch({ url });

expect(fileService.fetch).toHaveBeenCalledWith(url);
expect(result).toBe(expectedResult);
});
});

describe('parse', () => {
it('should call xmlService parse() with the request body', async () => {
const xmlBody = '<root><child>content</child></root>';
const expectedResult = 'parsed content';
xmlService.parse.mockResolvedValue(expectedResult);
const mockRequest = {
rawBody: Buffer.from(xmlBody)
} as RawBodyRequest<IncomingMessage>;

const result = await miscController.parse(mockRequest);

expect(xmlService.parse).toHaveBeenCalledWith(xmlBody);
expect(result).toBe(expectedResult);
});
});

describe('weekdays', () => {
it('should call dateService calculateWeekdays() and return JSON result', async () => {
const from = '2023-01-01';
const to = '2023-12-31';
const weekday = 1;
const count = 52;
dateService.calculateWeekdays.mockResolvedValue(count);

const result = await miscController.weekdays(
from,
to,
weekday.toString()
);

expect(dateService.calculateWeekdays).toHaveBeenCalledWith(
from,
to,
weekday
);
expect(result).toBe(JSON.stringify({ count }, null, 2));
});

it('should use default weekday 1 if not provided', async () => {
const from = '2023-01-01';
const to = '2023-12-31';
const count = 52;
dateService.calculateWeekdays.mockResolvedValue(count);

await miscController.weekdays(from, to);

expect(dateService.calculateWeekdays).toHaveBeenCalledWith(from, to, 1);
});
});
});
105 changes: 105 additions & 0 deletions src/misc/misc.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* eslint-disable max-classes-per-file */
import { DateService, FileService, XmlService } from './services';
import {
BadRequestException,
Body,
Controller,
Get,
Post,
Query,
RawBodyRequest,
Req
} from '@nestjs/common';
import {
ApiResponse,
ApiTags,
ApiBody,
ApiQuery,
ApiProperty
} from '@nestjs/swagger';
import { IncomingMessage } from 'http';

class FetchDto {
@ApiProperty({ description: 'URL to fetch content from' })
public url!: string;
}

class WeekdaysResponseDto {
@ApiProperty({ description: 'Number of weekdays in the given range' })
public count!: number;
}

@Controller('misc')
@ApiTags('misc')
export class MiscController {
constructor(
private readonly dateService: DateService,
private readonly fileService: FileService,
private readonly xmlService: XmlService
) {}

@Post('/fetch')
@ApiResponse({
status: 200,
description: 'Successfully fetched the content from the URL',
type: String
})
@ApiBody({ type: FetchDto })
public fetch(@Body() body: FetchDto): Promise<string> {
return this.fileService.fetch(body.url);
}

@Post('/xml')
@ApiResponse({
status: 200,
description: 'Successfully parsed XML',
type: Object
})
@ApiBody({ type: String, description: 'Raw XML string' })
public parse(@Req() req: RawBodyRequest<IncomingMessage>): Promise<string> {
if (!req.rawBody) {
throw new BadRequestException('Request body is required');
}

return this.xmlService.parse(req.rawBody.toString());
}

@Get('/weekdays')
@ApiResponse({
status: 200,
description:
'Successfully calculated number of given weekday in date range',
type: WeekdaysResponseDto
})
@ApiQuery({
name: 'from',
required: true,
type: String,
description: 'Start date (YYYY-MM-DD)'
})
@ApiQuery({
name: 'to',
required: true,
type: String,
description: 'End date (YYYY-MM-DD)'
})
@ApiQuery({
name: 'weekday',
required: false,
type: Number,
description: 'Weekday number (0-6, where 0 is Sunday)'
})
public async weekdays(
@Query('from') from: string,
@Query('to') to: string,
@Query('weekday') weekday?: string
): Promise<string> {
const count = await this.dateService.calculateWeekdays(
from,
to,
weekday ? +weekday : 1
);

return JSON.stringify({ count }, null, 2);
}
}
10 changes: 10 additions & 0 deletions src/misc/misc.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { MiscController } from './misc.controller';
import { DateService, FileService, XmlService } from './services';
import { Module } from '@nestjs/common';

@Module({
imports: [],
providers: [DateService, FileService, XmlService],
controllers: [MiscController]
})
export class MiscModule {}
79 changes: 79 additions & 0 deletions src/misc/services/date.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { DateService } from './date.service';
import { Test, TestingModule } from '@nestjs/testing';

describe('DateService', () => {
let service: DateService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [DateService]
}).compile();

service = module.get<DateService>(DateService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});

describe('calculateWeekdays', () => {
it('should calculate weekdays correctly', async () => {
const result = await service.calculateWeekdays(
'2024-07-15',
'2024-07-15',
1
);

expect(result).toBe(1);
});

it('should handle different weekdays', async () => {
const result = await service.calculateWeekdays(
'2024-07-14',
'2024-07-15',
0
);

expect(result).toBe(1);
});

it('should use default weekday if not provided', async () => {
const result = await service.calculateWeekdays(
'2024-07-01',
'2024-07-31'
);

expect(result).toBe(5);
});

it('should handle common years correctly', async () => {
const result = await service.calculateWeekdays(
'2023-01-01',
'2023-12-31',
3
);

expect(result).toBe(52);
});

it('should handle leap years correctly', async () => {
const result = await service.calculateWeekdays(
'2024-01-01',
'2024-12-31',
2
);

expect(result).toBe(53);
});

it('should return 0 if end date is before start date', async () => {
const result = await service.calculateWeekdays(
'2023-07-31',
'2023-07-01',
1
);

expect(result).toBe(0);
});
});
});
Loading

0 comments on commit 7d26edf

Please sign in to comment.