[ 'name' => TEST_VENDOR . '/' . TEST_PACKAGE, 'description' => 'Test', 'time' => '2024-01-01T00:00:00+00:00', 'type' => 'library', 'repository' => 'https://github.com/' . TEST_VENDOR . '/' . TEST_PACKAGE, 'language' => 'PHP', 'versions' => [ 'dev-main' => ['version' => 'dev-main'], 'dev-feature' => ['version' => 'dev-feature'], '1.0.0' => ['version' => '1.0.0'], ], ], ]; } function statsResponse(array $downloads): array { return [ 'labels' => ['2024-01', '2024-02', '2024-03'], 'values' => [$downloads], ]; } function fakePackageResponses(array $statsPerBranch = []): void { $fakes = [TEST_METADATA_URL => Http::response(validMetadata())]; foreach ($statsPerBranch as $branch => $response) { $fakes[TEST_STATS_URL . '/' . $branch . '.json*'] = $response; } Http::fake($fakes); } test('check command with slash format', function () { fakePackageResponses([ 'dev-feature' => Http::response(statsResponse([1, 2, 3])), 'dev-main' => Http::response(statsResponse([1, 2, 3])), ]); $this->artisan('check ' . TEST_VENDOR . '/' . TEST_PACKAGE) ->assertExitCode(0); }); test('check command with two arguments', function () { fakePackageResponses([ 'dev-feature' => Http::response(statsResponse([1, 2, 3])), 'dev-main' => Http::response(statsResponse([1, 2, 3])), ]); $this->artisan('check ' . TEST_VENDOR . ' ' . TEST_PACKAGE) ->assertExitCode(0); }); test('check command rejects invalid input', function (string $args, string $expected) { $this->artisan($args) ->expectsOutputToContain($expected) ->assertExitCode(1); })->with([ 'missing package' => ['check ivuorinen', 'Missing package name'], 'conflicting arguments' => ['check ivuorinen/branch-usage-checker extra', 'Conflicting arguments'], 'invalid vendor' => ['check INVALID!/package-name', 'Invalid vendor name'], 'invalid package' => ['check valid-vendor INVALID!', 'Invalid package name'], ]); test('check command with 404 shows package not found', function () { Http::fake([ 'packagist.org/packages/test-vendor/nonexistent-pkg.json' => Http::response([], 404), ]); $this->artisan('check test-vendor nonexistent-pkg') ->expectsOutputToContain('Package not found') ->assertExitCode(1); }); test('check command with 500 shows server error', function () { Http::fake([ TEST_METADATA_URL => Http::response([], 500), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('Failed to fetch package metadata (HTTP 500)') ->assertExitCode(1); }); test('check command skips branch when stats fetch fails', function () { fakePackageResponses([ 'dev-feature' => Http::response([], 500), 'dev-main' => Http::response(statsResponse([10, 20, 30])), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('Failed to fetch stats for dev-feature') ->assertExitCode(0); }); test('check command skips branch on connection failure', function () { fakePackageResponses([ 'dev-feature' => Http::failedConnection(), 'dev-main' => Http::response(statsResponse([10, 20, 30])), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('Failed to fetch stats for dev-feature') ->assertExitCode(0); }); test('check command stops when all stats fail', function () { fakePackageResponses([ 'dev-feature' => Http::response([], 500), 'dev-main' => Http::response([], 500), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('No statistics found... Stopping.') ->assertExitCode(0); }); test('check command lets TypeError propagate from malformed payload', function () { Http::fake([ TEST_METADATA_URL => Http::response([ 'package' => ['versions' => 'not-an-array'], ]), ]); $this->artisan(TEST_COMMAND); })->throws(\TypeError::class); test('check command shows no suggestions when all branches have downloads', function () { fakePackageResponses([ 'dev-main' => Http::response(statsResponse([10, 20, 30])), 'dev-feature' => Http::response(statsResponse([5, 10, 15])), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('No suggestions available. Good job!') ->assertExitCode(0); }); test('check command suggests branches with zero downloads', function () { fakePackageResponses([ 'dev-main' => Http::response(statsResponse([10, 20, 30])), 'dev-feature' => Http::response(statsResponse([0, 0, 0])), ]); $this->artisan(TEST_COMMAND) ->expectsOutputToContain('Found 1 branches') ->assertExitCode(0); });