Core system: - AnalyticsUniqId entity (visitor identity with device/os/browser parsing) - AnalyticsEvent entity (page views linked to visitor) - POST /t endpoint with AES-256-GCM encrypted payloads - HMAC-SHA256 visitor hash for anti-tampering - Async processing via Messenger - JS module: auto page_view tracking, setAuth for logged users - Encryption key shared via data-k attribute on body - setAuth only triggers when cookie consent is accepted - Clean CSP: remove old tracker domains (Cloudflare, Umami) 100% first-party, no cookies, invisible to adblockers, RGPD-friendly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
2.6 KiB
PHP
86 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Service;
|
|
|
|
use App\Service\AnalyticsCryptoService;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class AnalyticsCryptoServiceTest extends TestCase
|
|
{
|
|
private AnalyticsCryptoService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->service = new AnalyticsCryptoService('test_secret_key_for_analytics');
|
|
}
|
|
|
|
public function testEncryptDecryptRoundTrip(): void
|
|
{
|
|
$data = ['uid' => 'abc-123', 'url' => '/test', 'title' => 'Test Page'];
|
|
|
|
$encrypted = $this->service->encrypt($data);
|
|
self::assertNotEmpty($encrypted);
|
|
|
|
$decrypted = $this->service->decrypt($encrypted);
|
|
self::assertSame($data, $decrypted);
|
|
}
|
|
|
|
public function testDecryptInvalidPayloadReturnsNull(): void
|
|
{
|
|
self::assertNull($this->service->decrypt('invalid_base64'));
|
|
self::assertNull($this->service->decrypt(base64_encode('short')));
|
|
}
|
|
|
|
public function testDecryptTamperedPayloadReturnsNull(): void
|
|
{
|
|
$encrypted = $this->service->encrypt(['test' => true]);
|
|
$tampered = substr($encrypted, 0, -2).'AA';
|
|
|
|
self::assertNull($this->service->decrypt($tampered));
|
|
}
|
|
|
|
public function testGenerateAndVerifyVisitorHash(): void
|
|
{
|
|
$uid = 'test-uid-123';
|
|
$hash = $this->service->generateVisitorHash($uid);
|
|
|
|
self::assertNotEmpty($hash);
|
|
self::assertTrue($this->service->verifyVisitorHash($uid, $hash));
|
|
}
|
|
|
|
public function testVerifyVisitorHashRejectsTampered(): void
|
|
{
|
|
$uid = 'test-uid-123';
|
|
$hash = $this->service->generateVisitorHash($uid);
|
|
|
|
self::assertFalse($this->service->verifyVisitorHash('different-uid', $hash));
|
|
self::assertFalse($this->service->verifyVisitorHash($uid, 'wrong_hash'));
|
|
}
|
|
|
|
public function testGetKeyForJsReturnsBase64(): void
|
|
{
|
|
$key = $this->service->getKeyForJs();
|
|
self::assertNotEmpty($key);
|
|
self::assertNotFalse(base64_decode($key, true));
|
|
}
|
|
|
|
public function testDifferentSecretsProduceDifferentKeys(): void
|
|
{
|
|
$service2 = new AnalyticsCryptoService('different_secret');
|
|
|
|
$uid = 'test-uid';
|
|
$hash1 = $this->service->generateVisitorHash($uid);
|
|
$hash2 = $service2->generateVisitorHash($uid);
|
|
|
|
self::assertNotSame($hash1, $hash2);
|
|
}
|
|
|
|
public function testEncryptedDataCannotBeDecryptedByDifferentKey(): void
|
|
{
|
|
$service2 = new AnalyticsCryptoService('different_secret');
|
|
|
|
$encrypted = $this->service->encrypt(['test' => true]);
|
|
self::assertNull($service2->decrypt($encrypted));
|
|
}
|
|
}
|