Files
ladill-link/tests/Feature/LinkAnalyticsTest.php
T
isaaccladandCursor 344bf76391
Deploy Ladill Link / deploy (push) Successful in 39s
Add GeoIP country fallback for link click analytics.
Resolve countries from CDN headers first, then MaxMind GeoLite2 via visitor IP, with an artisan command to download and refresh the database.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 23:36:01 +00:00

123 lines
4.0 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\LinkClick;
use App\Models\ShortLink;
use App\Models\User;
use App\Services\Link\LinkAnalyticsService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;
class LinkAnalyticsTest extends TestCase
{
use RefreshDatabase;
private function user(): User
{
return User::create([
'public_id' => (string) Str::uuid(),
'name' => 'Test',
'email' => 'test+'.uniqid().'@example.com',
]);
}
public function test_redirect_records_device_browser_os_and_country(): void
{
config(['link.geoip_database' => base_path('tests/fixtures/GeoLite2-Country-Test.mmdb')]);
$user = $this->user();
ShortLink::create([
'user_id' => $user->id,
'slug' => 'tracked',
'source_app' => 'link',
'source_kind' => 'redirect',
'is_managed_here' => true,
'destination_url' => 'https://example.com/target',
'is_active' => true,
]);
$this->withHeaders([
'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
'CF-IPCountry' => 'GH',
])->get('https://ladl.link/tracked')
->assertRedirect('https://example.com/target');
$click = LinkClick::first();
$this->assertNotNull($click);
$this->assertSame('mobile', $click->device_type);
$this->assertSame('Safari', $click->browser);
$this->assertSame('iOS', $click->os);
$this->assertSame('GH', $click->country);
}
public function test_redirect_records_country_from_ip_when_proxy_header_missing(): void
{
config(['link.geoip_database' => base_path('tests/fixtures/GeoLite2-Country-Test.mmdb')]);
$user = $this->user();
ShortLink::create([
'user_id' => $user->id,
'slug' => 'geo-ip',
'source_app' => 'link',
'source_kind' => 'redirect',
'is_managed_here' => true,
'destination_url' => 'https://example.com/target',
'is_active' => true,
]);
$this->withServerVariables(['REMOTE_ADDR' => '81.2.69.142'])
->withHeaders([
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
])->get('https://ladl.link/geo-ip')
->assertRedirect('https://example.com/target');
$this->assertSame('GB', LinkClick::first()?->country);
}
public function test_breakdown_for_link_groups_clicks_by_device(): void
{
$user = $this->user();
$link = ShortLink::create([
'user_id' => $user->id,
'slug' => 'breakdown',
'source_app' => 'link',
'source_kind' => 'redirect',
'is_managed_here' => true,
'destination_url' => 'https://example.com',
'is_active' => true,
'clicks_total' => 2,
'unique_clicks_total' => 2,
]);
LinkClick::create([
'short_link_id' => $link->id,
'ip_hash' => 'a',
'device_type' => 'mobile',
'browser' => 'Chrome',
'os' => 'Android',
'country' => 'GH',
'is_unique' => true,
'clicked_at' => now(),
]);
LinkClick::create([
'short_link_id' => $link->id,
'ip_hash' => 'b',
'device_type' => 'desktop',
'browser' => 'Chrome',
'os' => 'Windows',
'country' => 'US',
'is_unique' => true,
'clicked_at' => now(),
]);
$devices = app(LinkAnalyticsService::class)->breakdownForLink($link, 'device_type');
$this->assertCount(2, $devices);
$labels = array_column($devices, 'label');
$this->assertContains('Mobile', $labels);
$this->assertContains('Desktop', $labels);
}
}