From 1ca11ca10ade6a557a8cbb1b9ad84ca285693979 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 08:58:57 +0000 Subject: [PATCH] test: add tests for GithubReleaseModuleCommand handle method Added comprehensive unit tests to cover the `handle` method of `GithubReleaseModuleCommand`. The tests utilize Mockery for partial mocking to safely intercept local git command execution and Laravel's `Http::fake` to mock API interactions with GitHub. Three key scenarios are tested: handling when there are no commits to release, successful release with changelog generation, and successful release without changelog generation. Co-authored-by: juzaweb <47020363+juzaweb@users.noreply.github.com> --- tests/Unit/GithubReleaseModuleCommandTest.php | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tests/Unit/GithubReleaseModuleCommandTest.php diff --git a/tests/Unit/GithubReleaseModuleCommandTest.php b/tests/Unit/GithubReleaseModuleCommandTest.php new file mode 100644 index 0000000..dc3933d --- /dev/null +++ b/tests/Unit/GithubReleaseModuleCommandTest.php @@ -0,0 +1,192 @@ +shouldAllowMockingProtectedMethods(); + + $command->shouldReceive('getGithubToken')->andReturn('fake-token'); + + // Mock getRepo and getLastTag outputs + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git ls-remote --tags'); + }) + ->andReturn('refs/tags/v1.0.0'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git config --get remote.origin.url'); + }) + ->andReturn('fake/repo'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return $cmd === 'git pull'; + }) + ->andReturn(''); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git log'); + }) + ->andReturn(''); + + $this->app[\Illuminate\Contracts\Console\Kernel::class]->registerCommand($command); + + File::shouldReceive('isDirectory')->andReturn(true); + + $this->artisan('github:release', [ + 'path' => '/fake/path' + ]) + ->expectsOutput('Nothing to release') + ->assertExitCode(0); + } + + public function testReleaseWithChangelog() + { + $command = Mockery::mock(GithubReleaseModuleCommand::class . '[runCmd,getGithubToken]') + ->shouldAllowMockingProtectedMethods(); + + $command->shouldReceive('getGithubToken')->andReturn('fake-token'); + + // Mock getRepo and getLastTag outputs + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git ls-remote --tags'); + }) + ->andReturn('refs/tags/v1.0.0'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git config --get remote.origin.url'); + }) + ->andReturn('fake/repo'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return $cmd === 'git pull'; + }) + ->andReturn(''); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git log'); + }) + ->andReturn("* Feature 1\n* Feature 2\n* :construction: WIP"); // :construction: should be filtered out + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return $cmd === 'git add changelog.md'; + }) + ->andReturn(''); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git commit -o changelog.md'); + }) + ->andReturn(''); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return $cmd === 'git push'; + }) + ->andReturn(''); + + $this->app[\Illuminate\Contracts\Console\Kernel::class]->registerCommand($command); + + File::shouldReceive('isDirectory')->andReturn(true); + File::shouldReceive('prepend') + ->withArgs(function ($path, $content) { + return str_contains($path, 'changelog.md') && str_contains($content, 'v1.0.1') && str_contains($content, 'Feature 1') && !str_contains($content, ':construction:'); + }) + ->once(); + + Http::fake([ + 'https://api.github.com/repos/fake/repo/releases' => Http::response(['html_url' => 'https://github.com/fake/repo/releases/tag/1.0.1'], 201), + ]); + + $this->artisan('github:release', [ + 'path' => '/fake/path' + ]) + ->expectsOutput('Add changelog') + ->expectsOutput('Release v1.0.1') + ->expectsOutput('Released url: https://github.com/fake/repo/releases/tag/1.0.1') + ->assertExitCode(0); + + Http::assertSent(function ($request) { + return $request->url() == 'https://api.github.com/repos/fake/repo/releases' && + $request['tag_name'] == '1.0.1' && + $request['body'] == "* Feature 1\n* Feature 2" && + $request->header('Authorization')[0] == 'Bearer fake-token'; + }); + } + + public function testReleaseWithoutChangelog() + { + $command = Mockery::mock(GithubReleaseModuleCommand::class . '[runCmd,getGithubToken]') + ->shouldAllowMockingProtectedMethods(); + + $command->shouldReceive('getGithubToken')->andReturn('fake-token'); + + // Mock getRepo and getLastTag outputs + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git ls-remote --tags'); + }) + ->andReturn('refs/tags/v1.0.0'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git config --get remote.origin.url'); + }) + ->andReturn('fake/repo'); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return $cmd === 'git pull'; + }) + ->andReturn(''); + + $command->shouldReceive('runCmd') + ->withArgs(function ($path, $cmd) { + return is_string($cmd) && str_contains($cmd, 'git log'); + }) + ->andReturn("* Fix bug 1"); + + $this->app[\Illuminate\Contracts\Console\Kernel::class]->registerCommand($command); + + File::shouldReceive('isDirectory')->andReturn(true); + // Prepend shouldn't be called + + Http::fake([ + 'https://api.github.com/repos/fake/repo/releases' => Http::response(['html_url' => 'https://github.com/fake/repo/releases/tag/1.0.1'], 201), + ]); + + $this->artisan('github:release', [ + 'path' => '/fake/path', + '--changelog' => false, + ]) + ->expectsOutput('Release v1.0.1') + ->expectsOutput('Released url: https://github.com/fake/repo/releases/tag/1.0.1') + ->doesntExpectOutput('Add changelog') + ->assertExitCode(0); + + Http::assertSent(function ($request) { + return $request->url() == 'https://api.github.com/repos/fake/repo/releases' && + $request['tag_name'] == '1.0.1' && + $request['body'] == "* Fix bug 1" && + $request->header('Authorization')[0] == 'Bearer fake-token'; + }); + } +}