From 2dc875a04b9420c15fc6b1a3661e20bcf9883ad2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:07:03 +0000 Subject: [PATCH 1/3] Initial plan From c60079dcd74a5f621a020fa68caea0adf867b92b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:12:55 +0000 Subject: [PATCH 2/3] Add integration tests for DevPullCommand with mocked GitHub API Co-authored-by: sak0a <24781653+sak0a@users.noreply.github.com> --- src/Console/Commands/DevPullCommand.php | 10 +- .../Console/DevPullCommandTest.php | 274 ++++++++++++++++++ 2 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 tests/Integration/Console/DevPullCommandTest.php diff --git a/src/Console/Commands/DevPullCommand.php b/src/Console/Commands/DevPullCommand.php index 370585d8..1c67a84a 100644 --- a/src/Console/Commands/DevPullCommand.php +++ b/src/Console/Commands/DevPullCommand.php @@ -22,6 +22,14 @@ class DevPullCommand extends Command protected $description = 'Pull the latest Notur framework code from GitHub for development'; + private ?Client $client = null; + + public function __construct(?Client $client = null) + { + parent::__construct(); + $this->client = $client; + } + public function handle(): int { $branch = $this->argument('branch'); @@ -33,7 +41,7 @@ public function handle(): int $repo = config('notur.repository', self::DEFAULT_REPO); $noturRoot = base_path('vendor/notur/notur'); - $client = new Client([ + $client = $this->client ?? new Client([ 'timeout' => 30, 'connect_timeout' => 10, 'headers' => [ diff --git a/tests/Integration/Console/DevPullCommandTest.php b/tests/Integration/Console/DevPullCommandTest.php new file mode 100644 index 00000000..db16cd42 --- /dev/null +++ b/tests/Integration/Console/DevPullCommandTest.php @@ -0,0 +1,274 @@ +set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + $app['config']->set('notur.repository', 'sak0a/notur'); + } + + protected function setUp(): void + { + parent::setUp(); + $this->loadMigrationsFrom(__DIR__ . '/../../../database/migrations'); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + public function test_dry_run_shows_what_would_be_done_without_making_changes(): void + { + // Mock the GitHub API response for commit info + $mockClient = Mockery::mock(Client::class); + + $mockResponse = new Response(200, [], json_encode([ + 'sha' => 'abc123def456abc123def456abc123def456abc1', + 'commit' => [ + 'message' => 'Test commit message', + 'author' => [ + 'name' => 'Test Author', + 'date' => '2024-01-15T10:30:00Z', + ], + ], + ])); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/master') + ->andReturn($mockResponse); + + // Bind the mock client to the service container + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['--dry-run' => true]) + ->expectsOutput('[DRY RUN] Would download and extract commit abc123de to ' . base_path('vendor/notur/notur')) + ->expectsOutput('[DRY RUN] Would rebuild frontend bridge') + ->expectsOutput('[DRY RUN] Would copy bridge.js and tailwind.css to public/notur/') + ->assertExitCode(0); + } + + public function test_dry_run_with_no_rebuild_option(): void + { + $mockClient = Mockery::mock(Client::class); + + $mockResponse = new Response(200, [], json_encode([ + 'sha' => 'abc123def456abc123def456abc123def456abc1', + 'commit' => [ + 'message' => 'Test commit message', + 'author' => [ + 'name' => 'Test Author', + 'date' => '2024-01-15T10:30:00Z', + ], + ], + ])); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/master') + ->andReturn($mockResponse); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['--dry-run' => true, '--no-rebuild' => true]) + ->expectsOutput('[DRY RUN] Would download and extract commit abc123de to ' . base_path('vendor/notur/notur')) + ->doesntExpectOutput('[DRY RUN] Would rebuild frontend bridge') + ->assertExitCode(0); + } + + public function test_dry_run_with_specific_commit(): void + { + $mockClient = Mockery::mock(Client::class); + + $mockResponse = new Response(200, [], json_encode([ + 'sha' => 'specific123commit456specific123commit456spe', + 'commit' => [ + 'message' => 'Specific commit message', + 'author' => [ + 'name' => 'Test Author', + 'date' => '2024-01-15T10:30:00Z', + ], + ], + ])); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/specific123') + ->andReturn($mockResponse); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['commit' => 'specific123', '--dry-run' => true]) + ->expectsOutput('[DRY RUN] Would download and extract commit specific to ' . base_path('vendor/notur/notur')) + ->assertExitCode(0); + } + + public function test_handles_invalid_ref_error(): void + { + $mockClient = Mockery::mock(Client::class); + + $request = new Request('GET', 'https://api.github.com/repos/sak0a/notur/commits/invalid-ref'); + $exception = new RequestException( + 'Not Found', + $request, + new Response(404, [], '{"message":"Not Found"}') + ); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/invalid-ref') + ->andThrow($exception); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['branch' => 'invalid-ref', '--dry-run' => true]) + ->expectsOutputToContain('Failed to fetch commit info') + ->assertExitCode(1); + } + + public function test_handles_network_error_on_commit_fetch(): void + { + $mockClient = Mockery::mock(Client::class); + + $request = new Request('GET', 'https://api.github.com/repos/sak0a/notur/commits/master'); + $exception = new RequestException( + 'Connection timeout', + $request + ); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/master') + ->andThrow($exception); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['--dry-run' => true]) + ->expectsOutputToContain('Failed to fetch commit info') + ->assertExitCode(1); + } + + public function test_handles_malformed_api_response(): void + { + $mockClient = Mockery::mock(Client::class); + + // Response missing 'sha' field + $mockResponse = new Response(200, [], json_encode([ + 'commit' => [ + 'message' => 'Test commit message', + ], + ])); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/master') + ->andReturn($mockResponse); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['--dry-run' => true]) + ->expectsOutputToContain('Failed to fetch commit info') + ->assertExitCode(1); + } + + public function test_displays_commit_information(): void + { + $mockClient = Mockery::mock(Client::class); + + $mockResponse = new Response(200, [], json_encode([ + 'sha' => 'abc123def456abc123def456abc123def456abc1', + 'commit' => [ + 'message' => "Add new feature\n\nDetailed description here", + 'author' => [ + 'name' => 'Jane Developer', + 'date' => '2024-01-15T10:30:00Z', + ], + ], + ])); + + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/sak0a/notur/commits/develop') + ->andReturn($mockResponse); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['branch' => 'develop', '--dry-run' => true]) + ->expectsOutput(' Branch: develop') + ->expectsOutput(' Commit: abc123de') + ->expectsOutput(' Author: Jane Developer') + ->expectsOutput(' Date: 2024-01-15T10:30:00Z') + ->expectsOutput(' Message: Add new feature') + ->assertExitCode(0); + } + + public function test_uses_custom_repository_from_config(): void + { + config(['notur.repository' => 'custom/repo']); + + $mockClient = Mockery::mock(Client::class); + + $mockResponse = new Response(200, [], json_encode([ + 'sha' => 'abc123def456abc123def456abc123def456abc1', + 'commit' => [ + 'message' => 'Test commit', + 'author' => [ + 'name' => 'Test Author', + 'date' => '2024-01-15T10:30:00Z', + ], + ], + ])); + + // Should use custom/repo instead of default + $mockClient->shouldReceive('get') + ->once() + ->with('https://api.github.com/repos/custom/repo/commits/master') + ->andReturn($mockResponse); + + $this->app->bind(Client::class, function () use ($mockClient) { + return $mockClient; + }); + + $this->artisan('notur:dev:pull', ['--dry-run' => true]) + ->assertExitCode(0); + } +} From 3ccdccb0be4b8868925a15a079f7bbfaf7c864a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:13:45 +0000 Subject: [PATCH 3/3] Fix test expectation for specific commit SHA format Co-authored-by: sak0a <24781653+sak0a@users.noreply.github.com> --- tests/Integration/Console/DevPullCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Console/DevPullCommandTest.php b/tests/Integration/Console/DevPullCommandTest.php index db16cd42..cbf1ec6b 100644 --- a/tests/Integration/Console/DevPullCommandTest.php +++ b/tests/Integration/Console/DevPullCommandTest.php @@ -110,7 +110,7 @@ public function test_dry_run_with_specific_commit(): void $mockClient = Mockery::mock(Client::class); $mockResponse = new Response(200, [], json_encode([ - 'sha' => 'specific123commit456specific123commit456spe', + 'sha' => 'abcd1234567890abcdef1234567890abcdef1234', 'commit' => [ 'message' => 'Specific commit message', 'author' => [ @@ -130,7 +130,7 @@ public function test_dry_run_with_specific_commit(): void }); $this->artisan('notur:dev:pull', ['commit' => 'specific123', '--dry-run' => true]) - ->expectsOutput('[DRY RUN] Would download and extract commit specific to ' . base_path('vendor/notur/notur')) + ->expectsOutput('[DRY RUN] Would download and extract commit abcd1234 to ' . base_path('vendor/notur/notur')) ->assertExitCode(0); }