refactor: config service
This commit is contained in:
53
test/helper/redis-mock.helper.ts
Normal file
53
test/helper/redis-mock.helper.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import IORedis from 'ioredis'
|
||||
import RedisMemoryServer from 'redis-memory-server'
|
||||
import { CacheKeys } from '~/constants/cache.constant'
|
||||
|
||||
export class MockCacheService {
|
||||
private client: IORedis.Redis
|
||||
constructor(port: number, host: string) {
|
||||
this.client = new IORedis(port, host)
|
||||
}
|
||||
|
||||
private get redisClient() {
|
||||
return this.client
|
||||
}
|
||||
|
||||
public get(key) {
|
||||
return this.client.get(key)
|
||||
}
|
||||
|
||||
public set(key, value: any) {
|
||||
return this.client.set(key, value)
|
||||
}
|
||||
|
||||
public getClient() {
|
||||
return this.redisClient
|
||||
}
|
||||
|
||||
public clearAggregateCache() {
|
||||
return Promise.all([
|
||||
this.redisClient.del(CacheKeys.RSS),
|
||||
this.redisClient.del(CacheKeys.RSSXmlCatch),
|
||||
this.redisClient.del(CacheKeys.AggregateCatch),
|
||||
this.redisClient.del(CacheKeys.SiteMapCatch),
|
||||
this.redisClient.del(CacheKeys.SiteMapXmlCatch),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export const createMockRedis = async () => {
|
||||
const redisServer = new RedisMemoryServer()
|
||||
|
||||
const redisHost = await redisServer.getHost()
|
||||
const redisPort = await redisServer.getPort()
|
||||
|
||||
const service = new MockCacheService(redisPort, redisHost)
|
||||
|
||||
return {
|
||||
service,
|
||||
async close() {
|
||||
await service.getClient().quit()
|
||||
await redisServer.stop()
|
||||
},
|
||||
}
|
||||
}
|
||||
132
test/src/modules/configs/configs.service.spec.ts
Normal file
132
test/src/modules/configs/configs.service.spec.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { BadRequestException } from '@nestjs/common'
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter'
|
||||
import { Test } from '@nestjs/testing'
|
||||
import { getModelForClass } from '@typegoose/typegoose'
|
||||
import { getModelToken } from 'nestjs-typegoose'
|
||||
import { dbHelper } from 'test/helper/db-mock.helper'
|
||||
import {
|
||||
createMockRedis,
|
||||
MockCacheService,
|
||||
} from 'test/helper/redis-mock.helper'
|
||||
import { RedisKeys } from '~/constants/cache.constant'
|
||||
import { OptionModel } from '~/modules/configs/configs.model'
|
||||
import { ConfigsService } from '~/modules/configs/configs.service'
|
||||
import { UserService } from '~/modules/user/user.service'
|
||||
import { CacheService } from '~/processors/cache/cache.service'
|
||||
import { getRedisKey } from '~/utils/redis.util'
|
||||
|
||||
describe('Test ConfigsService', () => {
|
||||
let service: ConfigsService
|
||||
let closeRedis: any
|
||||
let redisService: MockCacheService
|
||||
afterAll(async () => {
|
||||
await dbHelper.clear()
|
||||
await dbHelper.close()
|
||||
await closeRedis()
|
||||
})
|
||||
const optionModel = getModelForClass(OptionModel)
|
||||
const mockEmitFn = jest.fn()
|
||||
beforeAll(async () => {
|
||||
const { service: redisService$, close } = await createMockRedis()
|
||||
closeRedis = close
|
||||
redisService = redisService$
|
||||
await dbHelper.connect()
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
ConfigsService,
|
||||
{
|
||||
provide: getModelToken(OptionModel.name),
|
||||
useValue: optionModel,
|
||||
},
|
||||
{ provide: UserService, useValue: {} },
|
||||
{
|
||||
provide: CacheService,
|
||||
useValue: redisService$,
|
||||
},
|
||||
{ provide: EventEmitter2, useValue: { emit: mockEmitFn } },
|
||||
],
|
||||
}).compile()
|
||||
|
||||
service = moduleRef.get<ConfigsService>(ConfigsService)
|
||||
})
|
||||
|
||||
test('first get config should equal default config', async () => {
|
||||
const config = await service.getConfig()
|
||||
|
||||
expect(config).toBeDefined()
|
||||
expect(config).toStrictEqual(service.defaultConfig)
|
||||
})
|
||||
|
||||
describe('patch config should apply change between db and redis', () => {
|
||||
it('should update config', async () => {
|
||||
const updated = await service.patch('seo', {
|
||||
keywords: ['foo', 'bar'],
|
||||
})
|
||||
expect(updated).toBeDefined()
|
||||
expect(updated).toStrictEqual({
|
||||
...service.defaultConfig.seo,
|
||||
keywords: ['foo', 'bar'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should update redis', async () => {
|
||||
const redis = redisService.getClient()
|
||||
const dataStr = await redis.get(getRedisKey(RedisKeys.ConfigCache))
|
||||
const data = JSON.parse(dataStr)
|
||||
expect(data).toBeDefined()
|
||||
expect(data.seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||
})
|
||||
|
||||
it('should update db', async () => {
|
||||
const seo = (await optionModel.findOne({ name: 'seo' })).value
|
||||
expect(seo).toBeDefined()
|
||||
expect(seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||
})
|
||||
|
||||
it('should get updated config via `get()`', async () => {
|
||||
const seo = await service.get('seo')
|
||||
expect(seo).toBeDefined()
|
||||
expect(seo.keywords).toStrictEqual(['foo', 'bar'])
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw error if set a wrong type of config value', async () => {
|
||||
await expect(
|
||||
service.patchAndValid('seo', { title: true } as any),
|
||||
).rejects.toThrow(BadRequestException)
|
||||
})
|
||||
|
||||
it('should emit event if enable email option and update search', async () => {
|
||||
await service.patchAndValid('mailOptions', { enable: true })
|
||||
expect(mockEmitFn).toBeCalledTimes(1)
|
||||
mockEmitFn.mockClear()
|
||||
|
||||
await service.patchAndValid('mailOptions', { pass: '*' })
|
||||
expect(mockEmitFn).toBeCalledTimes(1)
|
||||
mockEmitFn.mockClear()
|
||||
|
||||
await service.patchAndValid('mailOptions', { pass: '*', enable: false })
|
||||
expect(mockEmitFn).toBeCalledTimes(0)
|
||||
mockEmitFn.mockClear()
|
||||
|
||||
await service.patchAndValid('algoliaSearchOptions', {
|
||||
enable: true,
|
||||
})
|
||||
expect(mockEmitFn).toBeCalledTimes(1)
|
||||
mockEmitFn.mockClear()
|
||||
|
||||
await service.patchAndValid('algoliaSearchOptions', {
|
||||
indexName: 'x',
|
||||
})
|
||||
expect(mockEmitFn).toBeCalledTimes(1)
|
||||
mockEmitFn.mockClear()
|
||||
|
||||
await service.patchAndValid('algoliaSearchOptions', {
|
||||
enable: false,
|
||||
})
|
||||
expect(mockEmitFn).toBeCalledTimes(0)
|
||||
mockEmitFn.mockClear()
|
||||
})
|
||||
})
|
||||
@@ -2,19 +2,24 @@ import { NestFastifyApplication } from '@nestjs/platform-fastify'
|
||||
import { Test } from '@nestjs/testing'
|
||||
import { getModelForClass } from '@typegoose/typegoose'
|
||||
import { getModelToken } from 'nestjs-typegoose'
|
||||
import { dbHelper } from 'test/helper/db-mock.helper'
|
||||
import { setupE2EApp } from 'test/helper/register-app.helper'
|
||||
import { firstKeyOfMap } from 'test/helper/utils.helper'
|
||||
import { SnippetController } from '~/modules/snippet/snippet.controller'
|
||||
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
||||
import { SnippetService } from '~/modules/snippet/snippet.service'
|
||||
|
||||
const mockingoose = require('mockingoose')
|
||||
|
||||
describe.only('test /snippets', () => {
|
||||
describe('test /snippets', () => {
|
||||
let app: NestFastifyApplication
|
||||
const model = getModelForClass(SnippetModel)
|
||||
|
||||
const mockTable = new Map()
|
||||
beforeAll(async () => {
|
||||
await dbHelper.connect()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await dbHelper.clear()
|
||||
await dbHelper.close()
|
||||
})
|
||||
const model = getModelForClass(SnippetModel)
|
||||
|
||||
const mockPayload1: Partial<SnippetModel> = Object.freeze({
|
||||
name: 'Snippet_1',
|
||||
@@ -38,27 +43,6 @@ describe.only('test /snippets', () => {
|
||||
app = await setupE2EApp(ref)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
mockingoose(model).toReturn(
|
||||
{
|
||||
...mockPayload1,
|
||||
_id: '61dfc5e1db3c871756fa5f9c',
|
||||
},
|
||||
'findOne',
|
||||
)
|
||||
mockingoose(model).toReturn(
|
||||
{
|
||||
...mockPayload1,
|
||||
_id: '61dfc5e1db3c871756fa5f9c',
|
||||
},
|
||||
'countDocuments',
|
||||
)
|
||||
mockTable.set('61dfc5e1db3c871756fa5f9c', {
|
||||
...mockPayload1,
|
||||
_id: '121212',
|
||||
})
|
||||
})
|
||||
|
||||
test('POST /snippets, should 422 with wrong name', async () => {
|
||||
await app
|
||||
.inject({
|
||||
@@ -76,6 +60,19 @@ describe.only('test /snippets', () => {
|
||||
expect(res.statusCode).toBe(422)
|
||||
})
|
||||
})
|
||||
let id: string
|
||||
test('POST /snippets, should create successfully', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/snippets',
|
||||
payload: mockPayload1,
|
||||
})
|
||||
expect(res.statusCode).toBe(201)
|
||||
const data = await res.json()
|
||||
expect(data.name).toEqual(mockPayload1.name)
|
||||
expect(data.id).toBeDefined()
|
||||
id = data.id
|
||||
})
|
||||
|
||||
test('POST /snippets, re-create same of name should return 400', async () => {
|
||||
await app
|
||||
@@ -98,7 +95,7 @@ describe.only('test /snippets', () => {
|
||||
await app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/snippets/' + firstKeyOfMap(mockTable),
|
||||
url: '/snippets/' + id,
|
||||
})
|
||||
.then((res) => {
|
||||
const json = res.json()
|
||||
|
||||
@@ -6,7 +6,7 @@ import { dbHelper } from 'test/helper/db-mock.helper'
|
||||
import { SnippetModel, SnippetType } from '~/modules/snippet/snippet.model'
|
||||
import { SnippetService } from '~/modules/snippet/snippet.service'
|
||||
|
||||
describe.only('test Snippet Service', () => {
|
||||
describe('test Snippet Service', () => {
|
||||
let service: SnippetService
|
||||
|
||||
beforeAll(async () => {
|
||||
|
||||
Reference in New Issue
Block a user