From 1d14117292027e31b0929cb86f8cb74a867cfc8a Mon Sep 17 00:00:00 2001 From: Major Hayden Date: Tue, 27 Jan 2026 08:00:36 -0600 Subject: [PATCH] test(e2e): add rlsapi v1 /infer endpoint tests - Add rlsapi_v1.feature with 7 test scenarios - Add rlsapi_v1.py step definitions for response validation - Update test_list.txt to include new feature file Implements LCORE-1223 Signed-off-by: Major Hayden --- .../lightspeed-stack-auth-noop-token.yaml | 3 + .../library-mode/lightspeed-stack.yaml | 3 + .../lightspeed-stack-auth-noop-token.yaml | 3 + .../server-mode/lightspeed-stack.yaml | 3 + tests/e2e/features/rlsapi_v1.feature | 89 +++++++++++++++++++ tests/e2e/features/steps/rlsapi_v1.py | 65 ++++++++++++++ tests/e2e/test_list.txt | 1 + 7 files changed, 167 insertions(+) create mode 100644 tests/e2e/features/rlsapi_v1.feature create mode 100644 tests/e2e/features/steps/rlsapi_v1.py diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml index 4ab62b2b5..0391b8ac6 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml @@ -23,3 +23,6 @@ conversation_cache: authentication: module: "noop-with-token" +inference: + default_provider: openai + default_model: gpt-4o-mini diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack.yaml index 118b917c5..bc5694578 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack.yaml @@ -17,6 +17,9 @@ user_data_collection: transcripts_storage: "/tmp/data/transcripts" authentication: module: "noop" +inference: + default_provider: openai + default_model: gpt-4o-mini mcp_servers: # Mock server with client-provided auth - should appear in mcp-auth/client-options response - name: "github-api" diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml index 960919eda..642624020 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml @@ -29,3 +29,6 @@ conversation_cache: authentication: module: "noop-with-token" +inference: + default_provider: openai + default_model: gpt-4o-mini diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack.yaml index 1dbef61cf..026c551de 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack.yaml @@ -18,6 +18,9 @@ user_data_collection: transcripts_storage: "/tmp/data/transcripts" authentication: module: "noop" +inference: + default_provider: openai + default_model: gpt-4o-mini mcp_servers: # Mock server with client-provided auth - should appear in mcp-auth/client-options response - name: "github-api" diff --git a/tests/e2e/features/rlsapi_v1.feature b/tests/e2e/features/rlsapi_v1.feature new file mode 100644 index 000000000..3b7d41afb --- /dev/null +++ b/tests/e2e/features/rlsapi_v1.feature @@ -0,0 +1,89 @@ +@Authorized +Feature: rlsapi v1 /infer endpoint API tests + + Background: + Given The service is started locally + And REST API service prefix is /v1 + + Scenario: Basic inference with minimal request (question only) + Given The system is in default state + And I set the Authorization header to Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpva + When I use "infer" to ask question with authorization header + """ + {"question": "How do I list files in Linux?"} + """ + Then The status code of the response is 200 + And The rlsapi response should have valid structure + + Scenario: Inference with full context (systeminfo populated) + Given The system is in default state + And I set the Authorization header to Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpva + When I use "infer" to ask question with authorization header + """ + {"question": "How do I configure SELinux?", "context": {"systeminfo": {"os": "RHEL", "version": "9.3", "arch": "x86_64"}}} + """ + Then The status code of the response is 200 + And The rlsapi response should have valid structure + + Scenario: Request without authorization returns 401 + Given The system is in default state + When I use "infer" to ask question + """ + {"question": "How do I list files?"} + """ + Then The status code of the response is 401 + And The body of the response is the following + """ + { + "detail": { + "response": "Missing or invalid credentials provided by client", + "cause": "No Authorization header found" + } + } + """ + + Scenario: Request with empty bearer token returns 401 + Given The system is in default state + And I set the Authorization header to Bearer + When I use "infer" to ask question with authorization header + """ + {"question": "How do I list files?"} + """ + Then The status code of the response is 401 + And The body of the response contains No token found in Authorization header + + Scenario: Empty/whitespace question returns 422 + Given The system is in default state + And I set the Authorization header to Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpva + When I use "infer" to ask question with authorization header + """ + {"question": " "} + """ + Then The status code of the response is 422 + And The body of the response contains Question cannot be empty + + Scenario: Response contains valid structure (data.text, data.request_id) + Given The system is in default state + And I set the Authorization header to Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpva + When I use "infer" to ask question with authorization header + """ + {"question": "What is RHEL?"} + """ + Then The status code of the response is 200 + And The rlsapi response should have valid structure + + Scenario: Multiple requests generate unique request_ids + Given The system is in default state + And I set the Authorization header to Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpva + When I use "infer" to ask question with authorization header + """ + {"question": "First question"} + """ + Then The status code of the response is 200 + And I store the rlsapi request_id + When I use "infer" to ask question with authorization header + """ + {"question": "Second question"} + """ + Then The status code of the response is 200 + And The rlsapi request_id should be different from the stored one diff --git a/tests/e2e/features/steps/rlsapi_v1.py b/tests/e2e/features/steps/rlsapi_v1.py new file mode 100644 index 000000000..3444acb37 --- /dev/null +++ b/tests/e2e/features/steps/rlsapi_v1.py @@ -0,0 +1,65 @@ +"""rlsapi v1 endpoint test steps.""" + +from behave import then, step # pyright: ignore[reportAttributeAccessIssue] +from behave.runner import Context + + +@then("The rlsapi response should have valid structure") +def check_rlsapi_response_structure(context: Context) -> None: + """Check that rlsapi v1 response has valid structure. + + Validates that the response contains: + - data.text (non-empty string) + - data.request_id (non-empty string) + """ + assert context.response is not None, "Request needs to be performed first" + response_json = context.response.json() + + assert "data" in response_json, "Response missing 'data' field" + data = response_json["data"] + + assert "text" in data, "Response data missing 'text' field" + assert isinstance(data["text"], str), "data.text must be a string" + assert len(data["text"]) > 0, "data.text must not be empty" + + assert "request_id" in data, "Response data missing 'request_id' field" + assert isinstance(data["request_id"], str), "data.request_id must be a string" + assert len(data["request_id"]) > 0, "data.request_id must not be empty" + + +@step("I store the rlsapi request_id") +def store_rlsapi_request_id(context: Context) -> None: + """Store the request_id from rlsapi response for later comparison.""" + assert context.response is not None, "Request needs to be performed first" + response_json = context.response.json() + + assert "data" in response_json, "Response missing 'data' field" + assert "request_id" in response_json["data"], "Response data missing 'request_id'" + assert isinstance( + response_json["data"]["request_id"], str + ), "data.request_id must be a string" + assert ( + len(response_json["data"]["request_id"]) > 0 + ), "data.request_id must not be empty" + + context.stored_request_id = response_json["data"]["request_id"] + + +@then("The rlsapi request_id should be different from the stored one") +def check_rlsapi_request_id_different(context: Context) -> None: + """Verify that the current request_id differs from the stored one.""" + assert context.response is not None, "Request needs to be performed first" + assert hasattr(context, "stored_request_id"), "No request_id was stored previously" + + response_json = context.response.json() + assert "data" in response_json, "Response missing 'data' field" + assert "request_id" in response_json["data"], "Response data missing 'request_id'" + + current_request_id = response_json["data"]["request_id"] + assert isinstance(current_request_id, str), "data.request_id must be a string" + assert len(current_request_id) > 0, "data.request_id must not be empty" + stored_request_id = context.stored_request_id + + assert ( + current_request_id != stored_request_id + ), f"request_id should be unique, but got same value: {current_request_id}" diff --git a/tests/e2e/test_list.txt b/tests/e2e/test_list.txt index 804e180cf..eee185425 100644 --- a/tests/e2e/test_list.txt +++ b/tests/e2e/test_list.txt @@ -10,5 +10,6 @@ features/feedback.feature features/health.feature features/info.feature features/query.feature +features/rlsapi_v1.feature features/streaming_query.feature features/rest_api.feature