157 lines
6.3 KiB
PHP
157 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Services\PipelineStatus;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\Client\Factory as HttpFactory;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class PipelineStatusTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private const SLUG = 'acme/staging';
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
Cache::flush();
|
|
config()->set('clusev.version', '0.9.61-beta1');
|
|
config()->set('clusev.github_token', 'tok_secret');
|
|
config()->set('clusev.staging_slug', self::SLUG);
|
|
}
|
|
|
|
/** @return array<string,mixed> the step with the given key */
|
|
private function step(array $pipeline, string $key): array
|
|
{
|
|
foreach ($pipeline['steps'] as $s) {
|
|
if ($s['key'] === $key) {
|
|
return $s;
|
|
}
|
|
}
|
|
$this->fail("no step {$key}");
|
|
}
|
|
|
|
/**
|
|
* Reset between pipeline phases inside one test: drop the cached step results AND the HTTP
|
|
* fake stubs. Laravel 13's Http::fake() appends stubs (first match wins), so re-faking the
|
|
* same URL without a fresh factory would keep returning the earlier phase's response.
|
|
*/
|
|
private function resetForNextPhase(): void
|
|
{
|
|
Cache::flush();
|
|
Http::swap(new HttpFactory(app('events')));
|
|
}
|
|
|
|
public function test_non_beta_version_has_no_pipeline(): void
|
|
{
|
|
config()->set('clusev.version', '0.9.60');
|
|
$this->assertNull(app(PipelineStatus::class)->forTrackedBeta());
|
|
}
|
|
|
|
public function test_tag_step_is_done_for_the_running_beta(): void
|
|
{
|
|
Http::fake(['api.github.com/*' => Http::response([], 404)]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('v0.9.61-beta1', $p['tag']);
|
|
$this->assertSame('done', $this->step($p, 'tag')['state']);
|
|
}
|
|
|
|
public function test_mirror_done_when_the_tag_exists_on_github(): void
|
|
{
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/v0.9.61-beta1' => Http::response(['ref' => 'refs/tags/v0.9.61-beta1']),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => []]),
|
|
]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('done', $this->step($p, 'mirror')['state']);
|
|
}
|
|
|
|
public function test_mirror_pending_on_404(): void
|
|
{
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response([], 404),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => []]),
|
|
]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('pending', $this->step($p, 'mirror')['state']);
|
|
}
|
|
|
|
public function test_ci_running_then_done_then_failed(): void
|
|
{
|
|
$run = fn (string $status, ?string $conclusion) => Http::response(['workflow_runs' => [[
|
|
'head_branch' => 'v0.9.61-beta1', 'status' => $status, 'conclusion' => $conclusion,
|
|
'html_url' => 'https://github.com/acme/staging/actions/runs/1',
|
|
]]]);
|
|
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => $run('in_progress', null),
|
|
]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('running', $this->step($p, 'ci')['state']);
|
|
$this->assertTrue($p['ciRunning']);
|
|
$this->assertStringContainsString('runs/1', $this->step($p, 'ci')['url']);
|
|
|
|
$this->resetForNextPhase();
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => $run('completed', 'success'),
|
|
]);
|
|
$this->assertSame('done', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']);
|
|
|
|
$this->resetForNextPhase();
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => $run('completed', 'failure'),
|
|
]);
|
|
$this->assertSame('failed', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']);
|
|
}
|
|
|
|
public function test_ci_pending_when_no_run_matches_the_tag(): void
|
|
{
|
|
Http::fake([
|
|
'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']),
|
|
'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => [['head_branch' => 'v9.9.9', 'status' => 'completed', 'conclusion' => 'success']]]),
|
|
]);
|
|
$this->assertSame('pending', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']);
|
|
}
|
|
|
|
public function test_no_token_makes_mirror_and_ci_unknown_without_calling_github(): void
|
|
{
|
|
config()->set('clusev.github_token', '');
|
|
Http::fake();
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('unknown', $this->step($p, 'mirror')['state']);
|
|
$this->assertSame('unknown', $this->step($p, 'ci')['state']);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_token_is_sent_in_the_header_never_in_the_url(): void
|
|
{
|
|
Http::fake(['api.github.com/*' => Http::response(['ref' => 'x'])]);
|
|
app(PipelineStatus::class)->forTrackedBeta();
|
|
Http::assertSent(fn ($req) => $req->hasHeader('Authorization')
|
|
&& ! str_contains($req->url(), 'tok_secret'));
|
|
}
|
|
|
|
public function test_github_5xx_degrades_to_unknown(): void
|
|
{
|
|
Http::fake(['api.github.com/*' => Http::response('boom', 500)]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('unknown', $this->step($p, 'mirror')['state']);
|
|
$this->assertSame('unknown', $this->step($p, 'ci')['state']);
|
|
}
|
|
|
|
public function test_github_throwing_degrades_to_unknown_never_throws(): void
|
|
{
|
|
Http::fake(['api.github.com/*' => fn () => throw new \RuntimeException('net down')]);
|
|
$p = app(PipelineStatus::class)->forTrackedBeta();
|
|
$this->assertSame('unknown', $this->step($p, 'mirror')['state']);
|
|
$this->assertSame('unknown', $this->step($p, 'ci')['state']);
|
|
}
|
|
}
|