main: 增强会话功能,支持归档与消息检索
- 添加会话归档接口及相关服务逻辑,并确保幂等性 - 实现单条消息获取接口,校验消息所属会话 - 增加 SSE 增量推送与实时消息订阅功能 - 提供相关的测试用例覆盖新功能 - 更新接口文档,完善 OpenAPI 规范,新增多项示例
This commit is contained in:
@@ -7,6 +7,7 @@ use App\Models\User;
|
||||
use App\Services\ChatService;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -218,4 +219,91 @@ class ChatSessionTest extends TestCase
|
||||
$this->withHeaders($headers)->patchJson("/api/sessions/{$session->session_id}", [])
|
||||
->assertStatus(422);
|
||||
}
|
||||
|
||||
public function test_archive_is_idempotent_and_blocks_user_prompt(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$headers = $this->authHeader($user);
|
||||
$service = app(ChatService::class);
|
||||
$session = $service->createSession('Archive');
|
||||
|
||||
$this->withHeaders($headers)->postJson("/api/sessions/{$session->session_id}/archive")
|
||||
->assertOk()
|
||||
->assertJsonFragment(['status' => ChatSessionStatus::CLOSED]);
|
||||
|
||||
// repeat archive
|
||||
$this->withHeaders($headers)->postJson("/api/sessions/{$session->session_id}/archive")
|
||||
->assertOk()
|
||||
->assertJsonFragment(['status' => ChatSessionStatus::CLOSED]);
|
||||
|
||||
// append should be blocked
|
||||
$this->withHeaders($headers)->postJson("/api/sessions/{$session->session_id}/messages", [
|
||||
'role' => 'USER',
|
||||
'type' => 'user.prompt',
|
||||
'content' => 'blocked',
|
||||
])->assertStatus(403);
|
||||
}
|
||||
|
||||
public function test_get_message_respects_session_scope(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$headers = $this->authHeader($user);
|
||||
$service = app(ChatService::class);
|
||||
$s1 = $service->createSession('S1');
|
||||
$s2 = $service->createSession('S2');
|
||||
|
||||
$msg1 = $service->appendMessage([
|
||||
'session_id' => $s1->session_id,
|
||||
'role' => 'USER',
|
||||
'type' => 'user.prompt',
|
||||
'content' => 'hello',
|
||||
]);
|
||||
|
||||
$this->withHeaders($headers)->getJson("/api/sessions/{$s1->session_id}/messages/{$msg1->message_id}")
|
||||
->assertOk()
|
||||
->assertJsonFragment(['message_id' => $msg1->message_id]);
|
||||
|
||||
// wrong session should 404
|
||||
$this->withHeaders($headers)->getJson("/api/sessions/{$s2->session_id}/messages/{$msg1->message_id}")
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_publish_to_redis_on_append(): void
|
||||
{
|
||||
Redis::shouldReceive('publish')->once()->andReturn(1);
|
||||
|
||||
$service = app(ChatService::class);
|
||||
$session = $service->createSession('Redis Pub');
|
||||
|
||||
$service->appendMessage([
|
||||
'session_id' => $session->session_id,
|
||||
'role' => 'USER',
|
||||
'type' => 'user.prompt',
|
||||
'content' => 'hello',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_sse_backlog_contains_messages(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$headers = $this->authHeader($user);
|
||||
$service = app(ChatService::class);
|
||||
$session = $service->createSession('SSE Session');
|
||||
$service->appendMessage([
|
||||
'session_id' => $session->session_id,
|
||||
'role' => 'USER',
|
||||
'type' => 'user.prompt',
|
||||
'content' => 'hello sse',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders($headers)->get("/api/sessions/{$session->session_id}/sse?after_seq=0");
|
||||
|
||||
$response->assertOk();
|
||||
$content = $response->baseResponse instanceof \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
? $response->streamedContent()
|
||||
: $response->getContent();
|
||||
$this->assertStringContainsString('id: 1', $content);
|
||||
$this->assertStringContainsString('event: message', $content);
|
||||
$this->assertStringContainsString('hello sse', $content);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user