seed(); Setting::set('ai_moderation_enabled', '1'); Setting::set('comment_audit', '1'); // 新评论先进入待审核 Setting::set('llm_base_url', 'https://example.test/v1'); Setting::set('llm_api_key', 'test-key'); Setting::set('llm_model', 'test-model'); } public function test_comment_created_dispatches_moderation_job(): void { \Illuminate\Support\Facades\Queue::fake(); $post = Post::create([ 'title' => '评论审核', 'slug' => 'ai-comment', 'content' => '正文', 'content_format' => 'markdown', 'status' => 'published', 'published_at' => now(), ]); $this->post('/posts/'.$post->id.'/comments', [ 'author_name' => '访客', 'content' => '这是一条需要审核的评论', ]); \Illuminate\Support\Facades\Queue::assertPushed(AiModerateCommentJob::class); } public function test_moderation_job_approves_comment(): void { $post = Post::create([ 'title' => '评论审核', 'slug' => 'ai-comment-2', 'content' => '正文', 'content_format' => 'markdown', 'status' => 'published', 'published_at' => now(), ]); $comment = Comment::create([ 'post_id' => $post->id, 'author_name' => '访客', 'content' => '正常评论内容', 'status' => 'pending', 'created_at' => now(), ]); $llm = $this->mock(LlmClient::class); $llm->shouldReceive('chat')->once()->andReturn('{"verdict":"approved","reason":"正常"}'); (new AiModerateCommentJob($comment->id))->handle($llm); $comment->refresh(); $this->assertSame('published', $comment->status); $this->assertStringContainsString('approved', $comment->ai_review['raw'] ?? ''); } public function test_moderation_job_rejects_spam(): void { $post = Post::create([ 'title' => '评论审核', 'slug' => 'ai-comment-3', 'content' => '正文', 'content_format' => 'markdown', 'status' => 'published', 'published_at' => now(), ]); $comment = Comment::create([ 'post_id' => $post->id, 'author_name' => '广告机', 'content' => '点击链接赚钱', 'status' => 'pending', 'created_at' => now(), ]); $llm = $this->mock(LlmClient::class); $llm->shouldReceive('chat')->once()->andReturn('{"verdict":"spam","reason":"广告"}'); (new AiModerateCommentJob($comment->id))->handle($llm); $comment->refresh(); $this->assertSame('spam', $comment->status); } }