-
Notifications
You must be signed in to change notification settings - Fork 393
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'didi:develop' into develop
- Loading branch information
Showing
110 changed files
with
6,489 additions
and
910 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export enum SURVEY_PERMISSION { | ||
SURVEY_CONF_MANAGE = 'SURVEY_CONF_MANAGE', | ||
SURVEY_RESPONSE_MANAGE = 'SURVEY_RESPONSE_MANAGE', | ||
SURVEY_COOPERATION_MANAGE = 'SURVEY_COOPERATION_MANAGE', | ||
} | ||
|
||
export const SURVEY_PERMISSION_DESCRIPTION = { | ||
SURVEY_CONF_MANAGE: { | ||
name: '问卷配置管理', | ||
value: SURVEY_PERMISSION.SURVEY_CONF_MANAGE, | ||
}, | ||
surveyResponseManage: { | ||
name: '问卷分析管理', | ||
value: SURVEY_PERMISSION.SURVEY_RESPONSE_MANAGE, | ||
}, | ||
surveyCooperatorManage: { | ||
name: '协作者管理', | ||
value: SURVEY_PERMISSION.SURVEY_COOPERATION_MANAGE, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
export enum ROLE { | ||
ADMIN = 'admin', | ||
USER = 'user', | ||
} | ||
|
||
export const ROLE_DESCRIPTION = { | ||
ADMIN: { | ||
name: '管理员', | ||
value: ROLE.ADMIN, | ||
}, | ||
USER: { | ||
name: '用户', | ||
value: ROLE.USER, | ||
}, | ||
}; | ||
|
||
export enum PERMISSION { | ||
READ_WORKSPACE = 'READ_WORKSPACE', | ||
WRITE_WORKSPACE = 'WRITE_WORKSPACE', | ||
READ_MEMBER = 'READ_MEMBER', | ||
WRITE_MEMBER = 'WRITE_MEMBER', | ||
READ_SURVEY = 'READ_SURVEY', | ||
WRITE_SURVEY = 'WRITE_SURVEY', | ||
} | ||
|
||
export const ROLE_PERMISSION: Record<ROLE, PERMISSION[]> = { | ||
[ROLE.ADMIN]: [ | ||
PERMISSION.READ_WORKSPACE, | ||
PERMISSION.WRITE_WORKSPACE, | ||
PERMISSION.READ_MEMBER, | ||
PERMISSION.WRITE_MEMBER, | ||
PERMISSION.READ_SURVEY, | ||
PERMISSION.WRITE_SURVEY, | ||
], | ||
[ROLE.USER]: [ | ||
PERMISSION.READ_WORKSPACE, | ||
PERMISSION.READ_MEMBER, | ||
PERMISSION.READ_SURVEY, | ||
PERMISSION.WRITE_SURVEY, | ||
], | ||
}; |
55 changes: 55 additions & 0 deletions
55
server/src/exceptions/__test/httpExceptions.filter.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { HttpExceptionsFilter } from '../httpExceptions.filter'; | ||
import { ArgumentsHost } from '@nestjs/common'; | ||
import { HttpException } from '../httpException'; | ||
import { Response } from 'express'; | ||
|
||
describe('HttpExceptionsFilter', () => { | ||
let filter: HttpExceptionsFilter; | ||
let mockArgumentsHost: ArgumentsHost; | ||
let mockResponse: Partial<Response>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [HttpExceptionsFilter], | ||
}).compile(); | ||
|
||
filter = module.get<HttpExceptionsFilter>(HttpExceptionsFilter); | ||
|
||
mockResponse = { | ||
status: jest.fn().mockReturnThis(), | ||
json: jest.fn(), | ||
}; | ||
|
||
mockArgumentsHost = { | ||
switchToHttp: jest.fn().mockReturnThis(), | ||
getResponse: jest.fn().mockReturnValue(mockResponse), | ||
} as unknown as ArgumentsHost; | ||
}); | ||
|
||
it('should return 500 status and "Internal Server Error" message for generic errors', () => { | ||
const genericError = new Error('Some error'); | ||
|
||
filter.catch(genericError, mockArgumentsHost); | ||
|
||
expect(mockResponse.status).toHaveBeenCalledWith(500); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ | ||
message: 'Internal Server Error', | ||
code: 500, | ||
errmsg: 'Some error', | ||
}); | ||
}); | ||
|
||
it('should return 200 status and specific message for HttpException', () => { | ||
const httpException = new HttpException('Specific error message', 1001); | ||
|
||
filter.catch(httpException, mockArgumentsHost); | ||
|
||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ | ||
message: 'Specific error message', | ||
code: 1001, | ||
errmsg: 'Specific error message', | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
// all-exceptions.filter.ts | ||
import { | ||
ExceptionFilter, | ||
Catch, | ||
|
4 changes: 2 additions & 2 deletions
4
...exceptions/noSurveyPermissionException.ts → ...r/src/exceptions/noPermissionException.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import { HttpException } from './httpException'; | ||
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; | ||
|
||
export class NoSurveyPermissionException extends HttpException { | ||
export class NoPermissionException extends HttpException { | ||
constructor(public readonly message: string) { | ||
super(message, EXCEPTION_CODE.NO_SURVEY_PERMISSION); | ||
super(message, EXCEPTION_CODE.NO_PERMISSION); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { Reflector } from '@nestjs/core'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ExecutionContext } from '@nestjs/common'; | ||
|
||
import { SurveyGuard } from '../survey.guard'; | ||
import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; | ||
import { CollaboratorService } from 'src/modules/survey/services/collaborator.service'; | ||
import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service'; | ||
import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; | ||
import { NoPermissionException } from 'src/exceptions/noPermissionException'; | ||
import { SurveyMeta } from 'src/models/surveyMeta.entity'; | ||
import { WorkspaceMember } from 'src/models/workspaceMember.entity'; | ||
import { Collaborator } from 'src/models/collaborator.entity'; | ||
|
||
describe('SurveyGuard', () => { | ||
let guard: SurveyGuard; | ||
let reflector: Reflector; | ||
let collaboratorService: CollaboratorService; | ||
let surveyMetaService: SurveyMetaService; | ||
let workspaceMemberService: WorkspaceMemberService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
SurveyGuard, | ||
{ | ||
provide: Reflector, | ||
useValue: { | ||
get: jest.fn(), | ||
}, | ||
}, | ||
{ | ||
provide: CollaboratorService, | ||
useValue: { | ||
getCollaborator: jest.fn(), | ||
}, | ||
}, | ||
{ | ||
provide: SurveyMetaService, | ||
useValue: { | ||
getSurveyById: jest.fn(), | ||
}, | ||
}, | ||
{ | ||
provide: WorkspaceMemberService, | ||
useValue: { | ||
findOne: jest.fn(), | ||
}, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
guard = module.get<SurveyGuard>(SurveyGuard); | ||
reflector = module.get<Reflector>(Reflector); | ||
collaboratorService = module.get<CollaboratorService>(CollaboratorService); | ||
surveyMetaService = module.get<SurveyMetaService>(SurveyMetaService); | ||
workspaceMemberService = module.get<WorkspaceMemberService>( | ||
WorkspaceMemberService, | ||
); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(guard).toBeDefined(); | ||
}); | ||
|
||
it('should allow access if no surveyId is present', async () => { | ||
const context = createMockExecutionContext(); | ||
jest.spyOn(reflector, 'get').mockReturnValue(null); | ||
|
||
const result = await guard.canActivate(context); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it('should throw SurveyNotFoundException if survey does not exist', async () => { | ||
const context = createMockExecutionContext(); | ||
jest.spyOn(reflector, 'get').mockReturnValue('params.surveyId'); | ||
jest.spyOn(surveyMetaService, 'getSurveyById').mockResolvedValue(null); | ||
|
||
await expect(guard.canActivate(context)).rejects.toThrow( | ||
SurveyNotFoundException, | ||
); | ||
}); | ||
|
||
it('should allow access if user is the owner of the survey', async () => { | ||
const context = createMockExecutionContext(); | ||
const surveyMeta = { owner: 'testUser', workspaceId: null }; | ||
jest.spyOn(reflector, 'get').mockReturnValue('params.surveyId'); | ||
jest | ||
.spyOn(surveyMetaService, 'getSurveyById') | ||
.mockResolvedValue(surveyMeta as SurveyMeta); | ||
|
||
const result = await guard.canActivate(context); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it('should allow access if user is a workspace member', async () => { | ||
const context = createMockExecutionContext(); | ||
const surveyMeta = { owner: 'anotherUser', workspaceId: 'workspaceId' }; | ||
jest.spyOn(reflector, 'get').mockReturnValue('params.surveyId'); | ||
jest | ||
.spyOn(surveyMetaService, 'getSurveyById') | ||
.mockResolvedValue(surveyMeta as SurveyMeta); | ||
jest | ||
.spyOn(workspaceMemberService, 'findOne') | ||
.mockResolvedValue({} as WorkspaceMember); | ||
|
||
const result = await guard.canActivate(context); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it('should throw NoPermissionException if user has no permissions', async () => { | ||
const context = createMockExecutionContext(); | ||
const surveyMeta = { owner: 'anotherUser', workspaceId: null }; | ||
jest.spyOn(reflector, 'get').mockReturnValueOnce('params.surveyId'); | ||
jest.spyOn(reflector, 'get').mockReturnValueOnce(['requiredPermission']); | ||
jest | ||
.spyOn(surveyMetaService, 'getSurveyById') | ||
.mockResolvedValue(surveyMeta as SurveyMeta); | ||
jest | ||
.spyOn(collaboratorService, 'getCollaborator') | ||
.mockResolvedValue({ permissions: [] } as Collaborator); | ||
|
||
await expect(guard.canActivate(context)).rejects.toThrow( | ||
NoPermissionException, | ||
); | ||
}); | ||
|
||
function createMockExecutionContext(): ExecutionContext { | ||
return { | ||
switchToHttp: jest.fn().mockReturnValue({ | ||
getRequest: jest.fn().mockReturnValue({ | ||
user: { username: 'testUser', _id: 'testUserId' }, | ||
params: { surveyId: 'surveyId' }, | ||
}), | ||
}), | ||
getHandler: jest.fn(), | ||
} as unknown as ExecutionContext; | ||
} | ||
}); |
Oops, something went wrong.