Spaces:
Sleeping
Sleeping
| use Illuminate\Http\Client\Request as HttpRequest; | |
| use Illuminate\Support\Facades\Http; | |
| use Inertia\Testing\AssertableInertia as Assert; | |
| function studentAuthUserCookie(): string | |
| { | |
| return json_encode([ | |
| 'email' => 'student@example.com', | |
| 'id' => 11, | |
| 'identity_number' => '123456', | |
| 'name' => 'Alma', | |
| 'role' => 'student', | |
| 'status' => 'active', | |
| ], JSON_THROW_ON_ERROR); | |
| } | |
| test('mahasiswa routes redirect guests to login', function () { | |
| $this->get(route('mahasiswa')) | |
| ->assertRedirect(route('login')); | |
| }); | |
| test('mahasiswa dashboard page can be rendered through laravel proxy', function () { | |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/courses*' => Http::response([ | |
| 'data' => [ | |
| [ | |
| 'description' => 'Materi struktur data', | |
| 'id' => 101, | |
| 'title' => 'Struktur Data', | |
| ], | |
| ], | |
| 'limit' => 100, | |
| 'page' => 1, | |
| 'total' => 1, | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions*' => Http::response([ | |
| 'data' => [ | |
| [ | |
| 'course_id' => '101', | |
| 'created_at' => '2026-05-01T10:00:00Z', | |
| 'message_count' => 2, | |
| 'title' => 'Apa itu stack?', | |
| 'updated_at' => '2026-05-01T10:00:00Z', | |
| 'user_id' => 'student-1', | |
| 'uuid_id' => 'session-1', | |
| ], | |
| ], | |
| 'pagination' => [ | |
| 'limit' => 20, | |
| 'page' => 1, | |
| 'total' => 1, | |
| 'total_pages' => 1, | |
| ], | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->get(route('mahasiswa')) | |
| ->assertOk() | |
| ->assertInertia(fn (Assert $page) => $page | |
| ->component('mahasiswa') | |
| ->where('backendMessage', null) | |
| ->has('courses', 1) | |
| ->where('courses.0.title', 'Struktur Data') | |
| ->has('chatSessions', 1) | |
| ->where('chatSessions.0.uuid_id', 'session-1') | |
| ->where('selectedCourseId', '101')); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' | |
| && str_starts_with($request->url(), 'https://rag.test/api/v1/chats/sessions?') | |
| && str_contains($request->url(), 'page=1') | |
| && str_contains($request->url(), 'limit=20') | |
| && $request->hasHeader('Authorization', 'Bearer token')); | |
| }); | |
| test('mahasiswa chat session can be created and initial message sent through laravel proxy', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'rest', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/chats/sessions/session-1/messages' => Http::response([ | |
| 'content' => 'Stack adalah struktur data LIFO.', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions' => Http::response([ | |
| 'course_id' => '101', | |
| 'created_at' => '2026-05-01T10:00:00Z', | |
| 'message_count' => 0, | |
| 'title' => 'Apa itu stack?', | |
| 'updated_at' => '2026-05-01T10:00:00Z', | |
| 'user_id' => 'student-1', | |
| 'uuid_id' => 'session-1', | |
| ], 201), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->from(route('mahasiswa')) | |
| ->post(route('mahasiswa.sessions.store'), [ | |
| 'course_id' => '101', | |
| 'title' => 'Apa itu stack?', | |
| ]) | |
| ->assertRedirect(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertSessionMissing('sevima_raghub_pending_initial_chat.session-1') | |
| ->assertSessionHasNoErrors(); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request['course_id'] === '101' | |
| && $request['title'] === 'Apa itu stack?'); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1/messages' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request['content'] === 'Apa itu stack?'); | |
| }); | |
| test('mahasiswa chat detail page renders session and history from api', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'rest', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/courses*' => Http::response([ | |
| 'data' => [ | |
| [ | |
| 'id' => 101, | |
| 'title' => 'Struktur Data', | |
| ], | |
| ], | |
| 'limit' => 100, | |
| 'page' => 1, | |
| 'total' => 1, | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions/session-1/messages' => Http::response([ | |
| 'data' => [ | |
| [ | |
| 'content' => 'Apa itu stack?', | |
| 'created_at' => '2026-05-01T10:00:00Z', | |
| 'role' => 'user', | |
| 'sources' => [], | |
| 'uuid_id' => 'user-message-1', | |
| ], | |
| [ | |
| 'content' => 'Stack adalah struktur data LIFO.', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ], | |
| ], | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions/session-1' => Http::response([ | |
| 'course_id' => '101', | |
| 'created_at' => '2026-05-01T10:00:00Z', | |
| 'message_count' => 2, | |
| 'title' => 'Apa itu stack?', | |
| 'updated_at' => '2026-05-01T10:01:00Z', | |
| 'user_id' => 'student-1', | |
| 'uuid_id' => 'session-1', | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions*' => Http::response([ | |
| 'data' => [], | |
| 'pagination' => [ | |
| 'limit' => 20, | |
| 'page' => 1, | |
| 'total' => 0, | |
| 'total_pages' => 0, | |
| ], | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->get(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertOk() | |
| ->assertInertia(fn (Assert $page) => $page | |
| ->component('mahasiswa-chat') | |
| ->where('sessionId', 'session-1') | |
| ->where('currentSession.uuid_id', 'session-1') | |
| ->has('messages', 2) | |
| ->where('messages.0.role', 'user') | |
| ->where('messages.1.role', 'assistant') | |
| ->where('selectedCourseId', '101')); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1'); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1/messages'); | |
| }); | |
| test('mahasiswa chat detail page can be rendered with direct session messages', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'direct', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/courses*' => Http::response([ | |
| 'data' => [ | |
| [ | |
| 'id' => 101, | |
| 'title' => 'Struktur Data', | |
| ], | |
| ], | |
| 'limit' => 100, | |
| 'page' => 1, | |
| 'total' => 1, | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions/session-1/messages' => Http::response([ | |
| 'data' => [], | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions/session-1' => Http::response([ | |
| 'course_id' => '101', | |
| 'created_at' => '2026-05-01T10:00:00Z', | |
| 'message_count' => 0, | |
| 'title' => 'Apa itu stack?', | |
| 'updated_at' => '2026-05-01T10:00:00Z', | |
| 'user_id' => 'student-1', | |
| 'uuid_id' => 'session-1', | |
| ]), | |
| 'https://rag.test/api/v1/chats/sessions*' => Http::response([ | |
| 'data' => [], | |
| 'pagination' => [ | |
| 'limit' => 20, | |
| 'page' => 1, | |
| 'total' => 0, | |
| 'total_pages' => 0, | |
| ], | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->withSession([ | |
| 'sevima_raghub_direct_messages.session-1' => [ | |
| [ | |
| 'content' => 'Jawaban bot', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ], | |
| ], | |
| ]) | |
| ->get(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertOk() | |
| ->assertInertia(fn (Assert $page) => $page | |
| ->component('mahasiswa-chat') | |
| ->where('backendMessage', null) | |
| ->where('sessionId', 'session-1') | |
| ->where('currentSession.uuid_id', 'session-1') | |
| ->has('messages', 1) | |
| ->where('messages.0.content', 'Jawaban bot') | |
| ->where('selectedCourseId', '101')); | |
| }); | |
| test('mahasiswa direct chat message can be sent through laravel proxy', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'direct', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/ai/query' => Http::response([ | |
| 'content' => 'Stack adalah struktur data LIFO.', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'message_id' => 'message-1', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->from(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->post(route('mahasiswa.messages.store', ['sessionId' => 'session-1']), [ | |
| 'content' => 'Apa itu stack?', | |
| 'course_id' => '101', | |
| ]) | |
| ->assertRedirect(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertSessionHasNoErrors(); | |
| $directMessages = session('sevima_raghub_direct_messages.session-1'); | |
| expect($directMessages)->toHaveCount(2) | |
| ->and($directMessages[0]['content'])->toBe('Apa itu stack?') | |
| ->and($directMessages[1]['content'])->toBe('Stack adalah struktur data LIFO.'); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/ai/query' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request['course_id'] === '101' | |
| && $request['prompt'] === 'Apa itu stack?'); | |
| }); | |
| test('mahasiswa rest chat message can be sent through laravel proxy', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'rest', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/chats/sessions/session-1/messages' => Http::response([ | |
| 'content' => 'Stack adalah struktur data LIFO.', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->from(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->post(route('mahasiswa.messages.store', ['sessionId' => 'session-1']), [ | |
| 'content' => 'Apa itu stack?', | |
| 'course_id' => '101', | |
| ]) | |
| ->assertRedirect(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertSessionHasNoErrors(); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1/messages' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request['content'] === 'Apa itu stack?'); | |
| }); | |
| test('mahasiswa stream mode json chat message uses rest endpoint', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'stream', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/chats/sessions/session-1/messages' => Http::response([ | |
| 'content' => 'Stack adalah struktur data LIFO.', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->withHeaders(['Accept' => 'application/json']) | |
| ->postJson(route('mahasiswa.messages.store', ['sessionId' => 'session-1']), [ | |
| 'content' => 'Apa itu stack?', | |
| 'course_id' => '101', | |
| ]) | |
| ->assertSuccessful() | |
| ->assertJsonPath('assistantMessage.content', 'Stack adalah struktur data LIFO.'); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1/messages' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request['content'] === 'Apa itu stack?'); | |
| }); | |
| test('mahasiswa stream chat message forwards backend server sent events', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'stream', | |
| ]); | |
| $streamBody = "data: {\"content\":\"Stack adalah struktur data LIFO.\"}\n\n"; | |
| Http::fake([ | |
| 'https://rag.test/api/v1/chats/sessions/session-1/stream' => Http::response( | |
| $streamBody, | |
| 200, | |
| ['Content-Type' => 'text/event-stream'], | |
| ), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->withHeaders(['Accept' => 'text/event-stream']) | |
| ->post(route('mahasiswa.messages.store', ['sessionId' => 'session-1']), [ | |
| 'content' => 'Apa itu stack?', | |
| 'course_id' => '101', | |
| ]) | |
| ->assertSuccessful() | |
| ->assertStreamed() | |
| ->assertHeader('Content-Type', 'text/event-stream; charset=utf-8') | |
| ->assertStreamedContent($streamBody); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1/stream' | |
| && $request->hasHeader('Authorization', 'Bearer token') | |
| && $request->hasHeader('Accept', 'text/event-stream') | |
| && $request['content'] === 'Apa itu stack?'); | |
| }); | |
| test('mahasiswa chat message uses backend error message', function () { | |
| config([ | |
| 'services.rag.base_url' => 'https://rag.test/api/v1', | |
| 'services.rag.chat_mode' => 'direct', | |
| ]); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/ai/query' => Http::response([ | |
| 'message' => 'I do not know based on available materials.', | |
| ], 422), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->from(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->post(route('mahasiswa.messages.store', ['sessionId' => 'session-1']), [ | |
| 'content' => 'Apa itu stack?', | |
| 'course_id' => '101', | |
| ]) | |
| ->assertRedirect(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->assertSessionHasErrors(['message' => 'I do not know based on available materials.']); | |
| }); | |
| test('mahasiswa chat session can be deleted through laravel proxy', function () { | |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); | |
| Http::fake([ | |
| 'https://rag.test/api/v1/chats/sessions/session-1' => Http::response([]), | |
| ]); | |
| $this->withCookie('sevima_raghub_auth_token', 'token') | |
| ->withCookie('sevima_raghub_auth_user', studentAuthUserCookie()) | |
| ->withSession([ | |
| 'sevima_raghub_direct_messages.session-1' => [ | |
| [ | |
| 'content' => 'Jawaban bot', | |
| 'created_at' => '2026-05-01T10:01:00Z', | |
| 'role' => 'assistant', | |
| 'sources' => [], | |
| 'uuid_id' => 'message-1', | |
| ], | |
| ], | |
| ]) | |
| ->from(route('mahasiswa.show', ['sessionId' => 'session-1'])) | |
| ->delete(route('mahasiswa.destroy', ['sessionId' => 'session-1']), [ | |
| 'active_session_id' => 'session-1', | |
| ]) | |
| ->assertRedirect(route('mahasiswa')) | |
| ->assertSessionMissing('sevima_raghub_direct_messages.session-1') | |
| ->assertSessionHasNoErrors(); | |
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'DELETE' | |
| && $request->url() === 'https://rag.test/api/v1/chats/sessions/session-1' | |
| && $request->hasHeader('Authorization', 'Bearer token')); | |
| }); | |