import { DyNTS_DiBo_IO_ControlService } from './dibo-io.control-service'; import { DyNTS_DiBo_Main_ControlService } from './dibo-main.control-service'; import { DyNTS_DiBo_Commands_ControlService } from './dibo-commands.control-service'; import { Message, TextChannel, Guild } from 'discord.js'; import { DyFM_Error } from '@futdevpro/fsm-dynamo'; import { DyNTS_global_settings } from '../../../_collections/global-settings.const'; import { DyNTS_DiBo_global_settings } from '../_collections/dibo-global-settings.conts'; import { DyFM_EnvironmentFlag } from '@futdevpro/fsm-dynamo'; class TestDiBoIOService extends DyNTS_DiBo_IO_ControlService { protected override mainDiscordBot_CS: any; protected override getMainDiscordBotControlService(): DyNTS_DiBo_Main_ControlService { return this.mainDiscordBot_CS; } protected override getCommandsControlService(): DyNTS_DiBo_Commands_ControlService { return this.commands_CS; } async handleMessage(message: Message, issuer: string): Promise { return message; } static getInstance(): TestDiBoIOService { return TestDiBoIOService.getSingletonInstance(); } } xdescribe('| DyNTS_DiBo_IO_ControlService', () => { let service: TestDiBoIOService; let mockMain_CS: jasmine.SpyObj; let mockCommands_CS: jasmine.SpyObj; let mockGuild: jasmine.SpyObj; let mockMessage: jasmine.SpyObj; let mockTextChannel: jasmine.SpyObj; beforeAll(() => { if (!DyNTS_global_settings.systemShortCodeName) { (DyNTS_global_settings as { systemShortCodeName?: string }).systemShortCodeName = 'TEST'; } if (!DyNTS_global_settings.env_settings) { (DyNTS_global_settings as { env_settings?: unknown }).env_settings = { environment: DyFM_EnvironmentFlag.local }; } }); beforeEach(() => { mockGuild = jasmine.createSpyObj('Guild', [], { id: 'guild-123', }); mockTextChannel = jasmine.createSpyObj('TextChannel', ['send'], { id: 'channel-123', name: 'test-channel', isTextBased: () => true, isDMBased: () => false, }); mockMessage = jasmine.createSpyObj('Message', ['reply']); // Set properties as writable/configurable so they can be modified in tests Object.defineProperties(mockMessage, { id: { value: 'message-123', writable: true, configurable: true }, content: { value: 'Test message', writable: true, configurable: true }, author: { value: { id: 'user-123', username: 'test-user', displayName: 'Test User', bot: false, }, writable: true, configurable: true }, channel: { value: mockTextChannel, writable: true, configurable: true }, reference: { value: null, writable: true, configurable: true }, cleanContent: { value: 'Test message', writable: true, configurable: true }, }); mockMain_CS = jasmine.createSpyObj('DyNTS_DiBo_Main_ControlService', [], { discordServer: mockGuild, botClientId: 'bot-123', botDisplayName: 'Test Bot', client: { user: { id: 'bot-123', }, }, }); mockCommands_CS = jasmine.createSpyObj('DyNTS_DiBo_Commands_ControlService', ['handleCommand']); service = new (TestDiBoIOService as any)(); (service as any).mainDiscordBot_CS = mockMain_CS; (service as any).commands_CS = mockCommands_CS; }); describe('| properties', () => { it('| should return discordServer from main service', () => { expect(service.discordServer).toBe(mockGuild); }); it('| should return botClientId from main service', () => { expect(service.botClientId).toBe('bot-123'); }); it('| should return botDisplayName from main service', () => { expect(service.botDisplayName).toBe('Test Bot'); }); }); describe('| setup', () => { it('| should setup service with main and commands services', async () => { await service.setup('test-issuer'); expect((service as any).mainDiscordBot_CS).toBe(mockMain_CS); expect((service as any).commands_CS).toBe(mockCommands_CS); }); it('| should throw error if main service not found', async () => { (service as any).getMainDiscordBotControlService = () => null; try { await service.setup('test-issuer'); fail('Should have thrown an error'); } catch (error) { expect(error).toBeInstanceOf(DyFM_Error); expect((error as DyFM_Error)._errorCode).toContain('DyNTS-DiBo-IO-S01'); } }); it('| should throw error if commands service not found', async () => { (service as any).getCommandsControlService = () => null; try { await service.setup('test-issuer'); fail('Should have thrown an error'); } catch (error) { expect(error).toBeInstanceOf(DyFM_Error); expect((error as DyFM_Error)._errorCode).toContain('DyNTS-DiBo-IO-S02'); } }); }); describe('| start', () => { it('| should start service successfully', async () => { await service.start('test-issuer'); expect(service.discordServer).toBe(mockGuild); }); it('| should throw error if discordServer not found', async () => { (service as any).mainDiscordBot_CS = null; try { await service.start('test-issuer'); fail('Should have thrown an error'); } catch (error) { expect(error).toBeInstanceOf(DyFM_Error); expect((error as DyFM_Error)._errorCode).toContain('DyNTS-DiBo-IO-ST00'); } }); }); describe('| handleNewMessage', () => { it('| should handle message if for bot', async () => { spyOn(service, 'getMessageIsForBotToHandle').and.returnValue(Promise.resolve(true)); spyOn(service, 'handleIfCommand').and.returnValue(Promise.resolve(false)); spyOn(service, 'handleMessage').and.returnValue(Promise.resolve(mockMessage)); await service.handleNewMessage(mockMessage, 'test-issuer'); expect(service.handleMessage).toHaveBeenCalled(); }); it('| should not handle message if not for bot', async () => { spyOn(service, 'getMessageIsForBotToHandle').and.returnValue(Promise.resolve(false)); spyOn(service, 'handleMessage'); await service.handleNewMessage(mockMessage, 'test-issuer'); expect(service.handleMessage).not.toHaveBeenCalled(); }); it('| should handle command if message is command', async () => { spyOn(service, 'getMessageIsForBotToHandle').and.returnValue(Promise.resolve(true)); const handleIfCommandSpy = spyOn(service, 'handleIfCommand').and.returnValue(Promise.resolve(true)); spyOn(service, 'handleMessage'); await service.handleNewMessage(mockMessage, 'test-issuer'); expect(service.handleMessage).not.toHaveBeenCalled(); // Since handleIfCommand is mocked, we verify it was called, not the underlying handleCommand expect(handleIfCommandSpy).toHaveBeenCalled(); }); it('| should handle errors gracefully', async () => { const error = new Error('Test error'); spyOn(service, 'getMessageIsForBotToHandle').and.throwError(error); mockMessage.reply.and.returnValue(Promise.resolve(mockMessage as any)); try { await service.handleNewMessage(mockMessage, 'test-issuer'); fail('Should have thrown an error'); } catch (err) { expect(err).toBeInstanceOf(DyFM_Error); } }); }); describe('| handleIfCommand', () => { it('| should handle command if message is command', async () => { spyOn(service, 'isCommand').and.returnValue(Promise.resolve(true)); mockCommands_CS.handleCommand.and.returnValue(Promise.resolve()); const result = await service.handleIfCommand(mockMessage, 'test-issuer'); expect(result).toBe(true); expect(mockCommands_CS.handleCommand).toHaveBeenCalled(); }); it('| should not handle if message is not command', async () => { spyOn(service, 'isCommand').and.returnValue(Promise.resolve(false)); const result = await service.handleIfCommand(mockMessage, 'test-issuer'); expect(result).toBe(false); expect(mockCommands_CS.handleCommand).not.toHaveBeenCalled(); }); }); describe('| isCommand', () => { it('| should return true if message starts with command operator', async () => { mockMessage.content = '!test command'; const originalCommandSettings = DyNTS_DiBo_global_settings.commandSettings; (DyNTS_DiBo_global_settings as any).commandSettings = { commandOperator: '!', commands: [{ command: 'test' }], }; const result = await service.isCommand(mockMessage, 'test-issuer'); expect(result).toBe(true); (DyNTS_DiBo_global_settings as any).commandSettings = originalCommandSettings; }); it('| should return false if message does not start with command operator', async () => { mockMessage.content = 'test command'; const originalCommandSettings = DyNTS_DiBo_global_settings.commandSettings; (DyNTS_DiBo_global_settings as any).commandSettings = { commandOperator: '!', commands: [{ command: 'test' }], }; const result = await service.isCommand(mockMessage, 'test-issuer'); expect(result).toBe(false); (DyNTS_DiBo_global_settings as any).commandSettings = originalCommandSettings; }); }); });