Deploy Ladill Link / deploy (push) Successful in 39s
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>
123 lines
4.0 KiB
PHP
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);
|
|
}
|
|
}
|