| <?php |
|
|
| use Illuminate\Http\Client\Request as HttpRequest; |
| use Illuminate\Support\Facades\Http; |
| use Inertia\Testing\AssertableInertia as Assert; |
|
|
| function adminAuthUserCookie(): string |
| { |
| return json_encode([ |
| 'email' => 'admin@example.com', |
| 'id' => 99, |
| 'identity_number' => 'ADM001', |
| 'name' => 'Admin', |
| 'role' => 'admin', |
| 'status' => 'active', |
| ], JSON_THROW_ON_ERROR); |
| } |
|
|
| test('admin dashboard fetches rag config and users from backend', function () { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| 'https://rag.test/api/v1/admin/rag/config' => Http::response([ |
| 'llm' => [], |
| 'path' => 'config/rag.yaml', |
| 'retrieval' => [], |
| 'vector_db' => [], |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/llm' => Http::response([ |
| 'max_tokens' => 2048, |
| 'model' => 'gpt-test', |
| 'request_timeout' => 30, |
| 'temperature' => 0.2, |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/retrieval' => Http::response([ |
| 'bm25_weight' => 0.2, |
| 'candidate_pool_size' => 20, |
| 'dense_weight' => 0.6, |
| 'enable_reranker' => true, |
| 'history_turns' => 3, |
| 'lexical_weight' => 0.2, |
| 'max_context_chars' => 6000, |
| 'neighbor_window' => 1, |
| 'reranker_model' => 'rerank-test', |
| 'similarity_threshold' => 0.1, |
| 'top_k' => 4, |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/vector_db' => Http::response([ |
| 'chunk_overlap' => 100, |
| 'chunk_size' => 800, |
| 'embedding_model' => 'embed-test', |
| ]), |
| 'https://rag.test/api/v1/user*' => Http::response([ |
| 'founds' => [ |
| [ |
| 'created_at' => '2026-05-01T10:00:00Z', |
| 'email' => 'admin@example.com', |
| 'id' => 99, |
| 'identity_number' => 'ADM001', |
| 'is_active' => true, |
| 'is_superuser' => true, |
| 'name' => 'Admin', |
| 'role' => 'admin', |
| 'updated_at' => '2026-05-01T10:00:00Z', |
| ], |
| ], |
| 'search_options' => [], |
| ]), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->get(route('admin.dashboard', ['name' => 'Admin'])) |
| ->assertOk() |
| ->assertInertia(fn (Assert $page) => $page |
| ->component('admin/dashboard') |
| ->where('backendMessage', null) |
| ->where('ragConfig.path', 'config/rag.yaml') |
| ->where('ragConfig.llm.model', 'gpt-test') |
| ->has('users', 1) |
| ->where('users.0.email', 'admin@example.com') |
| ->where('userFilters.name', 'Admin')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' |
| && $request->url() === 'https://rag.test/api/v1/admin/rag/config' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' |
| && $request->url() === 'https://rag.test/api/v1/admin/rag/config/llm' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' |
| && $request->url() === 'https://rag.test/api/v1/admin/rag/config/vector_db' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' |
| && $request->url() === 'https://rag.test/api/v1/admin/rag/config/retrieval' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'GET' |
| && str_starts_with($request->url(), 'https://rag.test/api/v1/user?') |
| && $request['name'] === 'Admin' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
| }); |
|
|
| test('admin config pages render separate inertia components', function (string $routeName, string $component) { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| 'https://rag.test/api/v1/admin/rag/config' => Http::response([ |
| 'llm' => [], |
| 'path' => 'config/rag.yaml', |
| 'retrieval' => [], |
| 'vector_db' => [], |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/llm' => Http::response([ |
| 'model' => 'gpt-test', |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/retrieval' => Http::response([ |
| 'top_k' => 4, |
| ]), |
| 'https://rag.test/api/v1/admin/rag/config/vector_db' => Http::response([ |
| 'chunk_size' => 800, |
| ]), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->get(route($routeName)) |
| ->assertOk() |
| ->assertInertia(fn (Assert $page) => $page |
| ->component($component) |
| ->where('backendMessage', null) |
| ->where('ragConfig.path', 'config/rag.yaml')); |
| })->with([ |
| 'rag config' => ['admin.rag-config', 'admin/rag-config'], |
| 'llm config' => ['admin.llm-config', 'admin/llm-config'], |
| 'vector retrieval config' => ['admin.vector-retrieval-config', 'admin/vector-retrieval-config'], |
| ]); |
|
|
| test('admin config update endpoints are proxied to backend', function (string $routeName, string $redirectRouteName, string $url, array $payload, string $assertKey) { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| $url => Http::response($payload), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->patch(route($routeName), $payload) |
| ->assertRedirect(route($redirectRouteName)) |
| ->assertSessionHasNoErrors(); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'PATCH' |
| && $request->url() === $url |
| && $request[$assertKey] === $payload[$assertKey] |
| && $request->hasHeader('Authorization', 'Bearer token')); |
| })->with([ |
| 'llm' => [ |
| 'admin.rag.llm.update', |
| 'admin.llm-config', |
| 'https://rag.test/api/v1/admin/rag/config/llm', |
| ['model' => 'gpt-test', 'temperature' => 0.1], |
| 'model', |
| ], |
| 'vector db' => [ |
| 'admin.rag.vector-db.update', |
| 'admin.vector-retrieval-config', |
| 'https://rag.test/api/v1/admin/rag/config/vector_db', |
| ['chunk_size' => 900, 'embedding_model' => 'embed-test'], |
| 'chunk_size', |
| ], |
| 'retrieval' => [ |
| 'admin.rag.retrieval.update', |
| 'admin.vector-retrieval-config', |
| 'https://rag.test/api/v1/admin/rag/config/retrieval', |
| ['enable_reranker' => true, 'top_k' => 5], |
| 'top_k', |
| ], |
| ]); |
|
|
| test('admin api key generation is proxied and flashes generated key', function () { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| 'https://rag.test/api/v1/auth/api-keys' => Http::response([ |
| 'api_key' => 'rag_sk_test', |
| 'expires_at' => null, |
| ], 201), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->post(route('admin.api-keys.store')) |
| ->assertRedirect(route('admin.dashboard')) |
| ->assertSessionHas('generatedApiKey.api_key', 'rag_sk_test') |
| ->assertSessionHasNoErrors(); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' |
| && $request->url() === 'https://rag.test/api/v1/auth/api-keys' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
| }); |
|
|
| test('admin user crud actions are proxied to backend', function () { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| 'https://rag.test/api/v1/user' => Http::response([ |
| 'email' => 'student@example.com', |
| 'id' => 10, |
| 'role' => 'student', |
| ]), |
| 'https://rag.test/api/v1/user/10' => Http::response([ |
| 'email' => 'student@example.com', |
| 'id' => 10, |
| 'role' => 'student', |
| ]), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->post(route('admin.users.store'), [ |
| 'email' => 'student@example.com', |
| 'identity_number' => 'M001', |
| 'is_active' => true, |
| 'is_superuser' => false, |
| 'name' => 'Student', |
| 'role' => 'student', |
| ]) |
| ->assertRedirect(route('admin.dashboard')) |
| ->assertSessionHasNoErrors(); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->patch(route('admin.users.update', ['userId' => 10]), [ |
| 'email' => 'student@example.com', |
| 'identity_number' => 'M001', |
| 'is_active' => false, |
| 'is_superuser' => false, |
| 'name' => 'Student Updated', |
| 'role' => 'student', |
| ]) |
| ->assertRedirect(route('admin.dashboard')) |
| ->assertSessionHasNoErrors(); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->delete(route('admin.users.destroy', ['userId' => 10])) |
| ->assertRedirect(route('admin.dashboard')) |
| ->assertSessionHasNoErrors(); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'POST' |
| && $request->url() === 'https://rag.test/api/v1/user' |
| && $request['email'] === 'student@example.com' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'PATCH' |
| && $request->url() === 'https://rag.test/api/v1/user/10' |
| && $request['name'] === 'Student Updated' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
|
|
| Http::assertSent(fn (HttpRequest $request): bool => $request->method() === 'DELETE' |
| && $request->url() === 'https://rag.test/api/v1/user/10' |
| && $request->hasHeader('Authorization', 'Bearer token')); |
| }); |
|
|
| test('admin actions use backend error messages', function () { |
| config(['services.rag.base_url' => 'https://rag.test/api/v1']); |
|
|
| Http::fake([ |
| 'https://rag.test/api/v1/user' => Http::response([ |
| 'detail' => [ |
| ['msg' => 'Email already exists.'], |
| ], |
| ], 422), |
| ]); |
|
|
| $this->withCookie('sevima_raghub_auth_token', 'token') |
| ->withCookie('sevima_raghub_auth_user', adminAuthUserCookie()) |
| ->from(route('admin.dashboard')) |
| ->post(route('admin.users.store'), [ |
| 'email' => 'admin@example.com', |
| 'role' => 'admin', |
| ]) |
| ->assertRedirect(route('admin.dashboard')) |
| ->assertSessionHasErrors(['user' => 'Email already exists.']); |
| }); |
|
|