165 lines
5.7 KiB
PHP
165 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\PageVisit;
|
|
use App\Support\UserAgentParser;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
class PageVisitTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private const CHANNEL = 'fe398bc1add87202c04c27293a032634';
|
|
|
|
#[Test]
|
|
public function hit_records_visit_with_parsed_ua(): void
|
|
{
|
|
Cache::flush();
|
|
$ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1';
|
|
|
|
$this->call('GET', '/statistic/t', [
|
|
'c' => self::CHANNEL,
|
|
'u' => '11111111-2222-4333-8444-555555555555',
|
|
'p' => '/web/'.self::CHANNEL.'/support.html',
|
|
], [], [], [
|
|
'HTTP_USER_AGENT' => $ua,
|
|
])->assertOk()
|
|
->assertHeader('Content-Type', 'image/gif');
|
|
|
|
$row = PageVisit::query()->first();
|
|
$this->assertNotNull($row);
|
|
$this->assertSame(self::CHANNEL, $row->channel_id);
|
|
$this->assertSame('11111111-2222-4333-8444-555555555555', $row->client_uid);
|
|
$this->assertSame('iOS', $row->os);
|
|
$this->assertSame('16.6', $row->os_version);
|
|
$this->assertSame('Safari', $row->browser);
|
|
$this->assertSame('16.6', $row->browser_version);
|
|
$this->assertSame($ua, $row->user_agent);
|
|
}
|
|
|
|
#[Test]
|
|
public function hit_debounces_duplicate_uid(): void
|
|
{
|
|
Cache::flush();
|
|
$params = [
|
|
'c' => self::CHANNEL,
|
|
'u' => 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee',
|
|
];
|
|
$this->get('/statistic/t?'.http_build_query($params))->assertOk();
|
|
$this->get('/statistic/t?'.http_build_query($params))->assertOk();
|
|
$this->assertSame(1, PageVisit::query()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function hit_rejects_bad_channel(): void
|
|
{
|
|
Cache::flush();
|
|
$this->get('/statistic/t?c=not-a-channel&u=11111111-2222-4333-8444-555555555555')->assertOk();
|
|
$this->assertSame(0, PageVisit::query()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function user_agent_parser_handles_chrome_ios(): void
|
|
{
|
|
$parsed = UserAgentParser::parse(
|
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/119.0.6045.109 Mobile/15E148 Safari/604.1'
|
|
);
|
|
$this->assertSame('iOS', $parsed['os']);
|
|
$this->assertSame('17.0', $parsed['os_version']);
|
|
$this->assertSame('Chrome', $parsed['browser']);
|
|
$this->assertSame('119.0.6045', $parsed['browser_version']);
|
|
}
|
|
|
|
#[Test]
|
|
public function dashboard_includes_pv_uv(): void
|
|
{
|
|
Cache::flush();
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u1',
|
|
'os' => 'iOS',
|
|
'os_version' => '16.6',
|
|
'browser' => 'Safari',
|
|
'browser_version' => '16.6',
|
|
'created_at' => now(),
|
|
]);
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u1',
|
|
'os' => 'iOS',
|
|
'os_version' => '16.6',
|
|
'browser' => 'Safari',
|
|
'browser_version' => '16.6',
|
|
'created_at' => now(),
|
|
]);
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u2',
|
|
'os' => 'Android',
|
|
'os_version' => '13',
|
|
'browser' => 'Chrome',
|
|
'browser_version' => '120.0.0',
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u3',
|
|
'os' => 'iOS',
|
|
'os_version' => '17.0',
|
|
'browser' => 'Safari',
|
|
'browser_version' => '17.0',
|
|
'created_at' => now(),
|
|
]);
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u4',
|
|
'os' => 'iOS',
|
|
'os_version' => '16.0',
|
|
'browser' => 'Chrome',
|
|
'browser_version' => '119.0',
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
|
$this->actingAs($admin, 'admin')
|
|
->getJson(route('admin.dashboard.data', ['range' => 'today']))
|
|
->assertOk()
|
|
->assertJsonPath('data.pv', 5)
|
|
->assertJsonPath('data.uv', 4)
|
|
->assertJsonPath('data.effective_pv', 2)
|
|
->assertJsonPath('data.effective_uv', 1)
|
|
->assertJsonPath('data.by_os.0.label', 'iOS')
|
|
->assertJsonPath('data.by_os.0.pv', 4)
|
|
->assertJsonPath('data.by_os.1.label', '其他')
|
|
->assertJsonPath('data.by_os.1.pv', 1)
|
|
->assertJsonStructure(['data' => ['by_os', 'by_ios_version', 'pv', 'uv', 'effective_pv', 'effective_uv']]);
|
|
}
|
|
|
|
#[Test]
|
|
public function visits_groups_by_os_version(): void
|
|
{
|
|
PageVisit::query()->create([
|
|
'channel_id' => self::CHANNEL,
|
|
'client_uid' => 'u1',
|
|
'os' => 'iOS',
|
|
'os_version' => '16.6',
|
|
'browser' => 'Safari',
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
|
$this->actingAs($admin, 'admin')
|
|
->getJson(route('admin.visits.groups', ['range' => 'today', 'group' => 'os_version']))
|
|
->assertOk()
|
|
->assertJsonPath('data.rows.0.label', 'iOS 16.6')
|
|
->assertJsonPath('data.rows.0.pv', 1)
|
|
->assertJsonPath('data.rows.0.uv', 1);
|
|
}
|
|
}
|