| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Integration\Admin;
- use App\Domain\Auth\Role;
- use App\Domain\Auth\TokenKind;
- use App\Tests\Integration\Support\AppTestCase;
- /**
- * `/api/v1/admin/jobs/{status,trigger}` — admin-only wrapper that the UI
- * uses to invoke jobs without needing the internal token.
- */
- final class JobsAdminControllerTest extends AppTestCase
- {
- public function testStatusRequiresViewer(): void
- {
- $resp = $this->request('GET', '/api/v1/admin/jobs/status');
- self::assertSame(401, $resp->getStatusCode());
- }
- public function testStatusReturnsAllJobs(): void
- {
- $token = $this->createToken(TokenKind::Admin, Role::Viewer);
- $resp = $this->request('GET', '/api/v1/admin/jobs/status', ['Authorization' => 'Bearer ' . $token]);
- self::assertSame(200, $resp->getStatusCode());
- $body = $this->decode($resp);
- self::assertArrayHasKey('jobs', $body);
- foreach (['recompute-scores', 'cleanup-audit', 'enrich-pending', 'refresh-geoip', 'tick'] as $name) {
- self::assertArrayHasKey($name, $body['jobs']);
- }
- }
- public function testTriggerOperatorForbidden(): void
- {
- $token = $this->createToken(TokenKind::Admin, Role::Operator);
- $resp = $this->request(
- 'POST',
- '/api/v1/admin/jobs/trigger/recompute-scores',
- ['Authorization' => 'Bearer ' . $token],
- );
- self::assertSame(403, $resp->getStatusCode());
- }
- public function testTriggerUnknownJobReturns404(): void
- {
- $token = $this->createToken(TokenKind::Admin, Role::Admin);
- $resp = $this->request(
- 'POST',
- '/api/v1/admin/jobs/trigger/does-not-exist',
- ['Authorization' => 'Bearer ' . $token],
- );
- self::assertSame(404, $resp->getStatusCode());
- }
- public function testTriggerRecomputeRunsAndAuditsAsManual(): void
- {
- $token = $this->createToken(TokenKind::Admin, Role::Admin);
- $resp = $this->request(
- 'POST',
- '/api/v1/admin/jobs/trigger/recompute-scores',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- '{}',
- );
- self::assertSame(200, $resp->getStatusCode());
- $body = $this->decode($resp);
- self::assertSame('recompute-scores', $body['job']);
- self::assertSame('success', $body['status']);
- // job_runs.triggered_by = manual
- $row = $this->db->fetchAssociative(
- "SELECT triggered_by FROM job_runs WHERE job_name = 'recompute-scores' ORDER BY id DESC LIMIT 1"
- );
- self::assertSame('manual', $row['triggered_by']);
- // Audit row attributed to the admin token
- $audit = $this->db->fetchAssociative(
- "SELECT actor_kind, action, target_id, details_json FROM audit_log WHERE action = 'job.triggered' ORDER BY id DESC LIMIT 1"
- );
- self::assertIsArray($audit);
- self::assertSame('admin-token', $audit['actor_kind']);
- self::assertSame('recompute-scores', $audit['target_id']);
- $details = json_decode((string) $audit['details_json'], true);
- self::assertSame('manual', $details['triggered_by']);
- }
- public function testRefreshGeoip412UnderMaxmindWithoutKey(): void
- {
- // Swap the downloader binding to MaxMind without a key.
- if (method_exists($this->container, 'set')) {
- /** @var \DI\Container $c */
- $c = $this->container;
- $c->set(
- \App\Infrastructure\Enrichment\Downloaders\GeoIpDownloader::class,
- new \App\Infrastructure\Enrichment\Downloaders\MaxMindDownloader(new \GuzzleHttp\Client(), licenseKey: ''),
- );
- $c->set(
- \App\Application\Admin\JobsAdminController::class,
- $c->make(\App\Application\Admin\JobsAdminController::class),
- );
- $this->app = \App\App\AppFactory::build($this->container);
- }
- $token = $this->createToken(TokenKind::Admin, Role::Admin);
- $resp = $this->request(
- 'POST',
- '/api/v1/admin/jobs/trigger/refresh-geoip',
- ['Authorization' => 'Bearer ' . $token],
- );
- self::assertSame(412, $resp->getStatusCode());
- $body = $this->decode($resp);
- self::assertSame('no_credential', $body['error']);
- self::assertSame('maxmind', $body['provider']);
- self::assertSame('MAXMIND_LICENSE_KEY', $body['missing']);
- }
- }
|