@@ -10,25 +10,27 @@ import { CreateWorkspaceDto } from './dtos/createWorkspace.dto';
1010import { Workspace } from './workspace.entity' ;
1111import { Role } from '../role/role.entity' ;
1212import { User } from '../user/user.entity' ;
13- import { TokenModule } from '../auth/token/token.module' ;
13+ import { TokenService } from '../auth/token/token.service' ;
14+ import { ForbiddenAccessException } from '../exception/access.exception' ;
1415
1516describe ( 'WorkspaceService' , ( ) => {
1617 let service : WorkspaceService ;
1718 let workspaceRepository : WorkspaceRepository ;
1819 let userRepository : UserRepository ;
1920 let roleRepository : RoleRepository ;
21+ let tokenService : TokenService ;
2022
2123 beforeEach ( async ( ) => {
2224 const module : TestingModule = await Test . createTestingModule ( {
23- imports : [ TokenModule ] ,
2425 providers : [
2526 WorkspaceService ,
2627 {
2728 provide : WorkspaceRepository ,
2829 useValue : {
29- save : jest . fn ( ) ,
3030 findOneBy : jest . fn ( ) ,
31+ findOne : jest . fn ( ) ,
3132 delete : jest . fn ( ) ,
33+ save : jest . fn ( ) ,
3234 } ,
3335 } ,
3436 {
@@ -41,17 +43,26 @@ describe('WorkspaceService', () => {
4143 provide : RoleRepository ,
4244 useValue : {
4345 findOneBy : jest . fn ( ) ,
46+ findOne : jest . fn ( ) ,
4447 find : jest . fn ( ) ,
4548 save : jest . fn ( ) ,
4649 } ,
4750 } ,
51+ {
52+ provide : TokenService ,
53+ useValue : {
54+ generateInviteToken : jest . fn ( ) ,
55+ verifyInviteToken : jest . fn ( ) ,
56+ } ,
57+ } ,
4858 ] ,
4959 } ) . compile ( ) ;
5060
5161 service = module . get < WorkspaceService > ( WorkspaceService ) ;
5262 workspaceRepository = module . get < WorkspaceRepository > ( WorkspaceRepository ) ;
5363 userRepository = module . get < UserRepository > ( UserRepository ) ;
5464 roleRepository = module . get < RoleRepository > ( RoleRepository ) ;
65+ tokenService = module . get < TokenService > ( TokenService ) ;
5566 } ) ;
5667
5768 it ( '서비스 클래스가 정상적으로 인스턴스화된다.' , ( ) => {
@@ -248,4 +259,138 @@ describe('WorkspaceService', () => {
248259 } ) ;
249260 } ) ;
250261 } ) ;
262+
263+ describe ( 'generateInviteUrl' , ( ) => {
264+ it ( '정상적으로 초대 링크를 생성한다.' , async ( ) => {
265+ const userId = 1 ;
266+ const workspaceId = 'workspace-snowflake-id' ;
267+ const workspaceMock = { id : 1 } as Workspace ;
268+ const tokenMock = 'invite-token' ;
269+
270+ jest
271+ . spyOn ( workspaceRepository , 'findOneBy' )
272+ . mockResolvedValue ( workspaceMock ) ;
273+ jest
274+ . spyOn ( roleRepository , 'findOneBy' )
275+ . mockResolvedValue ( { role : 'owner' } as Role ) ;
276+ jest
277+ . spyOn ( tokenService , 'generateInviteToken' )
278+ . mockReturnValue ( tokenMock ) ;
279+
280+ const result = await service . generateInviteUrl ( userId , workspaceId ) ;
281+
282+ expect ( workspaceRepository . findOneBy ) . toHaveBeenCalledWith ( {
283+ snowflakeId : workspaceId ,
284+ } ) ;
285+ expect ( roleRepository . findOneBy ) . toHaveBeenCalledWith ( {
286+ userId,
287+ workspaceId : workspaceMock . id ,
288+ role : 'owner' ,
289+ } ) ;
290+ expect ( result ) . toEqual (
291+ `https://octodocs.local/api/workspace/join?token=${ tokenMock } ` ,
292+ ) ;
293+ } ) ;
294+
295+ it ( '워크스페이스가 존재하지 않으면 예외를 던진다.' , async ( ) => {
296+ jest . spyOn ( workspaceRepository , 'findOneBy' ) . mockResolvedValue ( null ) ;
297+
298+ await expect (
299+ service . generateInviteUrl ( 1 , 'invalid-workspace-id' ) ,
300+ ) . rejects . toThrow ( WorkspaceNotFoundException ) ;
301+ } ) ;
302+
303+ it ( '소유자가 아닌 사용자가 초대 링크를 생성하려고 하면 예외를 던진다.' , async ( ) => {
304+ const workspaceMock = { id : 1 } as Workspace ;
305+
306+ jest
307+ . spyOn ( workspaceRepository , 'findOneBy' )
308+ . mockResolvedValue ( workspaceMock ) ;
309+ jest . spyOn ( roleRepository , 'findOneBy' ) . mockResolvedValue ( null ) ;
310+
311+ await expect (
312+ service . generateInviteUrl ( 1 , 'workspace-snowflake-id' ) ,
313+ ) . rejects . toThrow ( NotWorkspaceOwnerException ) ;
314+ } ) ;
315+ } ) ;
316+
317+ describe ( 'processInviteUrl' , ( ) => {
318+ it ( '정상적으로 초대 링크를 처리한다.' , async ( ) => {
319+ const userId = 1 ;
320+ const token = 'invite-token' ;
321+ const decodedToken = { workspaceId : '1' , role : 'guest' } ;
322+
323+ jest
324+ . spyOn ( tokenService , 'verifyInviteToken' )
325+ . mockReturnValue ( decodedToken ) ;
326+ jest . spyOn ( roleRepository , 'findOneBy' ) . mockResolvedValue ( null ) ;
327+
328+ await service . processInviteUrl ( userId , token ) ;
329+
330+ expect ( tokenService . verifyInviteToken ) . toHaveBeenCalledWith ( token ) ;
331+ expect ( roleRepository . save ) . toHaveBeenCalledWith ( {
332+ workspaceId : 1 ,
333+ userId,
334+ role : 'guest' ,
335+ } ) ;
336+ } ) ;
337+
338+ it ( '이미 워크스페이스에 등록된 사용자는 예외를 던진다.' , async ( ) => {
339+ const userId = 1 ;
340+ const token = 'invite-token' ;
341+ const decodedToken = { workspaceId : '1' , role : 'guest' } ;
342+
343+ jest
344+ . spyOn ( tokenService , 'verifyInviteToken' )
345+ . mockReturnValue ( decodedToken ) ;
346+ jest . spyOn ( roleRepository , 'findOneBy' ) . mockResolvedValue ( { } as Role ) ;
347+
348+ await expect ( service . processInviteUrl ( userId , token ) ) . rejects . toThrow (
349+ Error ,
350+ ) ;
351+ } ) ;
352+ } ) ;
353+
354+ describe ( 'checkAccess' , ( ) => {
355+ it ( '퍼블릭 워크스페이스는 접근을 허용한다.' , async ( ) => {
356+ jest
357+ . spyOn ( workspaceRepository , 'findOne' )
358+ . mockResolvedValue ( { visibility : 'public' } as Workspace ) ;
359+
360+ await expect (
361+ service . checkAccess ( null , 'workspace-snowflake-id' ) ,
362+ ) . resolves . toBeUndefined ( ) ;
363+ } ) ;
364+
365+ it ( '프라이빗 워크스페이스는 권한이 없으면 예외를 던진다.' , async ( ) => {
366+ jest
367+ . spyOn ( workspaceRepository , 'findOne' )
368+ . mockResolvedValue ( { visibility : 'private' } as Workspace ) ;
369+ jest
370+ . spyOn ( userRepository , 'findOneBy' )
371+ . mockResolvedValue ( { id : 1 } as User ) ;
372+ jest . spyOn ( roleRepository , 'findOne' ) . mockResolvedValue ( null ) ;
373+
374+ await expect (
375+ service . checkAccess ( 'user-snowflake-id' , 'workspace-snowflake-id' ) ,
376+ ) . rejects . toThrow ( ForbiddenAccessException ) ;
377+ } ) ;
378+
379+ it ( '프라이빗 워크스페이스는 권한이 있으면 접근을 허용한다.' , async ( ) => {
380+ const userMock = { id : 1 } ;
381+ const workspaceMock = { id : 1 , visibility : 'private' } ;
382+
383+ jest
384+ . spyOn ( workspaceRepository , 'findOne' )
385+ . mockResolvedValue ( workspaceMock as Workspace ) ;
386+ jest
387+ . spyOn ( userRepository , 'findOneBy' )
388+ . mockResolvedValue ( userMock as User ) ;
389+ jest . spyOn ( roleRepository , 'findOne' ) . mockResolvedValue ( { } as Role ) ;
390+
391+ await expect (
392+ service . checkAccess ( 'user-snowflake-id' , 'workspace-snowflake-id' ) ,
393+ ) . resolves . toBeUndefined ( ) ;
394+ } ) ;
395+ } ) ;
251396} ) ;
0 commit comments