Files
laralog/tests/Feature/SablogImportTest.php

171 lines
7.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Category;
use App\Models\Comment;
use App\Models\Link;
use App\Models\Post;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use PDO;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Tags\Tag;
use Tests\TestCase;
class SablogImportTest extends TestCase
{
use RefreshDatabase;
private string $legacyDb;
private string $attachmentsDir;
protected function setUp(): void
{
parent::setUp();
$this->seed();
$this->legacyDb = storage_path('app/tmp/sablog-fixture-'.uniqid().'.sqlite');
$this->attachmentsDir = storage_path('app/tmp/sablog-att-'.uniqid());
File::ensureDirectoryExists($this->attachmentsDir.'/2020/09');
File::put($this->attachmentsDir.'/2020/09/photo.jpg', 'fake-jpg');
File::put($this->attachmentsDir.'/2020/09/archive.zip', 'fake-zip');
$this->buildLegacyDb();
}
protected function tearDown(): void
{
File::delete($this->legacyDb);
File::deleteDirectory($this->attachmentsDir);
parent::tearDown();
}
private function buildLegacyDb(): void
{
$pdo = new PDO('sqlite:'.$this->legacyDb);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TABLE sablog_articles (articleid INTEGER PRIMARY KEY, cid INTEGER, uid INTEGER, title TEXT, description TEXT, content TEXT, keywords TEXT, dateline INTEGER, views INTEGER, comments INTEGER, attachments TEXT, trackbacks INTEGER, closecomment INTEGER, closetrackback INTEGER, visible INTEGER, stick INTEGER, readpassword TEXT)');
$pdo->exec('CREATE TABLE sablog_categories (cid INTEGER PRIMARY KEY, name TEXT, displayorder INTEGER, articles INTEGER)');
$pdo->exec('CREATE TABLE sablog_comments (commentid INTEGER PRIMARY KEY, articleid INTEGER, author TEXT, url TEXT, dateline INTEGER, content TEXT, ipaddress TEXT, visible INTEGER)');
$pdo->exec('CREATE TABLE sablog_links (linkid INTEGER PRIMARY KEY, displayorder INTEGER, name TEXT, url TEXT, note TEXT, visible INTEGER)');
$pdo->exec('CREATE TABLE sablog_tags (tagid INTEGER PRIMARY KEY, tag TEXT, usenum INTEGER, aids TEXT)');
$pdo->exec('CREATE TABLE sablog_settings (title TEXT PRIMARY KEY, value TEXT)');
$pdo->exec('CREATE TABLE sablog_users (userid INTEGER PRIMARY KEY, username TEXT, password TEXT, logincount INTEGER, loginip TEXT, logintime INTEGER, url TEXT, articles INTEGER, regdateline INTEGER, regip TEXT, groupid INTEGER, lastpost INTEGER)');
$pdo->exec('CREATE TABLE sablog_attachments (attachmentid INTEGER PRIMARY KEY, articleid INTEGER, dateline INTEGER, filename TEXT, filetype TEXT, filesize INTEGER, downloads INTEGER, filepath TEXT, thumb_filepath TEXT, thumb_width INTEGER, thumb_height INTEGER, isimage INTEGER)');
$now = time();
$pdo->exec("INSERT INTO sablog_categories VALUES (1, '生活随笔', 0, 1), (2, '技术分享', 1, 1)");
$pdo->exec("INSERT INTO sablog_users VALUES (1, '老管理员', '5f4dcc3b5aa765d61d8327deb882cf99', 5, '127.0.0.1', $now, 'https://example.com', 1, $now, '127.0.0.1', 1, $now)");
$pdo->exec("INSERT INTO sablog_articles VALUES (10, 1, 1, '第一篇老文章', '描述', '<p>正文 HTML</p><p>[attach=1]</p><p>[img=2]</p>', 'php,laravel', $now, 88, 1, 'a:1:{i:0;i:1;}', 0, 0, 0, 1, 0, '')");
$pdo->exec("INSERT INTO sablog_articles VALUES (11, 2, 1, '草稿文章', '', '<p>没写完</p>', '', $now, 0, 0, '', 0, 0, 0, 0, 0, '')");
$pdo->exec("INSERT INTO sablog_comments VALUES (5, 10, '访客', '', $now, '不错', '1.2.3.4', 1), (6, 10, '待审', '', $now, '请审核', '1.2.3.5', 0)");
$pdo->exec("INSERT INTO sablog_links VALUES (1, 0, '友链A', 'https://a.example.com', '', 1)");
$pdo->exec("INSERT INTO sablog_tags VALUES (1, 'laravel', 1, '10'), (2, '随笔', 1, '10')");
$pdo->exec("INSERT INTO sablog_settings VALUES ('name', '老站名'), ('description', '老描述'), ('templatename', 'default')");
$pdo->exec("INSERT INTO sablog_attachments VALUES (1, 10, $now, 'photo.jpg', 'image/jpeg', 100, 3, '2020/09/photo.jpg', '', 0, 0, 1), (2, 10, $now, 'archive.zip', 'application/zip', 200, 1, '2020/09/archive.zip', '', 0, 0, 0)");
}
private function import(array $extra = []): void
{
$params = [
'--driver' => 'sqlite',
'--database' => $this->legacyDb,
'--attachments-dir' => $this->attachmentsDir,
...$extra,
];
Artisan::call('sablog:import', $params);
}
public function test_imports_all_tables_preserving_ids(): void
{
$this->import(['--sync-attachments' => true]);
$this->assertSame(2, Post::count());
$this->assertSame(2, Category::count());
$this->assertSame(2, Comment::count());
$this->assertSame(1, Link::count());
$this->assertSame(2, Tag::count());
// 原始 ID 保留
$this->assertNotNull(Post::find(10));
$this->assertNotNull(Category::find(1));
// 老用户 id=1 覆盖 seed 的 admin(保 ID 语义)
$legacyAdmin = User::find(1);
$this->assertSame('老管理员', $legacyAdmin->name);
$this->assertTrue($legacyAdmin->hasRole('admin'));
$this->assertSame('5f4dcc3b5aa765d61d8327deb882cf99', $legacyAdmin->legacy_md5);
// 状态映射:visible=0 -> draft
$this->assertSame('draft', Post::find(11)->status);
// 评论状态映射:visible=0 -> pending
$this->assertSame('pending', Comment::find(6)->status);
// 角色映射:groupid=1 -> admin
$this->assertTrue(User::find(1)->hasRole('admin'));
// 设置映射
$this->assertSame('老站名', Setting::get('site_name'));
$this->assertSame('sablog', Setting::get('active_theme'));
}
public function test_import_converts_content_to_markdown(): void
{
$this->import(['--sync-attachments' => true, '--convert-markdown' => true]);
$post = Post::find(10);
$this->assertSame('markdown', $post->content_format);
$this->assertStringContainsString('{{attach:1}}', $post->content);
$this->assertStringNotContainsString('[attach=', $post->content);
// 渲染后 [attach] 解析为链接
$html = app(\App\Blog\Services\PostContentRenderer::class)->render($post);
$this->assertStringContainsString('photo.jpg', $html);
$this->assertStringNotContainsString('{{attach', $html);
}
public function test_import_keeps_html_format_without_conversion(): void
{
$this->import(['--sync-attachments' => true]);
$post = Post::find(10);
$this->assertSame('html', $post->content_format);
$this->assertStringContainsString('[attach=1]', $post->content);
// 渲染时解析短代码
$html = app(\App\Blog\Services\PostContentRenderer::class)->render($post);
$this->assertStringContainsString('photo.jpg', $html);
}
public function test_import_attachments_to_media_library(): void
{
$this->import(['--sync-attachments' => true]);
$this->assertSame(2, Media::query()->where('collection_name', 'attachments')->count());
$media = Media::query()->where('custom_properties->legacy_attachmentid', 1)->first();
$this->assertNotNull($media);
$this->assertFalse((bool) $media->getCustomProperty('pending_sync'));
$this->assertSame('photo.jpg', $media->file_name);
}
public function test_import_is_idempotent(): void
{
$this->import(['--sync-attachments' => true]);
$this->import(['--sync-attachments' => true]);
$this->assertSame(2, Post::count());
$this->assertSame(2, Comment::count());
$this->assertSame(2, Media::query()->where('collection_name', 'attachments')->count());
}
}