[Feature]: 补充file模块的单测 (#102)

* feat: 补充file模块的单测

* fix: 安装types依赖
This commit is contained in:
luch 2024-04-30 10:15:48 +08:00 committed by sudoooooo
parent 91838194bb
commit 00d5e98712
21 changed files with 603 additions and 111 deletions

4
server/.gitignore vendored
View File

@ -35,4 +35,6 @@ lerna-debug.log*
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
tmp

View File

@ -51,8 +51,12 @@
"@nestjs/cli": "^10.0.0", "@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0", "@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0", "@nestjs/testing": "^10.0.0",
"@types/ali-oss": "^6.16.11",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/jsonwebtoken": "^9.0.6",
"@types/lodash": "^4.17.0",
"@types/multer": "^1.4.11", "@types/multer": "^1.4.11",
"@types/node": "^20.3.1", "@types/node": "^20.3.1",
"@types/node-forge": "^1.3.11", "@types/node-forge": "^1.3.11",

View File

@ -1,16 +1,15 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { Authtication } from './authtication';
import { UserService } from '../modules/auth/services/user.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { AuthenticationException } from '../exceptions/authException'; import { Authtication } from './authtication';
import { AuthService } from 'src/modules/auth/services/auth.service';
import { AuthenticationException } from 'src/exceptions/authException';
import { User } from 'src/models/user.entity'; import { User } from 'src/models/user.entity';
import * as jwt from 'jsonwebtoken';
jest.mock('jsonwebtoken'); jest.mock('jsonwebtoken');
describe('Authtication', () => { describe('Authtication', () => {
let guard: Authtication; let guard: Authtication;
let userService: UserService; let authService: AuthService;
let configService: ConfigService; let configService: ConfigService;
beforeEach(async () => { beforeEach(async () => {
@ -18,9 +17,9 @@ describe('Authtication', () => {
providers: [ providers: [
Authtication, Authtication,
{ {
provide: UserService, provide: AuthService,
useValue: { useValue: {
getUserByUsername: jest.fn(), verifyToken: jest.fn(),
}, },
}, },
{ {
@ -33,7 +32,7 @@ describe('Authtication', () => {
}).compile(); }).compile();
guard = module.get<Authtication>(Authtication); guard = module.get<Authtication>(Authtication);
userService = module.get<UserService>(UserService); authService = module.get<AuthService>(AuthService);
configService = module.get<ConfigService>(ConfigService); configService = module.get<ConfigService>(ConfigService);
}); });
@ -62,7 +61,9 @@ describe('Authtication', () => {
}), }),
}; };
jest.spyOn(jwt, 'verify').mockReturnValue(new Error('token is invalid')); jest
.spyOn(authService, 'verifyToken')
.mockRejectedValue(new Error('token is invalid'));
jest jest
.spyOn(configService, 'get') .spyOn(configService, 'get')
@ -73,31 +74,6 @@ describe('Authtication', () => {
); );
}); });
it('should throw exception if user does not exist', async () => {
const context = {
switchToHttp: () => ({
getRequest: () => ({
headers: {
authorization: 'Bearer validToken',
},
}),
}),
};
const fakeUser = { username: 'testUser' } as User;
jest.spyOn(jwt, 'verify').mockReturnValue(fakeUser);
jest
.spyOn(configService, 'get')
.mockReturnValue('XIAOJU_SURVEY_JWT_SECRET');
jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(null);
await expect(guard.canActivate(context as any)).rejects.toThrow(
AuthenticationException,
);
});
it('should set user in request object and return true if user exists', async () => { it('should set user in request object and return true if user exists', async () => {
const request = { const request = {
headers: { headers: {
@ -114,9 +90,7 @@ describe('Authtication', () => {
jest jest
.spyOn(configService, 'get') .spyOn(configService, 'get')
.mockReturnValue('XIAOJU_SURVEY_JWT_SECRET'); .mockReturnValue('XIAOJU_SURVEY_JWT_SECRET');
jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(fakeUser); jest.spyOn(authService, 'verifyToken').mockResolvedValue(fakeUser);
jest.spyOn(jwt, 'verify').mockReturnValue(fakeUser);
const result = await guard.canActivate(context as any); const result = await guard.canActivate(context as any);

View File

@ -1,14 +1,10 @@
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { UserService } from '../modules/auth/services/user.service';
import { AuthenticationException } from '../exceptions/authException'; import { AuthenticationException } from '../exceptions/authException';
import { AuthService } from 'src/modules/auth/services/auth.service'; import { AuthService } from 'src/modules/auth/services/auth.service';
@Injectable() @Injectable()
export class Authtication implements CanActivate { export class Authtication implements CanActivate {
constructor( constructor(private readonly authService: AuthService) {}
private readonly userService: UserService,
private readonly authService: AuthService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();

View File

@ -1,18 +1,33 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from '../services/auth.service'; import { ConfigModule } from '@nestjs/config';
import { sign } from 'jsonwebtoken'; import * as jwt from 'jsonwebtoken';
import { User } from 'src/models/user.entity';
jest.mock('jsonwebtoken'); import { AuthService } from '../services/auth.service';
import { UserService } from '../services/user.service';
jest.mock('jsonwebtoken', () => {
return {
sign: jest.fn(),
verify: jest.fn().mockReturnValue({}),
};
});
describe('AuthService', () => { describe('AuthService', () => {
let service: AuthService; let service: AuthService;
let userService: UserService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [AuthService], imports: [ConfigModule],
providers: [
AuthService,
{ provide: UserService, useValue: { getUserByUsername: jest.fn() } },
],
}).compile(); }).compile();
service = module.get<AuthService>(AuthService); service = module.get<AuthService>(AuthService);
userService = module.get<UserService>(UserService);
}); });
describe('generateToken', () => { describe('generateToken', () => {
@ -25,9 +40,20 @@ describe('AuthService', () => {
await service.generateToken(userData, tokenConfig); await service.generateToken(userData, tokenConfig);
expect(sign).toHaveBeenCalledWith(userData, tokenConfig.secret, { expect(jwt.sign).toHaveBeenCalledWith(userData, tokenConfig.secret, {
expiresIn: tokenConfig.expiresIn, expiresIn: tokenConfig.expiresIn,
}); });
}); });
}); });
describe('verifyToken', () => {
it('should verifyToken succeed', async () => {
const token = 'mock token';
jest
.spyOn(userService, 'getUserByUsername')
.mockResolvedValue({} as User);
await service.verifyToken(token);
expect(userService.getUserByUsername).toHaveBeenCalledTimes(1);
});
});
}); });

View File

@ -0,0 +1,55 @@
import { AliOssHandler } from '../services/uploadHandlers/alioss.handler';
describe('AliOssHandler', () => {
describe('upload', () => {
it('should upload a file and return the key', async () => {
const file = {
originalname: 'mockFileName.txt',
buffer: Buffer.from('mockFileContent'),
} as Express.Multer.File;
const mockPutResult = { name: 'mockFileName.txt' };
const mockClient = {
put: jest.fn().mockResolvedValue(mockPutResult),
};
const handler = new AliOssHandler({
client: mockClient,
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = await handler.upload(file);
expect(result).toEqual({
key: expect.stringMatching(/\w+\.txt/),
});
});
});
describe('getUrl', () => {
it('should return the URL for the given key', () => {
const key = 'mockFilePath/mockFileName.txt';
const bucket = 'xiaojusurvey';
const accessKey = 'mockAccessKey';
const region = 'mockRegion';
const handler = new AliOssHandler({
accessKey,
secretKey: 'mockSecretKey',
bucket,
region,
endPoint: 'mockEndPoint',
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = handler.getUrl(key);
const keyReg = new RegExp(key);
const signatureReg = new RegExp('Signature=');
const expiredReg = new RegExp('Expires=');
expect(keyReg.test(result)).toBe(true);
expect(signatureReg.test(result)).toBe(true);
expect(expiredReg.test(result)).toBe(true);
});
});
});

View File

@ -0,0 +1,150 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FileController } from '../controllers/file.controller';
import { FileService } from '../services/file.service';
import { uploadConfig, channels } from '../config/index';
import { AuthenticationException } from 'src/exceptions/authException';
import { HttpException } from 'src/exceptions/httpException';
import { AuthService } from 'src/modules/auth/services/auth.service';
import { User } from 'src/models/user.entity';
describe('FileController', () => {
let controller: FileController;
let fileService: FileService;
let authService: AuthService;
// let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forFeature(() => {
return {
...channels,
...uploadConfig,
};
}),
],
controllers: [FileController],
providers: [
FileService,
{
provide: AuthService,
useValue: {
verifyToken: jest.fn(),
},
},
ConfigService,
],
}).compile();
controller = module.get<FileController>(FileController);
fileService = module.get<FileService>(FileService);
authService = module.get<AuthService>(AuthService);
// configService = module.get<ConfigService>(ConfigService);
});
describe('upload', () => {
it('should upload a file', async () => {
const file: Express.Multer.File = {
fieldname: '',
originalname: '',
encoding: '',
mimetype: '',
size: 0,
stream: null,
destination: '',
filename: '',
path: '',
buffer: null,
};
const req = { headers: { authorization: 'Bearer mockToken' } };
const reqBody = { channel: 'upload' };
jest.spyOn(authService, 'verifyToken').mockResolvedValueOnce({} as User);
const mockUploadResult = { key: 'mockKey', url: 'mockUrl' };
jest.spyOn(fileService, 'upload').mockResolvedValueOnce(mockUploadResult);
const result = await controller.upload(file, req, reqBody);
expect(result).toEqual({
code: 200,
data: {
url: mockUploadResult.url,
key: mockUploadResult.key,
},
});
});
it('should upload failed without token', async () => {
const file: Express.Multer.File = {
fieldname: '',
originalname: '',
encoding: '',
mimetype: '',
size: 0,
stream: null,
destination: '',
filename: '',
path: '',
buffer: null,
};
const req = { headers: { authorization: '' } };
const reqBody = { channel: 'upload' };
await expect(controller.upload(file, req, reqBody)).rejects.toThrow(
AuthenticationException,
);
});
it('should upload failed', async () => {
const file: Express.Multer.File = {
fieldname: '',
originalname: '',
encoding: '',
mimetype: '',
size: 0,
stream: null,
destination: '',
filename: '',
path: '',
buffer: null,
};
const req = { headers: { authorization: 'Bearer mockToken' } };
const reqBody = { channel: 'mockChannel' };
await expect(controller.upload(file, req, reqBody)).rejects.toThrow(
HttpException,
);
});
});
describe('generateGetUrl', () => {
it('should generate a URL for the given channel and key', async () => {
const reqBody = { channel: 'upload', key: 'mockKey' };
const mockUrl = 'mockGeneratedUrl';
jest.spyOn(fileService, 'getUrl').mockReturnValueOnce(mockUrl);
const result = await controller.generateGetUrl(reqBody);
expect(result).toEqual({
code: 200,
data: {
key: reqBody.key,
url: mockUrl,
},
});
});
it('should generate a URL failed', async () => {
const reqBody = { channel: 'mockChannel', key: 'mockKey' };
await expect(controller.generateGetUrl(reqBody)).rejects.toThrow(
HttpException,
);
});
});
});

View File

@ -0,0 +1,65 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FileService } from '../services/file.service';
import { LocalHandler } from '../services/uploadHandlers/local.handler';
import { uploadConfig, channels } from '../config/index';
describe('FileService', () => {
let fileService: FileService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forFeature(() => {
return {
...channels,
...uploadConfig,
};
}),
],
providers: [FileService, ConfigService],
}).compile();
fileService = module.get<FileService>(FileService);
});
describe('upload', () => {
it('should upload a file and return key and URL', async () => {
const configKey = 'mockConfigKey';
const file = {} as Express.Multer.File;
const pathPrefix = 'mockPathPrefix';
const mockKey = 'mockKey';
jest
.spyOn(LocalHandler.prototype, 'upload')
.mockResolvedValueOnce({ key: mockKey });
const mockUrl = 'mockUrl';
jest
.spyOn(LocalHandler.prototype, 'getUrl')
.mockResolvedValueOnce(mockUrl as never);
const result = await fileService.upload({ configKey, file, pathPrefix });
expect(result).toEqual({
key: mockKey,
url: mockUrl,
});
});
});
describe('getUrl', () => {
it('should return URL for the given configKey and key', async () => {
const configKey = 'mockConfigKey';
const key = 'mockKey';
const mockUrl = 'mockUrl';
jest.spyOn(LocalHandler.prototype, 'getUrl').mockReturnValueOnce(mockUrl);
const result = fileService.getUrl({ configKey, key });
expect(result).toEqual(mockUrl);
});
});
});

View File

@ -0,0 +1,32 @@
import { join } from 'path';
import { LocalHandler } from '../services/uploadHandlers/local.handler';
describe('LocalHandler', () => {
describe('upload', () => {
it('should upload a file and return the key', async () => {
const file = {
originalname: `mockFileName.txt`,
buffer: Buffer.from('mockFileContent'),
} as Express.Multer.File;
const physicalRootPath = join(__dirname, 'tmp');
const handler = new LocalHandler({ physicalRootPath });
const result = await handler.upload(file);
expect(result).toEqual({
key: expect.stringMatching(/\w+\.txt/),
});
});
});
describe('getUrl', () => {
it('should return the URL for the given key', () => {
const key = 'mockFilePath/mockFileName.txt';
const physicalRootPath = join(__dirname, 'tmp');
const handler = new LocalHandler({ physicalRootPath });
const result = handler.getUrl(key);
expect(result).toBe(`/${key}`);
});
});
});

View File

@ -0,0 +1,75 @@
import { Client } from 'minio';
import { MinIOHandler } from '../services/uploadHandlers/minio.handler';
import { HttpException } from 'src/exceptions/httpException';
describe('MinIOHandler', () => {
describe('upload', () => {
it('should upload a file and return the key', async () => {
const file = {
originalname: 'mockFileName.txt',
buffer: Buffer.from('mockFileContent'),
} as Express.Multer.File;
const mockPutObjectFn = jest.fn().mockResolvedValueOnce({});
const mockClient = {
putObject: mockPutObjectFn,
};
const handler = new MinIOHandler({
client: mockClient as unknown as Client,
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = await handler.upload(file);
expect(result).toEqual({
key: expect.stringMatching('.txt'),
});
expect(mockPutObjectFn).toHaveBeenCalledTimes(1);
});
it('should throw an HttpException if upload fails', async () => {
const file = {
originalname: 'mockFileName.txt',
buffer: Buffer.from('mockFileContent'),
} as Express.Multer.File;
const mockPutObjectFn = jest
.fn()
.mockRejectedValueOnce(new Error('Upload failed'));
const mockClient = {
putObject: mockPutObjectFn,
};
const handler = new MinIOHandler({
client: mockClient as unknown as Client,
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
await expect(handler.upload(file)).rejects.toThrow(HttpException);
});
});
describe('getUrl', () => {
it('should return the URL for the given key', async () => {
const key = 'mockFilePath/mockFileName.txt';
const handler = new MinIOHandler({
accessKey: 'mockAccessKey',
secretKey: 'mockSecretKey',
bucket: 'xiaojusurvey',
region: 'mockRegion',
endPoint: 'mockEndPoint',
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = await handler.getUrl(key);
const keyReg = new RegExp(key);
const signatureReg = new RegExp('X-Amz-Signature=');
expect(keyReg.test(result)).toBe(true);
expect(signatureReg.test(result)).toBe(true);
});
});
});

View File

@ -0,0 +1,102 @@
// In your test file
import { QiniuHandler } from '../services/uploadHandlers/qiniu.handler';
import qiniu from 'qiniu';
jest.mock('qiniu', () => ({
auth: {
digest: {
Mac: jest.fn(),
},
},
conf: {
Config: jest.fn(),
Zone: jest.fn(),
},
form_up: {
FormUploader: jest.fn().mockImplementation(() => {
return {
put: jest
.fn()
.mockImplementation(
(uploadToken, key, buffer, putExtra, callback) => {
callback && callback(null, null, { statusCode: 200 });
},
),
};
}),
PutExtra: jest.fn(),
},
rs: {
PutPolicy: jest.fn().mockImplementation(() => {
return {
uploadToken: jest.fn(),
};
}),
BucketManager: jest.fn().mockImplementation(() => {
return {
privateDownloadUrl: jest.fn().mockReturnValue(''),
publicDownloadUrl: jest.fn().mockReturnValue(''),
};
}),
},
}));
describe('QiniuHandler', () => {
describe('upload', () => {
it('should upload a file and return the key', async () => {
const file = {
originalname: 'mockFileName.txt',
buffer: Buffer.from('mockFileContent'),
} as Express.Multer.File;
const mockMacInstance = {
sign: jest.fn(),
};
jest
.spyOn(qiniu.auth.digest, 'Mac')
.mockImplementation(() => mockMacInstance as any);
// Create a new instance of QiniuHandler
const handler = new QiniuHandler({
accessKey: 'mockAccessKey',
secretKey: 'mockSecretKey',
bucket: 'mockBucket',
endPoint: 'mockEndPoint',
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = await handler.upload(file);
expect(result).toEqual({
key: expect.stringMatching('.txt'),
});
expect(qiniu.auth.digest.Mac).toHaveBeenCalledWith(
'mockAccessKey',
'mockSecretKey',
);
expect(qiniu.form_up.FormUploader).toHaveBeenCalled();
});
});
describe('getUrl', () => {
it('should return the URL for the given key', async () => {
const key = 'mockFilePath/mockFileName.txt';
const handler = new QiniuHandler({
accessKey: 'mockAccessKey',
secretKey: 'mockSecretKey',
bucket: 'xiaojusurvey',
endPoint: 'mockEndPoint',
useSSL: true,
isPrivateRead: true,
expiryTime: '1h',
});
const result = await handler.getUrl(key);
expect(typeof result).toBe('string');
});
});
});

View File

@ -24,26 +24,6 @@ export class FileController {
private readonly configService: ConfigService, private readonly configService: ConfigService,
) {} ) {}
private getPathPrefix(fileKeyPrefix: string, data: Record<string, string>) {
const regex = /\{([^}]*)\}/g;
let matches;
const keys = [];
while ((matches = regex.exec(fileKeyPrefix)) !== null) {
keys.push(matches[1]);
}
let result = fileKeyPrefix;
for (const key of keys) {
if (!data[key]) {
throw new HttpException(
`参数有误:${data[key]}`,
EXCEPTION_CODE.PARAMETER_ERROR,
);
}
result = result.replace(new RegExp(`{${key}}`), data[key]);
}
return result;
}
@Post('upload') @Post('upload')
@HttpCode(200) @HttpCode(200)
@UseInterceptors(FileInterceptor('file')) @UseInterceptors(FileInterceptor('file'))
@ -56,12 +36,12 @@ export class FileController {
if (!channel || !this.configService.get<string>(channel)) { if (!channel || !this.configService.get<string>(channel)) {
throw new HttpException( throw new HttpException(
`参数有误${channel}`, `参数有误channel不正确:${reqBody.channel}`,
EXCEPTION_CODE.PARAMETER_ERROR, EXCEPTION_CODE.PARAMETER_ERROR,
); );
} }
const configKey = this.configService.get<string>(channel); const configKey = this.configService.get<string>(channel);
const needAuth = this.configService.get<boolean>(configKey); const needAuth = this.configService.get<boolean>(`${configKey}.NEED_AUTH`);
if (needAuth) { if (needAuth) {
const token = req.headers.authorization?.split(' ')[1]; const token = req.headers.authorization?.split(' ')[1];

View File

@ -10,10 +10,6 @@ import { LocalHandler } from './uploadHandlers/local.handler';
export class FileService { export class FileService {
constructor(private readonly configService: ConfigService) {} constructor(private readonly configService: ConfigService) {}
private getConfig<T>(channel, key) {
return this.configService.get<T>(channel + '_' + key);
}
async upload({ async upload({
configKey, configKey,
file, file,

View File

@ -11,6 +11,7 @@ export class AliOssHandler implements FileUploadHandler {
isPrivateRead: boolean; isPrivateRead: boolean;
expiryTime: string; expiryTime: string;
constructor({ constructor({
client,
accessKey, accessKey,
secretKey, secretKey,
bucket, bucket,
@ -19,13 +20,25 @@ export class AliOssHandler implements FileUploadHandler {
useSSL, useSSL,
isPrivateRead, isPrivateRead,
expiryTime, expiryTime,
}: {
client?: OSS;
accessKey?: string;
secretKey?: string;
bucket?: string;
region?: string;
endPoint?: string;
useSSL?: boolean;
isPrivateRead?: boolean;
expiryTime?: string;
}) { }) {
const client = new OSS({ if (!client) {
region, client = new OSS({
accessKeyId: accessKey, region,
accessKeySecret: secretKey, accessKeyId: accessKey,
bucket, accessKeySecret: secretKey,
}); bucket,
});
}
this.client = client; this.client = client;
this.endPoint = endPoint; this.endPoint = endPoint;
this.useSSL = useSSL; this.useSSL = useSSL;

View File

@ -14,6 +14,7 @@ export class MinIOHandler implements FileUploadHandler {
expiryTime: string; expiryTime: string;
bucket: string; bucket: string;
constructor({ constructor({
client,
accessKey, accessKey,
secretKey, secretKey,
bucket, bucket,
@ -22,15 +23,27 @@ export class MinIOHandler implements FileUploadHandler {
useSSL, useSSL,
isPrivateRead, isPrivateRead,
expiryTime, expiryTime,
}: {
client?: Client;
accessKey?: string;
secretKey?: string;
bucket?: string;
region?: string;
endPoint?: string;
useSSL?: boolean;
isPrivateRead?: boolean;
expiryTime?: string;
}) { }) {
const client = new Client({ if (!client) {
endPoint, client = new Client({
accessKey, endPoint,
secretKey, accessKey,
region, secretKey,
useSSL, region,
pathStyle: true, useSSL,
}); pathStyle: true,
});
}
this.client = client; this.client = client;
this.endPoint = endPoint; this.endPoint = endPoint;
this.useSSL = useSSL; this.useSSL = useSSL;

View File

@ -1,12 +0,0 @@
export const replaceFileKey = (
originStr: string,
arr: Array<{ key; value }>,
) => {
let retStr = originStr;
for (const { key, value } of arr) {
if (value) {
retStr = retStr.replace(new RegExp(`{${key}}`), value);
}
}
return retStr;
};

View File

@ -5,7 +5,6 @@ import { MessagePushingTaskService } from '../services/messagePushingTask.servic
import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto'; import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto';
import { QueryMessagePushingTaskListDto } from '../dto/queryMessagePushingTaskList.dto'; import { QueryMessagePushingTaskListDto } from '../dto/queryMessagePushingTaskList.dto';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { import {
MESSAGE_PUSHING_HOOK, MESSAGE_PUSHING_HOOK,
MESSAGE_PUSHING_TYPE, MESSAGE_PUSHING_TYPE,
@ -16,6 +15,7 @@ import { UserService } from 'src/modules/auth/services/user.service';
import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto'; import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { AuthService } from 'src/modules/auth/services/auth.service';
describe('MessagePushingTaskController', () => { describe('MessagePushingTaskController', () => {
let controller: MessagePushingTaskController; let controller: MessagePushingTaskController;
@ -51,6 +51,14 @@ describe('MessagePushingTaskController', () => {
}, },
})), })),
}, },
{
provide: AuthService,
useClass: jest.fn().mockImplementation(() => ({
verifyToken() {
return {};
},
})),
},
], ],
}).compile(); }).compile();
@ -137,9 +145,7 @@ describe('MessagePushingTaskController', () => {
await expect( await expect(
controller.findAll(req, queryDto as QueryMessagePushingTaskListDto), controller.findAll(req, queryDto as QueryMessagePushingTaskListDto),
).rejects.toThrow( ).rejects.toThrow(HttpException);
new HttpException('参数错误', EXCEPTION_CODE.PARAMETER_ERROR),
);
expect(service.findAll).toHaveBeenCalledTimes(0); expect(service.findAll).toHaveBeenCalledTimes(0);
}); });
}); });

View File

@ -65,7 +65,6 @@ describe('MessagePushingTaskService', () => {
savedTask.type = MESSAGE_PUSHING_TYPE.HTTP; savedTask.type = MESSAGE_PUSHING_TYPE.HTTP;
savedTask.pushAddress = 'http://example.com'; savedTask.pushAddress = 'http://example.com';
savedTask.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED; savedTask.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED;
savedTask.surveys = ['surveyId1', 'surveyId2'];
const mockOwnerId = '66028642292c50f8b71a9eee'; const mockOwnerId = '66028642292c50f8b71a9eee';

View File

@ -37,10 +37,7 @@ export class CreateMessagePushingTaskDto {
triggerHook: Joi.string() triggerHook: Joi.string()
.allow(null) .allow(null)
.default(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED), .default(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED),
surveys: Joi.array() surveys: Joi.array().items(Joi.string()).allow(null).default([]),
.items(Joi.string().required())
.allow(null)
.default([]),
}).validateAsync(data); }).validateAsync(data);
} }
} }

View File

@ -12,6 +12,7 @@ import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager';
import { Authtication } from 'src/guards/authtication'; import { Authtication } from 'src/guards/authtication';
import { UserService } from 'src/modules/auth/services/user.service'; import { UserService } from 'src/modules/auth/services/user.service';
import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin';
import { AuthService } from 'src/modules/auth/services/auth.service';
jest.mock('../services/dataStatistic.service'); jest.mock('../services/dataStatistic.service');
jest.mock('../services/surveyMeta.service'); jest.mock('../services/surveyMeta.service');
@ -45,6 +46,14 @@ describe('DataStatisticController', () => {
}, },
})), })),
}, },
{
provide: AuthService,
useClass: jest.fn().mockImplementation(() => ({
varifytoken() {
return {};
},
})),
},
], ],
}).compile(); }).compile();

View File

@ -1,11 +1,13 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { SurveyHistoryController } from '../controllers/surveyHistory.controller'; import { SurveyHistoryController } from '../controllers/surveyHistory.controller';
import { SurveyHistoryService } from '../services/surveyHistory.service'; import { SurveyHistoryService } from '../services/surveyHistory.service';
import { SurveyMetaService } from '../services/surveyMeta.service'; import { SurveyMetaService } from '../services/surveyMeta.service';
import { UserService } from 'src/modules/auth/services/user.service'; import { UserService } from 'src/modules/auth/services/user.service';
import { Authtication } from 'src/guards/authtication'; import { Authtication } from 'src/guards/authtication';
import { AuthService } from 'src/modules/auth/services/auth.service';
import { ConfigService } from '@nestjs/config';
describe('SurveyHistoryController', () => { describe('SurveyHistoryController', () => {
let controller: SurveyHistoryController; let controller: SurveyHistoryController;
@ -43,6 +45,14 @@ describe('SurveyHistoryController', () => {
}, },
})), })),
}, },
{
provide: AuthService,
useClass: jest.fn().mockImplementation(() => ({
verifyToken() {
return {};
},
})),
},
], ],
}).compile(); }).compile();