Skip to content

Commit

Permalink
Merge pull request #5 from Mostaqem/dev
Browse files Browse the repository at this point in the history
chore: Add seeder and add unit test to the verse module and service  and test step to the ci
  • Loading branch information
the-sabra authored Jul 22, 2024
2 parents bf22f36 + f6e2e1b commit 45aa208
Show file tree
Hide file tree
Showing 12 changed files with 22,386 additions and 136 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ jobs:

- name: lint
run: pnpm run lint

- name: test
run: pnpm run test
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.6",
"mysql2": "^3.10.2",
"mysql2": "^3.10.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20",
Expand All @@ -43,24 +43,27 @@
"@types/cookie-parser": "^1.4.7",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.10",
"@types/node": "^20.14.11",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"source-map-support": "^0.5.21",
"supertest": "^6.3.4",
"ts-jest": "^29.2.2",
"ts-jest": "^29.2.3",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.3"
},
"jest": {
"moduleNameMapper": {
"src/(.*)": "<rootDir>/$1"
},
"moduleFileExtensions": [
"js",
"json",
Expand Down
1,035 changes: 906 additions & 129 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

21,241 changes: 21,241 additions & 0 deletions quran.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { VerseModule } from './verse/verse.module';
import { ReciterModule } from './reciter/reciter.module';
import { AudioModule } from './audio/audio.module';
import { ImageModule } from './image/image.module';
import { SurahService } from './surah/surah.service';
import { VerseService } from './verse/verse.service';

@Module({
imports: [
Expand Down Expand Up @@ -34,7 +36,17 @@ import { ImageModule } from './image/image.module';
],
})
export class AppModule {
constructor(
private readonly surahService: SurahService,
private readonly verseService: VerseService,
) {}

configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}

async onModuleInit() {
await this.surahService.initializeSurah();
this.verseService.initialVerses();
}
}
3 changes: 2 additions & 1 deletion src/middleware/logger.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ export class LoggerMiddleware implements NestMiddleware {

use(req: Request, res: Response, next: NextFunction) {
const startAt = process.hrtime();
const { ip, method, originalUrl } = req;
const { method, originalUrl } = req;

const userAgent = req.get('user-agent') || '';
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;

res.on('finish', () => {
const { statusCode } = res;
Expand Down
1 change: 1 addition & 0 deletions src/surah/surah.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import { Surah } from './entities/surah.entity';
imports: [TypeOrmModule.forFeature([Surah])],
controllers: [SurahController],
providers: [SurahService],
exports: [SurahService],
})
export class SurahModule {}
20 changes: 20 additions & 0 deletions src/surah/surah.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Injectable,
InternalServerErrorException,
Logger,
NotFoundException,
} from '@nestjs/common';
import { CreateSurahDto } from './dto/create-surah.dto';
Expand Down Expand Up @@ -40,4 +41,23 @@ export class SurahService {
surah.image = image;
return await this.surahRepository.save(surah);
}

async initializeSurah() {
const surah = await this.surahRepository.find();
if (surah.length != 114) {
const data = await require('../../quran.json');
const seedPromises = data.map(async (surah: any) => {
const newSurah = this.surahRepository.create({
id: surah.id,
name_arabic: surah.name,
name_complex: surah.transliteration,
verses_count: surah.total_verses,
revelation_place: surah.type,
});
return this.surahRepository.save(newSurah);
});
await Promise.all(seedPromises);
Logger.log('Surah Seeder Completed');
}
}
}
2 changes: 1 addition & 1 deletion src/verse/dto/create-verse.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IsString, IsNotEmpty, IsInt, Min, MaxLength } from 'class-validator';

export class CreateVerseDto {
id: number;
id?: number;

@IsString()
@IsNotEmpty()
Expand Down
1 change: 1 addition & 0 deletions src/verse/verse.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import { Verse } from './entities/verse.entity';
imports: [TypeOrmModule.forFeature([Verse])],
controllers: [VerseController],
providers: [VerseService],
exports: [VerseService],
})
export class VerseModule {}
164 changes: 164 additions & 0 deletions src/verse/verse.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { VerseService } from './verse.service';
import { Verse } from './entities/verse.entity';
import { CreateVerseDto } from './dto/create-verse.dto';
import { Repository } from 'typeorm';
import { InternalServerErrorException, Logger } from '@nestjs/common';

jest.mock('../../quran.json', () => [
{
id: 1,
verses: [
{
id: 1,
text: 'In the name of Allah, the Most Merciful, the Most Compassionate.',
},
{ id: 2, text: 'Praise be to Allah, the Lord of all the worlds.' },
],
},
]);

describe('VerseService', () => {
let verseService: VerseService;
let verseRepository: Repository<Verse>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
VerseService,
{
provide: getRepositoryToken(Verse),
useClass: Repository,
},
],
}).compile();

verseService = module.get<VerseService>(VerseService);
verseRepository = module.get<Repository<Verse>>(getRepositoryToken(Verse));
});

describe('create', () => {
it('should create a verse', async () => {
const createVerseDto: CreateVerseDto = {
vers: 'verse',
verse_number: 1,
vers_lang: 'eng',
surah_id: 1,
};

const createSpy = jest
.spyOn(verseRepository, 'create')
.mockReturnValue(createVerseDto as unknown as Verse);

const saveSpy = jest
.spyOn(verseRepository, 'save')
.mockResolvedValueOnce({} as Verse);

const result = await verseService.create(createVerseDto);

expect(createSpy).toHaveBeenCalledWith(createVerseDto);
expect(saveSpy).toHaveBeenCalledWith(createVerseDto);
expect(result).toEqual({} as Verse);
});

it('should throw an InternalServerErrorException if save fails', async () => {
const createVerseDto: CreateVerseDto = {
vers: 'verse',
verse_number: 1,
vers_lang: 'eng',
surah_id: 1,
};

jest
.spyOn(verseRepository, 'create')
.mockReturnValue(createVerseDto as unknown as Verse);

jest
.spyOn(verseRepository, 'save')
.mockRejectedValueOnce(new InternalServerErrorException());

await expect(verseService.create(createVerseDto)).rejects.toThrow(
InternalServerErrorException,
);
});
});

describe('getSurahVerses', () => {
it('should return verses of a surah', async () => {
const surahId = 1;
const verses: Verse[] = [];

jest.spyOn(verseRepository, 'find').mockResolvedValueOnce(verses);

const result = await verseService.getSurahVerses(surahId);

expect(verseRepository.find).toHaveBeenCalledWith({
where: { surah_id: surahId },
});

expect(result).toEqual({
verses,
totalVerseNumber: verses.length,
});
});
});

describe('initialVerses', () => {
it('should seed verses if verses table is empty', async () => {
const verses: Verse[] = [];
const findSpy = jest
.spyOn(verseRepository, 'find')
.mockResolvedValueOnce(verses);
const createSpy = jest
.spyOn(verseRepository, 'create')
.mockResolvedValue({
id: 1,
vers: 'verse',
verse_number: 1,
vers_lang: 'eng',
surah_id: 1,
} as never);

const saveSpy = jest
.spyOn(verseRepository, 'save')
.mockResolvedValue({} as never);
const logSpy = jest
.spyOn(Logger, 'log')
.mockResolvedValueOnce('Verse Seeder Completed' as never);

await verseService.initialVerses();

expect(findSpy).toHaveBeenCalled();
expect(createSpy).toHaveBeenCalledTimes(2);
expect(saveSpy).toHaveBeenCalledTimes(2);
expect(logSpy).toHaveBeenCalledWith('Verse Seeder Completed');
});

it('should not seed verses if verses table is not empty', async () => {
const verses: Verse[] = [
{
id: 1,
vers: 'verse',
verse_number: 1,
vers_lang: 'eng',
surah_id: 1,
} as Verse,
];

const createSpy = jest.spyOn(verseRepository, 'create');
const saveSpy = jest.spyOn(verseRepository, 'save');
// const logSpy = jest.spyOn(Logger, 'log');
const findSpy = jest
.spyOn(verseRepository, 'find')
.mockResolvedValueOnce(verses as any);

await verseService.initialVerses();

expect(findSpy).toHaveBeenCalled();
expect(createSpy).not.toHaveBeenCalled();
expect(saveSpy).not.toHaveBeenCalled();
// expect(logSpy).not.toHaveBeenCalled();
});
});
});
29 changes: 28 additions & 1 deletion src/verse/verse.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import {
Injectable,
InternalServerErrorException,
Logger,
} from '@nestjs/common';
import { CreateVerseDto } from './dto/create-verse.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Verse } from './entities/verse.entity';
Expand Down Expand Up @@ -30,4 +34,27 @@ export class VerseService {
};
return result;
}

async initialVerses() {
const verses = await this.verseSRepository.find();
const data = await require('../../quran.json');
console.log(verses.length == 0);
if (verses.length === 0) {
for (let i = 0; i < data.length; i++) {
const surah = data[i];
for (let j = 0; j < surah.verses.length; j++) {
const verse = surah.verses[j];
const newVerse = this.verseSRepository.create({
surah_id: surah.id,
verse_number: verse.id,
vers: verse.text,
vers_lang: 'ar',
});
await this.verseSRepository.save(newVerse);
}
}
Logger.log('Verse Seeder Completed');
}
return;
}
}

0 comments on commit 45aa208

Please sign in to comment.