parent
91838194bb
commit
00d5e98712
4
server/.gitignore
vendored
4
server/.gitignore
vendored
@ -35,4 +35,6 @@ lerna-debug.log*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
tmp
|
@ -51,8 +51,12 @@
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@types/ali-oss": "^6.16.11",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/node-forge": "^1.3.11",
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Authtication } from './authtication';
|
||||
import { UserService } from '../modules/auth/services/user.service';
|
||||
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 * as jwt from 'jsonwebtoken';
|
||||
|
||||
jest.mock('jsonwebtoken');
|
||||
|
||||
describe('Authtication', () => {
|
||||
let guard: Authtication;
|
||||
let userService: UserService;
|
||||
let authService: AuthService;
|
||||
let configService: ConfigService;
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -18,9 +17,9 @@ describe('Authtication', () => {
|
||||
providers: [
|
||||
Authtication,
|
||||
{
|
||||
provide: UserService,
|
||||
provide: AuthService,
|
||||
useValue: {
|
||||
getUserByUsername: jest.fn(),
|
||||
verifyToken: jest.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -33,7 +32,7 @@ describe('Authtication', () => {
|
||||
}).compile();
|
||||
|
||||
guard = module.get<Authtication>(Authtication);
|
||||
userService = module.get<UserService>(UserService);
|
||||
authService = module.get<AuthService>(AuthService);
|
||||
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
|
||||
.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 () => {
|
||||
const request = {
|
||||
headers: {
|
||||
@ -114,9 +90,7 @@ describe('Authtication', () => {
|
||||
jest
|
||||
.spyOn(configService, 'get')
|
||||
.mockReturnValue('XIAOJU_SURVEY_JWT_SECRET');
|
||||
jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(fakeUser);
|
||||
|
||||
jest.spyOn(jwt, 'verify').mockReturnValue(fakeUser);
|
||||
jest.spyOn(authService, 'verifyToken').mockResolvedValue(fakeUser);
|
||||
|
||||
const result = await guard.canActivate(context as any);
|
||||
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { UserService } from '../modules/auth/services/user.service';
|
||||
import { AuthenticationException } from '../exceptions/authException';
|
||||
import { AuthService } from 'src/modules/auth/services/auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class Authtication implements CanActivate {
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
private readonly authService: AuthService,
|
||||
) {}
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
@ -1,18 +1,33 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { sign } from 'jsonwebtoken';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
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', () => {
|
||||
let service: AuthService;
|
||||
let userService: UserService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
imports: [ConfigModule],
|
||||
providers: [
|
||||
AuthService,
|
||||
{ provide: UserService, useValue: { getUserByUsername: jest.fn() } },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
userService = module.get<UserService>(UserService);
|
||||
});
|
||||
|
||||
describe('generateToken', () => {
|
||||
@ -25,9 +40,20 @@ describe('AuthService', () => {
|
||||
|
||||
await service.generateToken(userData, tokenConfig);
|
||||
|
||||
expect(sign).toHaveBeenCalledWith(userData, tokenConfig.secret, {
|
||||
expect(jwt.sign).toHaveBeenCalledWith(userData, tokenConfig.secret, {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
55
server/src/modules/file/__test/alioss.handler.spec.ts
Normal file
55
server/src/modules/file/__test/alioss.handler.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
150
server/src/modules/file/__test/file.controller.spec.ts
Normal file
150
server/src/modules/file/__test/file.controller.spec.ts
Normal 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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
65
server/src/modules/file/__test/file.service.spec.ts
Normal file
65
server/src/modules/file/__test/file.service.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
32
server/src/modules/file/__test/local.handler.spec.ts
Normal file
32
server/src/modules/file/__test/local.handler.spec.ts
Normal 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}`);
|
||||
});
|
||||
});
|
||||
});
|
75
server/src/modules/file/__test/minio.handler.spec.ts
Normal file
75
server/src/modules/file/__test/minio.handler.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
102
server/src/modules/file/__test/qiniu.handler.spec.ts
Normal file
102
server/src/modules/file/__test/qiniu.handler.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
@ -24,26 +24,6 @@ export class FileController {
|
||||
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')
|
||||
@HttpCode(200)
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
@ -56,12 +36,12 @@ export class FileController {
|
||||
|
||||
if (!channel || !this.configService.get<string>(channel)) {
|
||||
throw new HttpException(
|
||||
`参数有误:${channel}`,
|
||||
`参数有误channel不正确:${reqBody.channel}`,
|
||||
EXCEPTION_CODE.PARAMETER_ERROR,
|
||||
);
|
||||
}
|
||||
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) {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
|
||||
|
@ -10,10 +10,6 @@ import { LocalHandler } from './uploadHandlers/local.handler';
|
||||
export class FileService {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
private getConfig<T>(channel, key) {
|
||||
return this.configService.get<T>(channel + '_' + key);
|
||||
}
|
||||
|
||||
async upload({
|
||||
configKey,
|
||||
file,
|
||||
|
@ -11,6 +11,7 @@ export class AliOssHandler implements FileUploadHandler {
|
||||
isPrivateRead: boolean;
|
||||
expiryTime: string;
|
||||
constructor({
|
||||
client,
|
||||
accessKey,
|
||||
secretKey,
|
||||
bucket,
|
||||
@ -19,13 +20,25 @@ export class AliOssHandler implements FileUploadHandler {
|
||||
useSSL,
|
||||
isPrivateRead,
|
||||
expiryTime,
|
||||
}: {
|
||||
client?: OSS;
|
||||
accessKey?: string;
|
||||
secretKey?: string;
|
||||
bucket?: string;
|
||||
region?: string;
|
||||
endPoint?: string;
|
||||
useSSL?: boolean;
|
||||
isPrivateRead?: boolean;
|
||||
expiryTime?: string;
|
||||
}) {
|
||||
const client = new OSS({
|
||||
region,
|
||||
accessKeyId: accessKey,
|
||||
accessKeySecret: secretKey,
|
||||
bucket,
|
||||
});
|
||||
if (!client) {
|
||||
client = new OSS({
|
||||
region,
|
||||
accessKeyId: accessKey,
|
||||
accessKeySecret: secretKey,
|
||||
bucket,
|
||||
});
|
||||
}
|
||||
this.client = client;
|
||||
this.endPoint = endPoint;
|
||||
this.useSSL = useSSL;
|
||||
|
@ -14,6 +14,7 @@ export class MinIOHandler implements FileUploadHandler {
|
||||
expiryTime: string;
|
||||
bucket: string;
|
||||
constructor({
|
||||
client,
|
||||
accessKey,
|
||||
secretKey,
|
||||
bucket,
|
||||
@ -22,15 +23,27 @@ export class MinIOHandler implements FileUploadHandler {
|
||||
useSSL,
|
||||
isPrivateRead,
|
||||
expiryTime,
|
||||
}: {
|
||||
client?: Client;
|
||||
accessKey?: string;
|
||||
secretKey?: string;
|
||||
bucket?: string;
|
||||
region?: string;
|
||||
endPoint?: string;
|
||||
useSSL?: boolean;
|
||||
isPrivateRead?: boolean;
|
||||
expiryTime?: string;
|
||||
}) {
|
||||
const client = new Client({
|
||||
endPoint,
|
||||
accessKey,
|
||||
secretKey,
|
||||
region,
|
||||
useSSL,
|
||||
pathStyle: true,
|
||||
});
|
||||
if (!client) {
|
||||
client = new Client({
|
||||
endPoint,
|
||||
accessKey,
|
||||
secretKey,
|
||||
region,
|
||||
useSSL,
|
||||
pathStyle: true,
|
||||
});
|
||||
}
|
||||
this.client = client;
|
||||
this.endPoint = endPoint;
|
||||
this.useSSL = useSSL;
|
||||
|
@ -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;
|
||||
};
|
@ -5,7 +5,6 @@ import { MessagePushingTaskService } from '../services/messagePushingTask.servic
|
||||
import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto';
|
||||
import { QueryMessagePushingTaskListDto } from '../dto/queryMessagePushingTaskList.dto';
|
||||
import { HttpException } from 'src/exceptions/httpException';
|
||||
import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
|
||||
import {
|
||||
MESSAGE_PUSHING_HOOK,
|
||||
MESSAGE_PUSHING_TYPE,
|
||||
@ -16,6 +15,7 @@ import { UserService } from 'src/modules/auth/services/user.service';
|
||||
|
||||
import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import { AuthService } from 'src/modules/auth/services/auth.service';
|
||||
|
||||
describe('MessagePushingTaskController', () => {
|
||||
let controller: MessagePushingTaskController;
|
||||
@ -51,6 +51,14 @@ describe('MessagePushingTaskController', () => {
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
provide: AuthService,
|
||||
useClass: jest.fn().mockImplementation(() => ({
|
||||
verifyToken() {
|
||||
return {};
|
||||
},
|
||||
})),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
@ -137,9 +145,7 @@ describe('MessagePushingTaskController', () => {
|
||||
|
||||
await expect(
|
||||
controller.findAll(req, queryDto as QueryMessagePushingTaskListDto),
|
||||
).rejects.toThrow(
|
||||
new HttpException('参数错误', EXCEPTION_CODE.PARAMETER_ERROR),
|
||||
);
|
||||
).rejects.toThrow(HttpException);
|
||||
expect(service.findAll).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
@ -65,7 +65,6 @@ describe('MessagePushingTaskService', () => {
|
||||
savedTask.type = MESSAGE_PUSHING_TYPE.HTTP;
|
||||
savedTask.pushAddress = 'http://example.com';
|
||||
savedTask.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED;
|
||||
savedTask.surveys = ['surveyId1', 'surveyId2'];
|
||||
|
||||
const mockOwnerId = '66028642292c50f8b71a9eee';
|
||||
|
||||
|
@ -37,10 +37,7 @@ export class CreateMessagePushingTaskDto {
|
||||
triggerHook: Joi.string()
|
||||
.allow(null)
|
||||
.default(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED),
|
||||
surveys: Joi.array()
|
||||
.items(Joi.string().required())
|
||||
.allow(null)
|
||||
.default([]),
|
||||
surveys: Joi.array().items(Joi.string()).allow(null).default([]),
|
||||
}).validateAsync(data);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager';
|
||||
import { Authtication } from 'src/guards/authtication';
|
||||
import { UserService } from 'src/modules/auth/services/user.service';
|
||||
import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin';
|
||||
import { AuthService } from 'src/modules/auth/services/auth.service';
|
||||
|
||||
jest.mock('../services/dataStatistic.service');
|
||||
jest.mock('../services/surveyMeta.service');
|
||||
@ -45,6 +46,14 @@ describe('DataStatisticController', () => {
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
provide: AuthService,
|
||||
useClass: jest.fn().mockImplementation(() => ({
|
||||
varifytoken() {
|
||||
return {};
|
||||
},
|
||||
})),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { SurveyHistoryController } from '../controllers/surveyHistory.controller';
|
||||
import { SurveyHistoryService } from '../services/surveyHistory.service';
|
||||
import { SurveyMetaService } from '../services/surveyMeta.service';
|
||||
|
||||
import { UserService } from 'src/modules/auth/services/user.service';
|
||||
import { Authtication } from 'src/guards/authtication';
|
||||
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { AuthService } from 'src/modules/auth/services/auth.service';
|
||||
|
||||
describe('SurveyHistoryController', () => {
|
||||
let controller: SurveyHistoryController;
|
||||
@ -43,6 +45,14 @@ describe('SurveyHistoryController', () => {
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
provide: AuthService,
|
||||
useClass: jest.fn().mockImplementation(() => ({
|
||||
verifyToken() {
|
||||
return {};
|
||||
},
|
||||
})),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user