From c46816d5052605acd510d482fbc1d1074c9d1994 Mon Sep 17 00:00:00 2001 From: Exanlv Date: Wed, 28 Feb 2024 21:12:48 +0100 Subject: [PATCH] Add unit tests --- fakes/DiscordFake.php | 1 - src/Command/CommandExtension.php | 3 +- tests/Command/CommandExtensionTest.php | 19 -- tests/Command/GlobalCommandExtensionTest.php | 213 +++++++++++++++++++ tests/Command/GuildCommandExtensionTest.php | 213 +++++++++++++++++++ 5 files changed, 428 insertions(+), 21 deletions(-) delete mode 100644 tests/Command/CommandExtensionTest.php create mode 100644 tests/Command/GlobalCommandExtensionTest.php create mode 100644 tests/Command/GuildCommandExtensionTest.php diff --git a/fakes/DiscordFake.php b/fakes/DiscordFake.php index a9e9c83b..005697e8 100644 --- a/fakes/DiscordFake.php +++ b/fakes/DiscordFake.php @@ -16,7 +16,6 @@ public static function get(): Mock|Discord $discord->rest = RestFake::get(); $discord->gateway = GatewayFake::get(); - $discord->interaction = InteractionHandlerFake::get(); return $discord; } diff --git a/src/Command/CommandExtension.php b/src/Command/CommandExtension.php index fa9e5035..0d462665 100644 --- a/src/Command/CommandExtension.php +++ b/src/Command/CommandExtension.php @@ -39,7 +39,8 @@ private function registerListener(Discord $discord): void $discord->gateway->events, Events::INTERACTION_CREATE, fn (InteractionCreate $interactionCreate) => - $interactionCreate?->type === InteractionType::APPLICATION_COMMAND + isset($interactionCreate->type) + && $interactionCreate->type === InteractionType::APPLICATION_COMMAND && isset($this->commandMappings[$interactionCreate->data->id]) ); diff --git a/tests/Command/CommandExtensionTest.php b/tests/Command/CommandExtensionTest.php deleted file mode 100644 index 47176141..00000000 --- a/tests/Command/CommandExtensionTest.php +++ /dev/null @@ -1,19 +0,0 @@ -discord = DiscordFake::get(); - } -} diff --git a/tests/Command/GlobalCommandExtensionTest.php b/tests/Command/GlobalCommandExtensionTest.php new file mode 100644 index 00000000..dd293e48 --- /dev/null +++ b/tests/Command/GlobalCommandExtensionTest.php @@ -0,0 +1,213 @@ +discord = DiscordFake::get(); + } + + public function testItEmitsEventsForApplicationCommands() + { + $commands = [new ApplicationCommand(), new ApplicationCommand()]; + + $commands[0]->id = '::application command 1::'; + $commands[0]->name = 'command-1'; + + $commands[1]->id = '::application command 2::'; + $commands[1]->name = 'command-2'; + + $this->discord->rest->globalCommand->shouldReceive() + ->getCommands('::application id::') + ->andReturns(PromiseFake::get($commands)); + + $extension = new GlobalCommandExtension('::application id::'); + $extension->initialize($this->discord); + + $hasRun = [false, false]; + + $extension->on('command-1', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun[0] = true; + }); + + $extension->on('command-2', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun[1] = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::APPLICATION_COMMAND; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command 1::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $interaction->data->id = '::application command 2::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertTrue($hasRun[0], 'Command 1 did not run'); + $this->assertTrue($hasRun[1], 'Command 2 did not run'); + } + + public function testItDoesNotEmitCommandIfDifferentInteractionOccured() + { + $command = new ApplicationCommand(); + + $command->id = '::application command::'; + $command->name = 'command'; + + $this->discord->rest->globalCommand->shouldReceive() + ->getCommands('::application id::') + ->andReturns(PromiseFake::get([$command])); + + $extension = new GlobalCommandExtension('::application id::'); + $extension->initialize($this->discord); + + $hasRun = false; + + $extension->on('command', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::PING; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertFalse($hasRun, 'Command was emitted wrongfully'); + } + + /** + * @dataProvider nameMappingProvider + * @depends testItEmitsEventsForApplicationCommands + */ + public function testItMapsNamesCorrectly(ApplicationCommand $command, string $expectedName) + { + $command->id = '::application command::'; + + $this->discord->rest->globalCommand->shouldReceive() + ->getCommands('::application id::') + ->andReturns(PromiseFake::get([$command])); + + $extension = new GlobalCommandExtension('::application id::'); + $extension->initialize($this->discord); + + $hasRun = false; + + $extension->on($expectedName, function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::APPLICATION_COMMAND; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertTrue($hasRun, 'Command was emitted wrongfully'); + } + + public static function nameMappingProvider(): array + { + return [ + 'Plain name' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + return $command; + })(), + 'expectedName' => 'command-name' + ], + + 'Nested 1 layer' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'sub'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND; + + return $command; + })(), + 'expectedName' => 'command-name.sub' + ], + + 'Nested 2 layer' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'double'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->name = 'sub'; + $command->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + return $command; + })(), + 'expectedName' => 'command-name.double.sub' + ], + + 'Nested 3 layer' => [ // NOTE: Not supported by Discord + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'double'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->name = 'sub'; + $command->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->options[0]->name = 'dub'; + $command->options[0]->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + return $command; + })(), + 'expectedName' => 'command-name.double.sub.dub' + ], + ]; + } +} diff --git a/tests/Command/GuildCommandExtensionTest.php b/tests/Command/GuildCommandExtensionTest.php new file mode 100644 index 00000000..4eebc7ce --- /dev/null +++ b/tests/Command/GuildCommandExtensionTest.php @@ -0,0 +1,213 @@ +discord = DiscordFake::get(); + } + + public function testItEmitsEventsForApplicationCommands() + { + $commands = [new ApplicationCommand(), new ApplicationCommand()]; + + $commands[0]->id = '::application command 1::'; + $commands[0]->name = 'command-1'; + + $commands[1]->id = '::application command 2::'; + $commands[1]->name = 'command-2'; + + $this->discord->rest->guildCommand->shouldReceive() + ->getCommands('::guild id::', '::application id::') + ->andReturns(PromiseFake::get($commands)); + + $extension = new GuildCommandExtension('::application id::', '::guild id::'); + $extension->initialize($this->discord); + + $hasRun = [false, false]; + + $extension->on('command-1', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun[0] = true; + }); + + $extension->on('command-2', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun[1] = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::APPLICATION_COMMAND; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command 1::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $interaction->data->id = '::application command 2::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertTrue($hasRun[0], 'Command 1 did not run'); + $this->assertTrue($hasRun[1], 'Command 2 did not run'); + } + + public function testItDoesNotEmitCommandIfDifferentInteractionOccured() + { + $command = new ApplicationCommand(); + + $command->id = '::application command::'; + $command->name = 'command'; + + $this->discord->rest->guildCommand->shouldReceive() + ->getCommands('::guild id::', '::application id::') + ->andReturns(PromiseFake::get([$command])); + + $extension = new GuildCommandExtension('::application id::', '::guild id::'); + $extension->initialize($this->discord); + + $hasRun = false; + + $extension->on('command', function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::PING; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertFalse($hasRun, 'Command was emitted wrongfully'); + } + + /** + * @dataProvider nameMappingProvider + * @depends testItEmitsEventsForApplicationCommands + */ + public function testItMapsNamesCorrectly(ApplicationCommand $command, string $expectedName) + { + $command->id = '::application command::'; + + $this->discord->rest->guildCommand->shouldReceive() + ->getCommands('::guild id::', '::application id::') + ->andReturns(PromiseFake::get([$command])); + + $extension = new GuildCommandExtension('::application id::', '::guild id::'); + $extension->initialize($this->discord); + + $hasRun = false; + + $extension->on($expectedName, function (CommandInteraction $firedCommand) use (&$hasRun) { + $hasRun = true; + }); + + $interaction = new InteractionCreate(); + $interaction->type = InteractionType::APPLICATION_COMMAND; + $interaction->data = new InteractionData(); + $interaction->data->id = '::application command::'; + + $this->discord->gateway->events->emit( + Events::INTERACTION_CREATE, + [$interaction] + ); + + $this->assertTrue($hasRun, 'Command was emitted wrongfully'); + } + + public static function nameMappingProvider(): array + { + return [ + 'Plain name' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + return $command; + })(), + 'expectedName' => 'command-name' + ], + + 'Nested 1 layer' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'sub'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND; + + return $command; + })(), + 'expectedName' => 'command-name.sub' + ], + + 'Nested 2 layer' => [ + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'double'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->name = 'sub'; + $command->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + return $command; + })(), + 'expectedName' => 'command-name.double.sub' + ], + + 'Nested 3 layer' => [ // NOTE: Not supported by Discord + 'command' => (function () { + $command = new ApplicationCommand(); + $command->name = 'command-name'; + + $command->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->name = 'double'; + $command->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->name = 'sub'; + $command->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + $command->options[0]->options[0]->options = [new ApplicationCommandOptionStructure()]; + $command->options[0]->options[0]->options[0]->name = 'dub'; + $command->options[0]->options[0]->options[0]->type = ApplicationCommandOptionType::SUB_COMMAND_GROUP; + + return $command; + })(), + 'expectedName' => 'command-name.double.sub.dub' + ], + ]; + } +}