refactor: config service

This commit is contained in:
Innei
2022-01-15 22:15:28 +08:00
parent 286a82b3d7
commit 70da42f8b8
16 changed files with 420 additions and 142 deletions

View 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()
},
}
}

View 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()
})
})

View File

@@ -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()

View File

@@ -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 () => {