From 62bb0d6d9acc2000902f19b533a7b74270e1ead9 Mon Sep 17 00:00:00 2001 From: TESTELIN Geoffrey Date: Sun, 25 Apr 2021 19:51:04 +0200 Subject: [PATCH] fix(release-notes): the feature command will works as expected for the release notes feature --- ...rd-message-command-feature.service.spec.ts | 904 ++++++++++++++++++ ...discord-message-command-feature.service.ts | 173 ++-- 2 files changed, 1020 insertions(+), 57 deletions(-) diff --git a/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.spec.ts b/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.spec.ts index e756910a7..def68d1a4 100644 --- a/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.spec.ts +++ b/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.spec.ts @@ -1,5 +1,6 @@ import { DiscordMessageCommandFeatureService } from './discord-message-command-feature.service'; import { DiscordMessageCommandFeatureNoonService } from './features/noon/services/discord-message-command-feature-noon.service'; +import { DiscordMessageCommandFeatureReleaseNotesService } from './features/release-notes/services/discord-message-command-feature-release-notes.service'; import { DiscordMessageCommandFeatureEmptyContentErrorService } from './services/discord-message-command-feature-empty-content-error.service'; import { DiscordMessageCommandFeatureEmptyFeatureNameErrorService } from './services/feature-names/discord-message-command-feature-empty-feature-name-error.service'; import { DiscordMessageCommandFeatureWrongFeatureNameErrorService } from './services/feature-names/discord-message-command-feature-wrong-feature-name-error.service'; @@ -28,6 +29,7 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { let loggerService: LoggerService; let discordMessageConfigService: DiscordMessageConfigService; let discordMessageCommandFeatureNoonService: DiscordMessageCommandFeatureNoonService; + let discordMessageCommandFeatureReleaseNotesService: DiscordMessageCommandFeatureReleaseNotesService; let discordMessageCommandFeatureEmptyContentErrorService: DiscordMessageCommandFeatureEmptyContentErrorService; let discordMessageCommandFeatureEmptyFeatureNameErrorService: DiscordMessageCommandFeatureEmptyFeatureNameErrorService; let discordMessageCommandFeatureWrongFeatureNameErrorService: DiscordMessageCommandFeatureWrongFeatureNameErrorService; @@ -41,6 +43,7 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { loggerService = LoggerService.getInstance(); discordMessageConfigService = DiscordMessageConfigService.getInstance(); discordMessageCommandFeatureNoonService = DiscordMessageCommandFeatureNoonService.getInstance(); + discordMessageCommandFeatureReleaseNotesService = DiscordMessageCommandFeatureReleaseNotesService.getInstance(); discordMessageCommandFeatureEmptyContentErrorService = DiscordMessageCommandFeatureEmptyContentErrorService.getInstance(); discordMessageCommandFeatureEmptyFeatureNameErrorService = DiscordMessageCommandFeatureEmptyFeatureNameErrorService.getInstance(); discordMessageCommandFeatureWrongFeatureNameErrorService = DiscordMessageCommandFeatureWrongFeatureNameErrorService.getInstance(); @@ -149,7 +152,9 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { let loggerServiceDebugSpy: jest.SpyInstance; let discordMessageCommandFeatureNoonServiceIsNoonFeatureSpy: jest.SpyInstance; + let discordMessageCommandFeatureReleaseNotesServiceIsReleaseNotesFeatureSpy: jest.SpyInstance; let discordMessageCommandFeatureNoonServiceGetMessageResponseSpy: jest.SpyInstance; + let discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy: jest.SpyInstance; let discordMessageCommandFeatureEmptyContentErrorServiceGetMessageResponseSpy: jest.SpyInstance; let discordMessageCommandFeatureEmptyFeatureNameErrorServiceGetMessageResponseSpy: jest.SpyInstance; let discordMessageCommandFeatureWrongFeatureNameErrorServiceGetMessageResponseSpy: jest.SpyInstance; @@ -190,9 +195,15 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { discordMessageCommandFeatureNoonServiceIsNoonFeatureSpy = jest .spyOn(discordMessageCommandFeatureNoonService, `isNoonFeature`) .mockImplementation(); + discordMessageCommandFeatureReleaseNotesServiceIsReleaseNotesFeatureSpy = jest + .spyOn(discordMessageCommandFeatureReleaseNotesService, `isReleaseNotesFeature`) + .mockImplementation(); discordMessageCommandFeatureNoonServiceGetMessageResponseSpy = jest .spyOn(discordMessageCommandFeatureNoonService, `getMessageResponse`) .mockRejectedValue(new Error(`discordMessageCommandFeatureNoonService getMessageResponse error`)); + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy = jest + .spyOn(discordMessageCommandFeatureReleaseNotesService, `getMessageResponse`) + .mockRejectedValue(new Error(`discordMessageCommandFeatureReleaseNotesService getMessageResponse error`)); discordMessageCommandFeatureEmptyContentErrorServiceGetMessageResponseSpy = jest .spyOn(discordMessageCommandFeatureEmptyContentErrorService, `getMessageResponse`) .mockRejectedValue(new Error(`discordMessageCommandFeatureEmptyContentErrorService getMessageResponse error`)); @@ -372,6 +383,7 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { describe(`when the given message feature is not an existing feature`, (): void => { beforeEach((): void => { discordMessageCommandFeatureNoonServiceIsNoonFeatureSpy.mockReturnValue(false); + discordMessageCommandFeatureReleaseNotesServiceIsReleaseNotesFeatureSpy.mockReturnValue(false); }); it(`should get the wrong feature name error message response`, async (): Promise => { @@ -1340,6 +1352,898 @@ describe(`DiscordMessageCommandFeatureService`, (): void => { }); }); }); + + describe(`when the given message feature is the release notes feature`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceIsReleaseNotesFeatureSpy.mockReturnValue(true); + }); + + describe(`when the given message feature does not contain a flag`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes`; + }); + + it(`should get the empty flags error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureEmptyFlagsErrorService getMessageResponse error`) + ); + + expect(discordMessageCommandFeatureEmptyFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureEmptyFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledWith( + anyDiscordMessage, + [DiscordMessageCommandEnum.FEATURE, DiscordMessageCommandEnum.F], + `release-notes` + ); + }); + + describe(`when the fetch of the empty flags error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureEmptyFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureEmptyFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureEmptyFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the empty flags error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureEmptyFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getEmptyFlagsErrorMessageResponse + ); + }); + + it(`should return the empty flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getEmptyFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that no flags was specified`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureEmptyFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes not having any flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature contains a flag`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --yo`; + }); + + describe(`when the given message feature flag is unknown`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --yo`; + }); + + it(`should get the wrong flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + + expect(discordMessageCommandFeatureWrongFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledTimes( + 1 + ); + expect(discordMessageCommandFeatureWrongFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledWith([ + { + description: `The flag \`yo\` is unknown to the release-notes feature.`, + isUnknown: true, + name: `Unknown flag`, + } as IDiscordCommandFlagError, + ]); + }); + + describe(`when the fetch of the wrong flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureWrongFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the wrong flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureWrongFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getWrongFlagsErrorMessageResponse + ); + }); + + it(`should return the wrong flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getWrongFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is wrong`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes not having all valid flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 2 flags enabled which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --enabled=true -e`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--enabled=true\` and \`-e\` are duplicated.`, + name: `Enabled flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 2 flags disabled which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --disabled=true -d`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--disabled=true\` and \`-d\` are duplicated.`, + name: `Disabled flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 2 flags help which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --help -h`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--help\` and \`-h\` are duplicated.`, + name: `Help flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 3 flags enabled which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --enabled=true -e --enabled=false`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--enabled=true\`, \`-e\` and \`--enabled=false\` are duplicated.`, + name: `Enabled flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 3 flags disabled which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --disabled=true -d --disabled=false`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--disabled=true\`, \`-d\` and \`--disabled=false\` are duplicated.`, + name: `Disabled flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 3 flags help which are known and valid but duplicated`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --help -h --help`; + }); + + it(`should get the duplicated flag error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect( + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledWith([ + { + description: `The flags \`--help\`, \`-h\` and \`--help\` are duplicated.`, + name: `Help flag duplicated`, + } as IDiscordCommandFlagDuplicated, + ]); + }); + + describe(`when the fetch of the duplicated flag error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the duplicated flag error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureDuplicatedFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the duplicated flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is duplicated`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureDuplicatedFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has duplicated flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 2 flags which are known and valid but opposites`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --enabled=true --disabled=true`; + }); + + it(`should get the opposite flags error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledWith( + [ + { + description: `The flags \`--enabled=true\` and \`--disabled=true\` are opposites.`, + name: `Enabled and Disabled flags can not be combined`, + } as IDiscordCommandFlagDuplicated, + ] + ); + }); + + describe(`when the fetch of the opposite flags error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the opposite flags error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the opposite flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is opposite`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has opposite flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature has 2 shortcut flags which are known and valid but opposites`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes -e -d`; + }); + + it(`should get the opposite flags error message response`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + + expect( + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy + ).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy).toHaveBeenCalledWith( + [ + { + description: `The flags \`-e\` and \`-d\` are opposites.`, + name: `Enabled and Disabled flags can not be combined`, + } as IDiscordCommandFlagDuplicated, + ] + ); + }); + + describe(`when the fetch of the opposite flags error message response failed`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should throw an error`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the fetch of the opposite flags error message response succeeded`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureOppositeFlagsErrorServiceGetMessageResponseSpy.mockResolvedValue( + getDuplicatedFlagsErrorMessageResponse + ); + }); + + it(`should return the opposite flags error message response`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(getDuplicatedFlagsErrorMessageResponse); + }); + }); + + it(`should log about the fact that at least one flag is opposite`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureOppositeFlagsErrorService getMessageResponse error`) + ); + + expect(loggerServiceDebugSpy).toHaveBeenCalledTimes(1); + expect(loggerServiceDebugSpy).toHaveBeenCalledWith({ + context: `DiscordMessageCommandFeatureService`, + hasExtendedContext: true, + message: `context-[dummy-id] text-feature name value-Release-notes has opposite flags`, + } as ILoggerLog); + }); + }); + + describe(`when the given message feature enabled flag is known and valid`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --enabled=true`; + }); + + it(`should get a message response for the release notes feature`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureReleaseNotesService getMessageResponse error`) + ); + + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledWith( + anyDiscordMessage, + `--enabled=true` + ); + }); + + describe(`when the message response for the release notes feature failed to be fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should return the message response error for the release notes feature`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the message response for the release notes feature was successfully fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockResolvedValue( + discordMessageResponse + ); + }); + + it(`should return a Discord message response for the release notes feature`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(discordMessageResponse); + }); + }); + }); + + describe(`when the given message feature disabled flag is known and valid`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --disabled=true`; + }); + + it(`should get a message response for the release notes feature`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureReleaseNotesService getMessageResponse error`) + ); + + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledWith( + anyDiscordMessage, + `--disabled=true` + ); + }); + + describe(`when the message response for the release notes feature failed to be fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should return the message response error for the release notes feature`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the message response for the release notes feature was successfully fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockResolvedValue( + discordMessageResponse + ); + }); + + it(`should return a Discord message response for the release notes feature`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(discordMessageResponse); + }); + }); + }); + + describe(`when the given message feature help flag is known and valid`, (): void => { + beforeEach((): void => { + anyDiscordMessage.content = `message !feature Release-notes --help`; + }); + + it(`should get a message response for the release notes feature`, async (): Promise => { + expect.assertions(3); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureReleaseNotesService getMessageResponse error`) + ); + + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledTimes(1); + expect(discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy).toHaveBeenCalledWith( + anyDiscordMessage, + `--help` + ); + }); + + describe(`when the message response for the release notes feature failed to be fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockRejectedValue( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + + it(`should return the message response error for the release notes feature`, async (): Promise => { + expect.assertions(1); + + await expect(service.getMessageResponse(anyDiscordMessage)).rejects.toThrow( + new Error(`discordMessageCommandFeatureWrongFlagsErrorService getMessageResponse error`) + ); + }); + }); + + describe(`when the message response for the release notes feature was successfully fetched`, (): void => { + beforeEach((): void => { + discordMessageCommandFeatureReleaseNotesServiceGetMessageResponseSpy.mockResolvedValue( + discordMessageResponse + ); + }); + + it(`should return a Discord message response for the release notes feature`, async (): Promise => { + expect.assertions(1); + + const result = await service.getMessageResponse(anyDiscordMessage); + + expect(result).toStrictEqual(discordMessageResponse); + }); + }); + }); + }); + }); }); }); }); diff --git a/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.ts b/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.ts index 90efc7a68..6172f01d9 100644 --- a/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.ts +++ b/src/features/discord/messages/services/command/feature/discord-message-command-feature.service.ts @@ -1,6 +1,8 @@ import { DiscordMessageCommandFeatureNameEnum } from './enums/discord-message-command-feature-name.enum'; import { DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS } from './features/noon/constants/discord-message-command-feature-noon-flags'; import { DiscordMessageCommandFeatureNoonService } from './features/noon/services/discord-message-command-feature-noon.service'; +import { DISCORD_MESSAGE_COMMAND_FEATURE_RELEASE_NOTES_FLAGS } from './features/release-notes/constants/discord-message-command-feature-release-notes-flags'; +import { DiscordMessageCommandFeatureReleaseNotesService } from './features/release-notes/services/discord-message-command-feature-release-notes.service'; import { DiscordMessageCommandFeatureEmptyContentErrorService } from './services/discord-message-command-feature-empty-content-error.service'; import { DiscordMessageCommandFeatureEmptyFeatureNameErrorService } from './services/feature-names/discord-message-command-feature-empty-feature-name-error.service'; import { DiscordMessageCommandFeatureWrongFeatureNameErrorService } from './services/feature-names/discord-message-command-feature-wrong-feature-name-error.service'; @@ -22,6 +24,7 @@ import { IDiscordCommandFlagsDuplicated } from '../../../types/commands/flags/di import { IDiscordCommandFlagsErrors } from '../../../types/commands/flags/discord-command-flags-errors'; import { IDiscordCommandFlagsOpposite } from '../../../types/commands/flags/discord-command-flags-opposite'; import { DiscordMessageConfigService } from '../../config/discord-message-config.service'; +import { Message } from 'discord.js'; import _ from 'lodash'; export class DiscordMessageCommandFeatureService extends AbstractService { @@ -63,81 +66,45 @@ export class DiscordMessageCommandFeatureService extends AbstractService { return DiscordMessageCommandFeatureEmptyContentErrorService.getInstance().getMessageResponse(); } - const featureName: string | null = this._getFeatureName(anyDiscordMessage.content); + // Small type hack but the other way is to use instanceof and it's not nice for the testing purposes + const message: Message = anyDiscordMessage as Message; + const featureName: string | null = this._getFeatureName(message.content); if (_.isNil(featureName)) { LoggerService.getInstance().debug({ context: this._serviceName, hasExtendedContext: true, - message: LoggerService.getInstance().getSnowflakeContext(anyDiscordMessage.id, `feature name not specified`), + message: LoggerService.getInstance().getSnowflakeContext(message.id, `feature name not specified`), }); return DiscordMessageCommandFeatureEmptyFeatureNameErrorService.getInstance().getMessageResponse( - anyDiscordMessage, + message, this._commands ); } - if (!DiscordMessageCommandFeatureNoonService.getInstance().isNoonFeature(featureName)) { - LoggerService.getInstance().debug({ - context: this._serviceName, - hasExtendedContext: true, - message: LoggerService.getInstance().getSnowflakeContext( - anyDiscordMessage.id, - `feature name ${ChalkService.getInstance().value(featureName)} not matching an existing feature` - ), - }); - - return DiscordMessageCommandFeatureWrongFeatureNameErrorService.getInstance().getMessageResponse( - anyDiscordMessage, - this._commands, - featureName - ); + if (DiscordMessageCommandFeatureNoonService.getInstance().isNoonFeature(featureName)) { + return this._getNoonMessageResponse(message); } - const messageFlags: string | null = this.getFlags(anyDiscordMessage.content); - - if (_.isNil(messageFlags)) { - return this._getEmptyFlagsErrorMessageResponse(anyDiscordMessage, DiscordMessageCommandFeatureNameEnum.NOON); - } - - const flagsErrors: IDiscordCommandFlagsErrors | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getErrors( - messageFlags - ); - - if (!_.isNil(flagsErrors)) { - return this._getWrongFlagsErrorMessageResponse( - anyDiscordMessage, - DiscordMessageCommandFeatureNameEnum.NOON, - flagsErrors - ); + if (DiscordMessageCommandFeatureReleaseNotesService.getInstance().isReleaseNotesFeature(featureName)) { + return this._getReleaseNotesMessageResponse(message); } - const flagsDuplicated: IDiscordCommandFlagsDuplicated | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getDuplicated( - messageFlags - ); - - if (!_.isNil(flagsDuplicated)) { - return this._getDuplicatedFlagsErrorMessageResponse( - anyDiscordMessage, - DiscordMessageCommandFeatureNameEnum.NOON, - flagsDuplicated - ); - } + LoggerService.getInstance().debug({ + context: this._serviceName, + hasExtendedContext: true, + message: LoggerService.getInstance().getSnowflakeContext( + message.id, + `feature name ${ChalkService.getInstance().value(featureName)} not matching an existing feature` + ), + }); - const oppositeFlags: IDiscordCommandFlagsOpposite | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getOpposites( - messageFlags + return DiscordMessageCommandFeatureWrongFeatureNameErrorService.getInstance().getMessageResponse( + message, + this._commands, + featureName ); - - if (!_.isNil(oppositeFlags)) { - return this._getOppositeFlagsErrorMessageResponse( - anyDiscordMessage, - DiscordMessageCommandFeatureNameEnum.NOON, - oppositeFlags - ); - } - - return DiscordMessageCommandFeatureNoonService.getInstance().getMessageResponse(anyDiscordMessage, messageFlags); } public hasCommand(message: Readonly): boolean { @@ -240,4 +207,96 @@ export class DiscordMessageCommandFeatureService extends AbstractService { return DiscordMessageCommandFeatureOppositeFlagsErrorService.getInstance().getMessageResponse(oppositeFlags); } + + private _getNoonMessageResponse( + message: Readonly + ): Promise { + const messageFlags: string | null = this.getFlags(message.content); + + if (_.isNil(messageFlags)) { + return this._getEmptyFlagsErrorMessageResponse(message, DiscordMessageCommandFeatureNameEnum.NOON); + } + + const flagsErrors: IDiscordCommandFlagsErrors | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getErrors( + messageFlags + ); + + if (!_.isNil(flagsErrors)) { + return this._getWrongFlagsErrorMessageResponse(message, DiscordMessageCommandFeatureNameEnum.NOON, flagsErrors); + } + + const flagsDuplicated: IDiscordCommandFlagsDuplicated | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getDuplicated( + messageFlags + ); + + if (!_.isNil(flagsDuplicated)) { + return this._getDuplicatedFlagsErrorMessageResponse( + message, + DiscordMessageCommandFeatureNameEnum.NOON, + flagsDuplicated + ); + } + + const oppositeFlags: IDiscordCommandFlagsOpposite | null = DISCORD_MESSAGE_COMMAND_FEATURE_NOON_FLAGS.getOpposites( + messageFlags + ); + + if (!_.isNil(oppositeFlags)) { + return this._getOppositeFlagsErrorMessageResponse( + message, + DiscordMessageCommandFeatureNameEnum.NOON, + oppositeFlags + ); + } + + return DiscordMessageCommandFeatureNoonService.getInstance().getMessageResponse(message, messageFlags); + } + + private _getReleaseNotesMessageResponse( + message: Readonly + ): Promise { + const messageFlags: string | null = this.getFlags(message.content); + + if (_.isNil(messageFlags)) { + return this._getEmptyFlagsErrorMessageResponse(message, DiscordMessageCommandFeatureNameEnum.RELEASE_NOTES); + } + + const flagsErrors: IDiscordCommandFlagsErrors | null = DISCORD_MESSAGE_COMMAND_FEATURE_RELEASE_NOTES_FLAGS.getErrors( + messageFlags + ); + + if (!_.isNil(flagsErrors)) { + return this._getWrongFlagsErrorMessageResponse( + message, + DiscordMessageCommandFeatureNameEnum.RELEASE_NOTES, + flagsErrors + ); + } + + const flagsDuplicated: IDiscordCommandFlagsDuplicated | null = DISCORD_MESSAGE_COMMAND_FEATURE_RELEASE_NOTES_FLAGS.getDuplicated( + messageFlags + ); + + if (!_.isNil(flagsDuplicated)) { + return this._getDuplicatedFlagsErrorMessageResponse( + message, + DiscordMessageCommandFeatureNameEnum.RELEASE_NOTES, + flagsDuplicated + ); + } + + const oppositeFlags: IDiscordCommandFlagsOpposite | null = DISCORD_MESSAGE_COMMAND_FEATURE_RELEASE_NOTES_FLAGS.getOpposites( + messageFlags + ); + + if (!_.isNil(oppositeFlags)) { + return this._getOppositeFlagsErrorMessageResponse( + message, + DiscordMessageCommandFeatureNameEnum.RELEASE_NOTES, + oppositeFlags + ); + } + + return DiscordMessageCommandFeatureReleaseNotesService.getInstance().getMessageResponse(message, messageFlags); + } }