- Use the correct endpoint (analytics.mattitja.se instead of the non-existent .cloud domain) - Authenticate with the site ingest secret as a Bearer token so the API trusts the forwarded visitor IP and user agent - Forward the visitor Accept-Language header (required by bot detection) - Only track successful GET requests rendering HTML - Short timeouts and a file-based circuit breaker that pauses sending after connection failures or 5xx responses, so no queue, worker or cache is needed and a down API never slows the host site - Log rejected requests instead of failing silently
190 lines
5.6 KiB
PHP
190 lines
5.6 KiB
PHP
<?php
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Mattitja\Analytics\Middleware\AnalyticsMiddleware;
|
|
|
|
beforeEach(function () {
|
|
Http::fake();
|
|
|
|
$this->app->singleton(\Mattitja\Analytics\Services\AnalyticsClient::class, function ($app) {
|
|
return new \Mattitja\Analytics\Services\AnalyticsClient;
|
|
});
|
|
});
|
|
|
|
function createTestRequest(string $url = 'https://example.com/test', string $method = 'GET'): Request
|
|
{
|
|
return Request::create($url, $method);
|
|
}
|
|
|
|
function createMiddleware(): AnalyticsMiddleware
|
|
{
|
|
return new AnalyticsMiddleware;
|
|
}
|
|
|
|
function handleRequest(AnalyticsMiddleware $middleware, Request $request): Response
|
|
{
|
|
$response = $middleware->handle($request, function ($req) {
|
|
return new Response('Test content');
|
|
});
|
|
|
|
$middleware->terminate($request, $response);
|
|
|
|
return $response;
|
|
}
|
|
|
|
it('captures analytics data for web requests', function () {
|
|
$middleware = createMiddleware();
|
|
$request = createTestRequest();
|
|
$request->headers->set('User-Agent', 'Mozilla/5.0 Chrome');
|
|
$request->headers->set('Referer', 'https://google.com');
|
|
$request->server->set('REMOTE_ADDR', '192.168.1.1');
|
|
|
|
$response = handleRequest($middleware, $request);
|
|
|
|
expect($response)->toBeInstanceOf(Response::class);
|
|
|
|
Http::assertSent(function ($request) {
|
|
$data = $request->data();
|
|
|
|
return $request->url() === 'https://analytics.mattitja.se/api/send' &&
|
|
$data['k'] === 'testsiteky' &&
|
|
$data['u'] === 'https://example.com/test' &&
|
|
$data['r'] === 'https://google.com' &&
|
|
$data['ip'] === '192.168.1.1' &&
|
|
$data['ua'] === 'Mozilla/5.0 Chrome';
|
|
});
|
|
});
|
|
|
|
it('does not capture analytics when disabled', function () {
|
|
config(['mattitja-analytics.enabled' => false]);
|
|
|
|
$middleware = new AnalyticsMiddleware;
|
|
$request = Request::create('https://example.com/test', 'GET');
|
|
|
|
$response = $middleware->handle($request, function ($req) {
|
|
return new Response('Test content');
|
|
});
|
|
|
|
expect($response)->toBeInstanceOf(Response::class);
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('ignores configured paths', function () {
|
|
config(['mattitja-analytics.ignore_paths' => ['admin/*']]);
|
|
|
|
$middleware = new AnalyticsMiddleware;
|
|
$request = Request::create('https://example.com/admin/dashboard', 'GET');
|
|
|
|
$response = $middleware->handle($request, function ($req) {
|
|
return new Response('Test content');
|
|
});
|
|
|
|
expect($response)->toBeInstanceOf(Response::class);
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('ignores bot user agents when bots capture is disabled', function () {
|
|
config(['mattitja-analytics.capture_bots' => false]);
|
|
|
|
$middleware = new AnalyticsMiddleware;
|
|
$request = Request::create('https://example.com/test', 'GET');
|
|
$request->headers->set('User-Agent', 'Googlebot/2.1');
|
|
|
|
$response = $middleware->handle($request, function ($req) {
|
|
return new Response('Test content');
|
|
});
|
|
|
|
expect($response)->toBeInstanceOf(Response::class);
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('respects Do Not Track header when configured', function () {
|
|
config(['mattitja-analytics.respect_dnt' => true]);
|
|
|
|
$middleware = createMiddleware();
|
|
$request = createTestRequest();
|
|
$request->headers->set('DNT', '1');
|
|
|
|
$response = handleRequest($middleware, $request);
|
|
|
|
expect($response)->toBeInstanceOf(Response::class);
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('captures user agent in analytics data', function () {
|
|
$middleware = createMiddleware();
|
|
$request = createTestRequest();
|
|
$request->headers->set('User-Agent', 'CustomClient/1.0 (Test)');
|
|
|
|
$response = handleRequest($middleware, $request);
|
|
|
|
Http::assertSent(function ($request) {
|
|
$data = $request->data();
|
|
|
|
return $data['ua'] === 'CustomClient/1.0 (Test)';
|
|
});
|
|
});
|
|
|
|
it('handles empty user agent gracefully', function () {
|
|
$middleware = createMiddleware();
|
|
$request = createTestRequest();
|
|
$request->headers->set('User-Agent', '');
|
|
|
|
$response = handleRequest($middleware, $request);
|
|
|
|
Http::assertSent(function ($request) {
|
|
$data = $request->data();
|
|
|
|
return array_key_exists('ua', $data) && $data['ua'] === '';
|
|
});
|
|
});
|
|
|
|
it('does not capture analytics without an ingest secret', function () {
|
|
config(['mattitja-analytics.ingest_secret' => null]);
|
|
|
|
handleRequest(createMiddleware(), createTestRequest());
|
|
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('forwards the visitor Accept-Language header', function () {
|
|
$request = createTestRequest();
|
|
$request->headers->set('Accept-Language', 'sv-SE,sv;q=0.9');
|
|
|
|
handleRequest(createMiddleware(), $request);
|
|
|
|
Http::assertSent(fn ($request) => $request->hasHeader('Accept-Language', 'sv-SE,sv;q=0.9'));
|
|
});
|
|
|
|
it('ignores non-GET requests', function () {
|
|
handleRequest(createMiddleware(), createTestRequest(method: 'POST'));
|
|
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('ignores JSON responses and redirects', function () {
|
|
$middleware = createMiddleware();
|
|
$request = createTestRequest();
|
|
|
|
foreach ([
|
|
new \Illuminate\Http\JsonResponse(['ok' => true]),
|
|
new \Illuminate\Http\RedirectResponse('https://example.com/elsewhere'),
|
|
] as $response) {
|
|
$result = $middleware->handle($request, fn () => $response);
|
|
$middleware->terminate($request, $result);
|
|
}
|
|
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('ignores requests expecting JSON', function () {
|
|
$request = createTestRequest();
|
|
$request->headers->set('Accept', 'application/json');
|
|
|
|
handleRequest(createMiddleware(), $request);
|
|
|
|
Http::assertNothingSent();
|
|
});
|