'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.']); });