From 65f12219c43fbd16cdf8bff91d6f3027c45ae21b Mon Sep 17 00:00:00 2001 From: Sofiya Nuryyeva Date: Thu, 6 Aug 2026 07:02:59 -0400 Subject: [PATCH 1/3] adding new verificatioin flows --- .../library-mode/lightspeed-stack-rbac.yaml | 3 + .../server-mode/lightspeed-stack-rbac.yaml | 3 + tests/e2e/features/rbac.feature | 140 ++++++++++++++++++ .../e2e/features/steps/llm_query_response.py | 15 +- tests/e2e/features/steps/rbac.py | 2 +- tests/e2e/mock_jwks_server/generate_tokens.py | 6 + tests/e2e/mock_jwks_server/server.py | 15 +- tests/e2e/utils/utils.py | 24 ++- 8 files changed, 194 insertions(+), 14 deletions(-) diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml index 4461d60f5..0252d190e 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml @@ -78,6 +78,9 @@ authorization: - "info" - "model_override" - "rlsapi_v1_infer" + - "responses" + - "manage_prompts" + - "read_prompts" # Viewer role can only read (no mutations) - role: "viewer" actions: diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml index ea5bce5f3..6fb7166a2 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml @@ -76,6 +76,9 @@ authorization: - "info" - "model_override" - "rlsapi_v1_infer" + - "responses" + - "manage_prompts" + - "read_prompts" # Viewer role can only read (no mutations) - role: "viewer" actions: diff --git a/tests/e2e/features/rbac.feature b/tests/e2e/features/rbac.feature index 07d711ddb..a25cd7d30 100644 --- a/tests/e2e/features/rbac.feature +++ b/tests/e2e/features/rbac.feature @@ -124,3 +124,143 @@ Feature: Role-Based Access Control (RBAC) When I access REST API endpoint "conversations" using HTTP GET method Then The status code of the response is 403 And The body of the response contains does not have permission + + Scenario: No-role user cannot hit responses API - returns 403 + And I authenticate as "no_role" user + And I use "responses" to ask question with authorization header + """ + { + "input": "Tell me a short bedtime story. Max length: 15 sentences", + "model": "{PROVIDER}/{MODEL}", + "instructions": "You are a helpful assistant", + "stream": false + } + """ + Then The status code of the response is 403 + And The body of the response contains does not have permission + + # ============================================ + # Testing resource ownership - authorization checks + # ============================================ + + Scenario: Query on another user's conversation - returns 403 + And I authenticate as "user" user + And I use "query" to ask question with authorization header + """ + {"query": "Give me first 6 digits of PI", "model": "{MODEL}", "provider": "{PROVIDER}"} + """ + And The status code of the response is 200 + And I store conversation details + And I authenticate as "user2" user + When I use "query" to ask question with authorization header + """ + {"query": "Say hi", "conversation_id": "{CONVERSATION_ID}", "model": "{MODEL}", "provider": "{PROVIDER}"} + """ + Then The status code of the response is 403 + And The body of the response contains does not have permission + And The body of the response is the following + """ + { + "detail": { + "response": "User does not have permission to perform this action", + "cause": "User user2-id does not have permission to read conversation with ID {CONVERSATION_ID}" + } + } + """ + + Scenario: Streaming query on another user's conversation - returns 403 + And I authenticate as "user" user + And I use "streaming_query" to ask question with authorization header + """ + {"query": "Give me first 6 digits of PI", "model": "{MODEL}", "provider": "{PROVIDER}"} + """ + And I wait for the response to be completed + And The status code of the response is 200 + And I authenticate as "user2" user + When I use "streaming_query" to ask question with same conversation_id + """ + {"query": "Say hi!", "system_prompt": "provide coding assistance", "model": "{MODEL}", "provider": "{PROVIDER}"} + """ + Then The status code of the response is 403 + And The body of the response contains does not have permission + And The body of the response is the following + """ + { + "detail": { + "response": "User does not have permission to perform this action", + "cause": "User user2-id does not have permission to read conversation with ID {CONVERSATION_ID}" + } + } + """ + + Scenario: Accessing another user's responses returns 403 Forbidden - returns 403 + And I authenticate as "user" user + And I use "responses" to ask question with authorization header + """ + { + "input": "List all colors of the rainbow", + "model": "{PROVIDER}/{MODEL}", + "instructions": "You are a helpful assistant", + "stream": false + } + """ + And The status code of the response is 200 + And I store conversation details + And I authenticate as "user2" user + When I use "responses" to ask question with authorization header + """ + { + "input": "Hello there!", + "model": "{PROVIDER}/{MODEL}", + "instructions": "You are a helpful assistant", + "stream": false, + "conversation": "{CONVERSATION_ID}" + } + """ + Then The status code of the response is 403 + And The body of the response contains does not have permission + And The body of the response is the following + """ + { + "detail": { + "response": "User does not have permission to perform this action", + "cause": "User user2-id does not have permission to read conversation with ID {CONVERSATION_ID}" + } + } + """ + + Scenario: Accessing another user's streaming responses - returns 403 + And I authenticate as "user" user + And I use "responses" to ask question with authorization header + """ + { + "input": "List all colors of the rainbow", + "model": "{PROVIDER}/{MODEL}", + "instructions": "You are a helpful assistant", + "stream": true + } + """ + And The status code of the response is 200 + And I store conversation details + And I authenticate as "user2" user + When I use "responses" to ask question with authorization header + """ + { + "input": "Hello there!", + "model": "{PROVIDER}/{MODEL}", + "instructions": "You are a helpful assistant", + "stream": true, + "conversation": "{CONVERSATION_ID}" + } + """ + Then The status code of the response is 403 + And The body of the response contains does not have permission + And The body of the response is the following + """ + { + "detail": { + "response": "User does not have permission to perform this action", + "cause": "User user2-id does not have permission to read conversation with ID {CONVERSATION_ID}" + } + } + """ diff --git a/tests/e2e/features/steps/llm_query_response.py b/tests/e2e/features/steps/llm_query_response.py index 30d327b3c..50ff3cbd4 100644 --- a/tests/e2e/features/steps/llm_query_response.py +++ b/tests/e2e/features/steps/llm_query_response.py @@ -8,7 +8,11 @@ from behave import step, then # pyright: ignore[reportAttributeAccessIssue] from behave.runner import Context -from tests.e2e.utils.utils import replace_placeholders, request_with_transient_retry +from tests.e2e.utils.utils import ( + parse_responses_sse_final_response_object, + replace_placeholders, + request_with_transient_retry, +) # Longer timeout for Prow/OpenShift with CPU-based vLLM DEFAULT_LLM_TIMEOUT = 180 if os.getenv("RUNNING_PROW") else 120 @@ -183,7 +187,14 @@ def ask_question_too_long_authorized(context: Context, endpoint: str) -> None: @step("I store conversation details") def store_conversation_details(context: Context) -> None: """Store details about the conversation.""" - context.response_data = json.loads(context.response.text) + try: + context.response_data = json.loads(context.response.text) + except json.JSONDecodeError: + context.response_data = _parse_streaming_response(context.response.text) + if not context.response_data.get("conversation_id"): + terminal = parse_responses_sse_final_response_object(context.response.text) + context.response_data["conversation"] = terminal.get("conversation") + context.response_data["conversation_id"] = terminal.get("conversation") @step('I use "{endpoint}" to ask question with same conversation_id') diff --git a/tests/e2e/features/steps/rbac.py b/tests/e2e/features/steps/rbac.py index 82224f21d..1ccfd6ca2 100644 --- a/tests/e2e/features/steps/rbac.py +++ b/tests/e2e/features/steps/rbac.py @@ -28,7 +28,7 @@ def authenticate_as_role(context: Context, role: str) -> None: Fetches pre-generated test tokens from the mock JWKS server and sets the appropriate Authorization header for the given role. - Available roles: admin, user, viewer, query_only, no_role + Available roles: admin, user, user2, viewer, query_only, no_role """ tokens = get_test_tokens() diff --git a/tests/e2e/mock_jwks_server/generate_tokens.py b/tests/e2e/mock_jwks_server/generate_tokens.py index 6f7f8ef65..4770fe687 100644 --- a/tests/e2e/mock_jwks_server/generate_tokens.py +++ b/tests/e2e/mock_jwks_server/generate_tokens.py @@ -70,6 +70,12 @@ def int_to_base64url(n: int, length: int) -> str: "permissions": ["query"], }, "no_role": {"sub": "norole-id", "name": "No Role User", "admin": False}, + "user2": { + "sub": "user2-id", + "name": "Regular User 2", + "admin": False, + "role": "user", + }, } tokens = {} diff --git a/tests/e2e/mock_jwks_server/server.py b/tests/e2e/mock_jwks_server/server.py index 8af50712a..2d3cedc08 100644 --- a/tests/e2e/mock_jwks_server/server.py +++ b/tests/e2e/mock_jwks_server/server.py @@ -17,19 +17,20 @@ "kid": "test-key-1", "use": "sig", "alg": "RS256", - "n": "oYVHa2Map44Cbd32Ai_37P0CHnRqDU3U3MKNdHIBkkI9nl3VV1K-4GqyKmTHl6CfSDUh5_JrKJJblyY-u7MOB9kzrPn-7it2FBfmhnc8RNBRvvF2ti3_IC-an3-2t_qYP30ZtkTx4EtgbBhd6iCJFjDU6Rjl9fxtYG-jZR_91UDOyJSQnVCV9-1oRWhkA_5y6l1gNKu-Kc92Kmu39fhxOs4U8399MPI-RkGcJkGRP86xg9lNx1Linz7UzEENGvYhPf2peaUvCZSElSZcgy_EFI3Tag9-nSTDCZPmxv1ugAohMGIgtQtmBI-K30_1Mek_RPwMOXh2EX5ThVhvIbXXmw", + "n": "z02KGhSys-53buuo9yyNIpkqXs1vbbpb63RSdkCTr-U4UPdkr60Y_mhHzIT9BIbwTnHr4nc6B088PxsMf8-mjAfFnmZEMRYJ1wNDLkZpmcCklqK4wRxiohTaiyNCblb9aKNvAw9kZ9UDTcndCv6JaABaYlCO-uUW226fc514N-x34azoAIgQl6JvwIofTddRjorGVpXJ_2wnpcNYQdjxVXsAPpCJttNUxm85SRe-IsBWoZC2t9v8TpxVyUe8b2FvUolgbeJ5w2-mBZG2DSGrTka6SZrdLyLRGp2PM3iltWIlhIMPtSkiMQ1-Ydc0q44wJml1HZgsVOb6MrDDW9Sn-Q", "e": "AQAB", } ] } -# Pre-generated test tokens (valid for 10 years from Jan 2026) +# Pre-generated test tokens (valid for 10 years from Aug 2026) TOKENS = { - "admin": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3NjgzNzkzMDcsImV4cCI6MjA4MzczOTMwNywic3ViIjoiYWRtaW4tdXNlci1pZCIsIm5hbWUiOiJBZG1pbiBVc2VyIiwiYWRtaW4iOnRydWUsInJvbGUiOiJhZG1pbiJ9.BFVQDG6Io59q3gYwt54c2NJEI5q3MUIXwRIlPhu3v1F9inrZOPtLKBUbjgkF6OpU5xe5ck09BsKwvuNX0gBS8iVHb4vetkd2hwqDljk8wHEOs_E8X4_3Yqoz5NFgs1Mx3fd66xuWy2TtwLaIZ3Mwx6aGERZBXBvY_5yP7HI2oUQ4jVHe6TZL4qa927YFXtNZv11DBq9FkrZRaFtACt6iikEA-UD-v4N1szWlBvn_JCsmB9gQc8txN8FfNH_h01qTJWfuqBbK-6pSpgjr9pS4dG3AuFpBucp-eaBDCGlC7kz085_I10hnZhGCoB7XD1VOTILtwdMvjB_6VFd4f-0EiQ", - "user": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3NjgzNzkzMDcsImV4cCI6MjA4MzczOTMwNywic3ViIjoidXNlci1pZCIsIm5hbWUiOiJSZWd1bGFyIFVzZXIiLCJhZG1pbiI6ZmFsc2UsInJvbGUiOiJ1c2VyIn0.eocDRnf8Cbw1wEee3mmZGDyPlUGAFN8-dH9LmEChAYSSQ6g94vRhL4yoQCiDJA76Vuzmt9CJKGxHNlvmqZh82rEPezLDq0H_a3qgPZq_9uS_dzl3c-ityojbI0YBE1DWm_29vhEv9lfVaJc9EalSObN5xttq32GJ8-1kFWATgP--n5SP3omoljLxAmVMlQlU2gjB7trH7OyLLHp4-DqsUzUUXsNg1pj-BmWT7pkw36QjRfintX-GEcSMbHABX0g2CXUKuLAWsqbbyLPPtDPlPFQh6HmZna74-riWJqOYg6pL4XSUwl_DKxafjZ_wCysSULUjR_i2E6XlgBlIRAZC5A", - "viewer": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3NjgzNzkzMDcsImV4cCI6MjA4MzczOTMwNywic3ViIjoidmlld2VyLWlkIiwibmFtZSI6IlZpZXdlciBVc2VyIiwiYWRtaW4iOmZhbHNlLCJyb2xlIjoidmlld2VyIn0.a_6FLiAw9cg-hUNNtdv1WyQtwkMJCmMnXXB1fOcGNyjgYSL-z3-bW12FOGH86MTxdcXKxsvfaw5FrUqOZVUitlo3AjqFdZJaZkKJO23-eMvWwaCME90wPkM6nW0L95nygkko8SkX4WWoccPBqqDRG3QxzsBxq6Lu7NdSnpz2iGlZcYwmCZdIhmBqgxuQbUPeMQlxJtoiv6AUXA8lMJbHAcftrwoQ2oWVKIRwjK4VHn-s8G5HzK3ezlDKz31kNxg74rQo4jZzlRkWVHQ2wByabyaRGCysoM7KrNuCJwjs4W_tShb9nM50zTc_jrcjeur3LbtDt3XOPNyKpVxElpAgYw", - "query_only": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3NjgzNzkzMDcsImV4cCI6MjA4MzczOTMwNywic3ViIjoicXVlcnktaWQiLCJuYW1lIjoiUXVlcnkgVXNlciIsImFkbWluIjpmYWxzZSwicGVybWlzc2lvbnMiOlsicXVlcnkiXX0.fOEEnWhVajeBSGxxMhzmcHPJ1ZWoDrz-JgFGngoanbEA8NGoQcNnbZvnDGg_Jn6_4YtFwQ5NnVb50lZSw046HapLPRfbQsz2yxCzW1FaX2Jvc8-d8kciZPh_aWwxv2foAEii_8hG9ZisRvUIDoBUHmtJdxGcRcilgXywIc4BS15Cxi-Ib7RPkqsKN56vIy30-vTeV0bwcAXVjmpPiekIrFqZX-rLpFptjouSdBTF8PEvh_K1pmFteMfe1QJzonDYYNdMTOsQRy-c0KH9fX7oWhw9xJvvTlh0pDZbh1zAk6EYeiSCavq6myxRGyImNT0wQ7IuzWywsBUmLauRxf6W5Q", - "no_role": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3NjgzNzkzMDcsImV4cCI6MjA4MzczOTMwNywic3ViIjoibm9yb2xlLWlkIiwibmFtZSI6Ik5vIFJvbGUgVXNlciIsImFkbWluIjpmYWxzZX0.jBpNj3HKfSwMNED8J-o3A847aJg7LBDiHJeEB_tRUYJZhd4U6wMv2iun7fpdkns6b-70qtVqOd8xd-BUOsiXNpldjVWI8GaXsqh0q63X622ZYGItMWX0BGgwg2LoQgmN2G1k0xQIs1unCQn0wDmSB6ZFBAMDDSYLpZ0KOLNknh5NUX4GJyMXYgz3FZj6my0ypxWOnmOmC4iL5HGUszq6GB-K7nu75TMOuMZh4FxhbxIvWoT59y-NVKzoTxrkU4w6s0_gfcbqjieJd0sJbp-T4xm3qap7PF4yuFjwkptfbT_hiwAgbOsguTE1LbZQXOz0tdzuORQq7J9skyt2LCjV7w", + "admin": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoiYWRtaW4tdXNlci1pZCIsIm5hbWUiOiJBZG1pbiBVc2VyIiwiYWRtaW4iOnRydWUsInJvbGUiOiJhZG1pbiJ9.Uk6zMwMXySVNQ3Cn4mAKAYVjJevVYh7zCi9VDojffmhYc0R0-3mZrhwhOQfg76s-zE1r2UZNqaYJAMdfozuDqWa_bn4Y9GDrtpXrCs2XM_N-oEIeSLag1Ki6MG-nQfPzW1vvwJ10JjPRcOk-qjM46OrRgotT4gmfWe9i7xm3l26EtygPaiS4Kux7XJy5LVSIqycRrLMdKwJRKaKJ6vXD8_NnFJKRQQyQCyULRjHRthIUGdiQ-jZDVLt9ZySuLBMzxUKfPSSCiibJct0yZNPjVdWc54t_aUu1jXx--lX5qlY5giwtnVG5Ww0jeD6kMXdmhqI9CWHJfuamznSlYQvgoA", + "user": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoidXNlci1pZCIsIm5hbWUiOiJSZWd1bGFyIFVzZXIiLCJhZG1pbiI6ZmFsc2UsInJvbGUiOiJ1c2VyIn0.GdCavkm5inxF4KA45dhvrCoyhe04qkK14wKhluF4-mktCsPtD4A-lw-M5Oz76QAqMMeS9Kr56BOGuDh0kXOaOiEO6V_7IAqZOlR_34fP_taIBPv3NA753Ql35EgeblC-ohQH_ZzUUJCMvepiuFw1jP1bGDvoqPKlrjYHbwedFEWjrxMJhZo7hM91qU738NnVkaEvAOAOGBkeA_Ho8asR7-5e1XxUS3Z7bXY9o_nqmwnQ-pWWf0litugHfIsgsJ9VLqWWpdlytfScqIMKbhWZuJ7Hgk1zXjW7EHLEkgCGUL-fmDTI4-BxQqSPn8vgNd9HqBNWzBFXcV3XQpgr3AgfHw", + "viewer": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoidmlld2VyLWlkIiwibmFtZSI6IlZpZXdlciBVc2VyIiwiYWRtaW4iOmZhbHNlLCJyb2xlIjoidmlld2VyIn0.tFRd7KDs_ZtNOS3Xnr6eE2dJEqY-MWpJaVH8A8W55gxpvoytp3-EBh1XHpKb3k0Q0qBazJMsss6eat-B7RymlsRaeqAapPiZ3QJssi_sxZcu4JSk-typEDM70rakhYss8JgrYbw5fAQNcpu6y3AqzOQr3MCVcW_sGp-ghTMC0qIbvx8Tcw0wS6Qtlj5hdDyKOxH9IJzlDivU58QCASR_qkc-RySzUQu7dxTGoDmG9UN4XZF1I490TxcQsBDGM9qf7uLm4MnbjFaJJYP86nB-j2166VWVyUyyEN1SIwB3sZ51KUDGpIiTXAld0dPPr2Sqv1lz1qqIGxEpaE1xrzRk7g", + "query_only": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoicXVlcnktaWQiLCJuYW1lIjoiUXVlcnkgVXNlciIsImFkbWluIjpmYWxzZSwicGVybWlzc2lvbnMiOlsicXVlcnkiXX0.oKwNnjiepUzfWxDoVeto9XL96K5uWX_DWMBIb8cde7k8ujU-RUSiGArnmhXfGxOLD0jIqJsJkwVyjeQUNDLGa1hD49v__dWCeDdEbemPI-K3i5jeE1W96igk0MxtOBlkj4SKnDcerY4y93J3lSjDVZsx4nOzJU8jI0T8jT20K0Si-zRtDdcwEtGbu-LFGHHbCea3fRSUls8Vd6NL1rI7-v0m6ztljxEjE2GJSLCKCbngtsJ8ni_lvJBG28Ys7VVOmNiZVF2NdIpFQzmKVCWM_-_A0uH4yXh2JsHGapMJyaleqHzO7bAzLXsUx2mF-U1wJQXBwnpv2kKeuawMXlwJuw", + "no_role": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoibm9yb2xlLWlkIiwibmFtZSI6Ik5vIFJvbGUgVXNlciIsImFkbWluIjpmYWxzZX0.s6aJBlINEiryWJlLuQzObGKNhMapEVNLZAmh6Qxrx8s_KHeyJpzBLedF7qFxDMMJD7M7zuGpPKTvWo1OT4yxl-XLi94QMkfx4-_UlH6bTa1Kq-gWW4xM6q8BpuV2uEAzfSONpX-Cqdys5ywyc9CraiWdkfVVZcy_Z-7mu-D9vs9-3_OIyibqT4P1eKJwrZsICvqdQtRvdcVTwn6ETzJ8jekup-4b5tDcSulj04S1zlCUEKpuYFSs15mviLCAYX2nW_AaOnvQz-fIOA6Q2S8ifm1L85jwef3BWIeLf4ZWwUO_wN_od2pyCkxqiGQxyd3WnBVS-BJxfjdEl10sj2ypog", + "user2": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3ODU4NzY5NzYsImV4cCI6MjEwMTIzNjk3Niwic3ViIjoidXNlcjItaWQiLCJuYW1lIjoiUmVndWxhciBVc2VyIDIiLCJhZG1pbiI6ZmFsc2UsInJvbGUiOiJ1c2VyIn0.D_rGmLkOjDyFm4NIOdL3eI1dlDbA4nZ7lEpXCGHQF2FGCmkfiyMeBggxihVeC0WOwZbk1PLD2h-9LgpX-g8PhsYPC6c6aG8pFOvUgV8mjn4xKr5Hi7-IopQuOXDd4N7Ea1zW__WTzwciGUNOFTT4c1GrAa2ZQyPibCErCrxtju6Zan_UzUr8tU0wT031HgcYv3JqE33AT6u0RhO94MHQABo0Zl02vkvacTz3EVIfICR_v_LmOxRraIyrzS5-w7UInaBep2EnaY5su2scAcYqpGERVE600SuZUbjLXYfxc7zTFpL0Bub8VqvwLe48QyilH-ylvCopUp4OJ6uIhWHUzw", } diff --git a/tests/e2e/utils/utils.py b/tests/e2e/utils/utils.py index 0597c4846..5d611b9f8 100644 --- a/tests/e2e/utils/utils.py +++ b/tests/e2e/utils/utils.py @@ -524,17 +524,21 @@ def wait_for_lightspeed_stack_http_ready( def replace_placeholders(context: Context, text: str) -> str: - """Replace {MODEL}, {PROVIDER}, and {VECTOR_STORE_ID} placeholders from context. + """Replace known placeholders in *text* with values from the Behave context. + + Supported placeholders: ``{MODEL}``, ``{PROVIDER}``, ``{VECTOR_STORE_ID}``, + ``{RESPONSES_FIRST_RESPONSE_ID}``, ``{RESPONSES_CONVERSATION_ID}``, + ``{RESPONSES_SECOND_RESPONSE_ID}``, and ``{CONVERSATION_ID}``. Parameters: ---------- - context (Context): Behave context (default_model, default_provider, - optional faiss_vector_store_id from ``FAISS_VECTOR_STORE_ID``). + context (Context): Behave context carrying model/provider defaults, + optional vector-store and response IDs, and ``response_data``. text (str): String that may contain placeholders to replace. Returns: ------- - String with placeholders replaced by actual values + String with placeholders replaced by actual values. """ result = text.replace("{MODEL}", context.default_model) result = result.replace("{PROVIDER}", context.default_provider) @@ -552,4 +556,16 @@ def replace_placeholders(context: Context, text: str) -> str: result = result.replace( "{RESPONSES_SECOND_RESPONSE_ID}", context.responses_second_response_id ) + if hasattr(context, "response_data") and context.response_data.get( + "conversation_id" + ): + result = result.replace( + "{CONVERSATION_ID}", context.response_data["conversation_id"] + ) + if hasattr(context, "response_data") and context.response_data.get( + "conversation" + ): + result = result.replace( + "{CONVERSATION_ID}", context.response_data["conversation"] + ) return result From b8c25c46a287860232e2433feef1f1a2e0066521 Mon Sep 17 00:00:00 2001 From: Sofiya Nuryyeva Date: Thu, 6 Aug 2026 07:13:35 -0400 Subject: [PATCH 2/3] fixed python version issue --- tests/e2e/utils/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/e2e/utils/utils.py b/tests/e2e/utils/utils.py index 5d611b9f8..df7db9f7a 100644 --- a/tests/e2e/utils/utils.py +++ b/tests/e2e/utils/utils.py @@ -562,10 +562,8 @@ def replace_placeholders(context: Context, text: str) -> str: result = result.replace( "{CONVERSATION_ID}", context.response_data["conversation_id"] ) - if hasattr(context, "response_data") and context.response_data.get( - "conversation" - ): - result = result.replace( - "{CONVERSATION_ID}", context.response_data["conversation"] + if hasattr(context, "response_data") and context.response_data.get("conversation"): + result = result.replace( + "{CONVERSATION_ID}", context.response_data["conversation"] ) return result From c58a4941afedcb2e70a0d4475b42d7e81d6a7f14 Mon Sep 17 00:00:00 2001 From: Sofiya Nuryyeva Date: Thu, 6 Aug 2026 16:05:26 -0400 Subject: [PATCH 3/3] removed unused role actions from config --- tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml | 2 -- tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml index 0252d190e..6624959f7 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-rbac.yaml @@ -79,8 +79,6 @@ authorization: - "model_override" - "rlsapi_v1_infer" - "responses" - - "manage_prompts" - - "read_prompts" # Viewer role can only read (no mutations) - role: "viewer" actions: diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml index 6fb7166a2..91170b493 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-rbac.yaml @@ -77,8 +77,6 @@ authorization: - "model_override" - "rlsapi_v1_infer" - "responses" - - "manage_prompts" - - "read_prompts" # Viewer role can only read (no mutations) - role: "viewer" actions: