| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Integration\Public;
- use App\Domain\Auth\Role;
- use App\Domain\Auth\TokenKind;
- use App\Tests\Integration\Support\AppTestCase;
- final class ReportControllerTest extends AppTestCase
- {
- public function testValidReportInsertedAndScoreUpdated(): void
- {
- $reporterId = $this->createReporter('web-prod');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '203.0.113.42', 'category' => 'brute_force']) ?: null,
- );
- self::assertSame(202, $resp->getStatusCode());
- $body = $this->decode($resp);
- self::assertArrayHasKey('report_id', $body);
- self::assertSame('203.0.113.42', $body['ip']);
- self::assertArrayHasKey('received_at', $body);
- // ip_scores must have a row > 0.
- $score = $this->db->fetchOne(
- "SELECT score FROM ip_scores WHERE ip_text = '203.0.113.42'"
- );
- self::assertNotFalse($score);
- self::assertGreaterThan(0.0, (float) $score);
- }
- public function testManyReportsAccumulateMonotonically(): void
- {
- $reporterId = $this->createReporter('web-many');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- for ($i = 0; $i < 5; $i++) {
- $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '198.51.100.7', 'category' => 'scanner']) ?: null,
- );
- }
- $count = (int) $this->db->fetchOne(
- "SELECT COUNT(*) FROM reports WHERE ip_text = '198.51.100.7'"
- );
- self::assertSame(5, $count);
- $score = (float) $this->db->fetchOne(
- "SELECT score FROM ip_scores WHERE ip_text = '198.51.100.7'"
- );
- // 5 fresh reports × weight 1.0 × decay~1.0 should be ~5.0.
- self::assertEqualsWithDelta(5.0, $score, 0.05);
- }
- public function testWrongKindTokenRejected(): void
- {
- $token = $this->createToken(TokenKind::Admin, role: Role::Admin);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '1.2.3.4', 'category' => 'spam']) ?: null,
- );
- self::assertSame(401, $resp->getStatusCode());
- }
- public function testInvalidIpReturns400(): void
- {
- $reporterId = $this->createReporter('web-bad-ip');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => 'not-an-ip', 'category' => 'spam']) ?: null,
- );
- self::assertSame(400, $resp->getStatusCode());
- $body = $this->decode($resp);
- self::assertSame('validation_failed', $body['error']);
- self::assertArrayHasKey('ip', $body['details']);
- }
- public function testUnknownCategoryReturns400(): void
- {
- $reporterId = $this->createReporter('web-bad-cat');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '1.2.3.4', 'category' => 'no-such']) ?: null,
- );
- self::assertSame(400, $resp->getStatusCode());
- self::assertArrayHasKey('category', $this->decode($resp)['details']);
- }
- public function testMetadataMustBeObject(): void
- {
- $reporterId = $this->createReporter('web-bad-meta');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '1.2.3.4', 'category' => 'spam', 'metadata' => [1, 2, 3]]) ?: null,
- );
- self::assertSame(400, $resp->getStatusCode());
- }
- public function testMetadataExceedingLimitRejected(): void
- {
- $reporterId = $this->createReporter('web-big-meta');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $big = ['blob' => str_repeat('A', 5000)];
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '1.2.3.4', 'category' => 'spam', 'metadata' => $big]) ?: null,
- );
- self::assertSame(400, $resp->getStatusCode());
- }
- public function testInactiveReporterTokenRejected(): void
- {
- $reporterId = $this->createReporter('web-disabled');
- $token = $this->createToken(TokenKind::Reporter, reporterId: $reporterId);
- $this->db->update('reporters', ['is_active' => 0], ['id' => $reporterId]);
- $resp = $this->request(
- 'POST',
- '/api/v1/report',
- ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
- json_encode(['ip' => '1.2.3.4', 'category' => 'spam']) ?: null,
- );
- self::assertSame(401, $resp->getStatusCode());
- }
- }
|