| import type { DismissBannerRequestDto, OwnerSetupRequestDto } from '@n8n/api-types'; | |
| import type { Logger } from '@n8n/backend-common'; | |
| import type { User } from '@n8n/db'; | |
| import type { PublicUser, SettingsRepository } from '@n8n/db'; | |
| import type { UserRepository } from '@n8n/db'; | |
| import type { Response } from 'express'; | |
| import { mock } from 'jest-mock-extended'; | |
| import type { AuthService } from '@/auth/auth.service'; | |
| import config from '@/config'; | |
| import { OwnerController } from '@/controllers/owner.controller'; | |
| import { BadRequestError } from '@/errors/response-errors/bad-request.error'; | |
| import type { EventService } from '@/events/event.service'; | |
| import type { AuthenticatedRequest } from '@/requests'; | |
| import type { BannerService } from '@/services/banner.service'; | |
| import type { PasswordUtility } from '@/services/password.utility'; | |
| import type { UserService } from '@/services/user.service'; | |
| describe('OwnerController', () => { | |
| const configGetSpy = jest.spyOn(config, 'getEnv'); | |
| const configSetSpy = jest.spyOn(config, 'set'); | |
| const logger = mock<Logger>(); | |
| const eventService = mock<EventService>(); | |
| const authService = mock<AuthService>(); | |
| const bannerService = mock<BannerService>(); | |
| const userService = mock<UserService>(); | |
| const userRepository = mock<UserRepository>(); | |
| const settingsRepository = mock<SettingsRepository>(); | |
| const passwordUtility = mock<PasswordUtility>(); | |
| const controller = new OwnerController( | |
| logger, | |
| eventService, | |
| settingsRepository, | |
| authService, | |
| bannerService, | |
| userService, | |
| passwordUtility, | |
| mock(), | |
| userRepository, | |
| ); | |
| describe('setupOwner', () => { | |
| it('should throw a BadRequestError if the instance owner is already setup', async () => { | |
| configGetSpy.mockReturnValue(true); | |
| await expect(controller.setupOwner(mock(), mock(), mock())).rejects.toThrowError( | |
| new BadRequestError('Instance owner already setup'), | |
| ); | |
| expect(userRepository.findOneOrFail).not.toHaveBeenCalled(); | |
| expect(userRepository.save).not.toHaveBeenCalled(); | |
| expect(authService.issueCookie).not.toHaveBeenCalled(); | |
| expect(settingsRepository.update).not.toHaveBeenCalled(); | |
| expect(configSetSpy).not.toHaveBeenCalled(); | |
| expect(eventService.emit).not.toHaveBeenCalled(); | |
| expect(logger.debug).toHaveBeenCalledWith( | |
| 'Request to claim instance ownership failed because instance owner already exists', | |
| ); | |
| }); | |
| it('should setup the instance owner successfully', async () => { | |
| const user = mock<User>({ | |
| id: 'userId', | |
| role: 'global:owner', | |
| authIdentities: [], | |
| }); | |
| const browserId = 'test-browser-id'; | |
| const req = mock<AuthenticatedRequest>({ user, browserId }); | |
| const res = mock<Response>(); | |
| const payload = mock<OwnerSetupRequestDto>({ | |
| email: 'valid@email.com', | |
| password: 'NewPassword123', | |
| firstName: 'Jane', | |
| lastName: 'Doe', | |
| }); | |
| configGetSpy.mockReturnValue(false); | |
| userRepository.findOneOrFail.mockResolvedValue(user); | |
| userRepository.save.mockResolvedValue(user); | |
| userService.toPublic.mockResolvedValue(mock<PublicUser>({ id: 'newUserId' })); | |
| const result = await controller.setupOwner(req, res, payload); | |
| expect(userRepository.findOneOrFail).toHaveBeenCalledWith({ | |
| where: { role: 'global:owner' }, | |
| }); | |
| expect(userRepository.save).toHaveBeenCalledWith(user, { transaction: false }); | |
| expect(authService.issueCookie).toHaveBeenCalledWith(res, user, browserId); | |
| expect(settingsRepository.update).toHaveBeenCalledWith( | |
| { key: 'userManagement.isInstanceOwnerSetUp' }, | |
| { value: JSON.stringify(true) }, | |
| ); | |
| expect(configSetSpy).toHaveBeenCalledWith('userManagement.isInstanceOwnerSetUp', true); | |
| expect(eventService.emit).toHaveBeenCalledWith('instance-owner-setup', { userId: 'userId' }); | |
| expect(result.id).toEqual('newUserId'); | |
| }); | |
| }); | |
| describe('dismissBanner', () => { | |
| it('should not call dismissBanner if no banner is provided', async () => { | |
| const payload = mock<DismissBannerRequestDto>({ banner: undefined }); | |
| const result = await controller.dismissBanner(mock(), mock(), payload); | |
| expect(bannerService.dismissBanner).not.toHaveBeenCalled(); | |
| expect(result).toBeUndefined(); | |
| }); | |
| it('should call dismissBanner with the correct banner name', async () => { | |
| const payload = mock<DismissBannerRequestDto>({ banner: 'TRIAL' }); | |
| await controller.dismissBanner(mock(), mock(), payload); | |
| expect(bannerService.dismissBanner).toHaveBeenCalledWith('TRIAL'); | |
| }); | |
| }); | |
| }); | |