|
11 | 11 | from sap_cloud_sdk.core.auditlog_ng.client import ( |
12 | 12 | AuditClient, |
13 | 13 | _fill_common_from_auth_context, |
| 14 | + _strip_message_context_text, |
14 | 15 | ) |
15 | 16 | from sap_cloud_sdk.core.auditlog_ng.config import AuditLogNGConfig, SCHEMA_URL |
16 | 17 | from sap_cloud_sdk.core.auditlog_ng.exceptions import ValidationError |
@@ -401,3 +402,56 @@ def test_does_not_overwrite_explicit_timestamp(self): |
401 | 402 | def test_event_without_common_field_is_skipped(self): |
402 | 403 | event = MagicMock(spec=[]) # no 'common' attribute |
403 | 404 | _fill_common_from_auth_context(event) # must not raise |
| 405 | + |
| 406 | + |
| 407 | +class TestStripMessageContextText: |
| 408 | + def test_strips_prompt_and_response_text(self): |
| 409 | + event = pb.AiPromptReceived() |
| 410 | + event.message_context.prompt_text = "verbatim user prompt" |
| 411 | + event.message_context.response_text = "verbatim model response" |
| 412 | + _strip_message_context_text(event) |
| 413 | + assert event.message_context.prompt_text == "" |
| 414 | + assert event.message_context.response_text == "" |
| 415 | + |
| 416 | + def test_preserves_correlation_and_token_fields(self): |
| 417 | + event = pb.AiPromptReceived() |
| 418 | + event.message_context.prompt_text = "secret" |
| 419 | + event.message_context.response_text = "secret" |
| 420 | + event.message_context.uid = "conversation-123" |
| 421 | + event.message_context.name = "thread-name" |
| 422 | + event.message_context.prompt_tokens = 42 |
| 423 | + event.message_context.completion_tokens = 7 |
| 424 | + event.message_context.total_tokens = 49 |
| 425 | + _strip_message_context_text(event) |
| 426 | + assert event.message_context.uid == "conversation-123" |
| 427 | + assert event.message_context.name == "thread-name" |
| 428 | + assert event.message_context.prompt_tokens == 42 |
| 429 | + assert event.message_context.completion_tokens == 7 |
| 430 | + assert event.message_context.total_tokens == 49 |
| 431 | + |
| 432 | + def test_unset_message_context_is_not_created(self): |
| 433 | + event = pb.AiPromptReceived() |
| 434 | + _strip_message_context_text(event) |
| 435 | + assert not event.HasField("message_context") |
| 436 | + |
| 437 | + def test_event_without_message_context_field_is_skipped(self): |
| 438 | + # AiSkillSelected has no message_context field at all. |
| 439 | + event = pb.AiSkillSelected() |
| 440 | + _strip_message_context_text(event) # must not raise |
| 441 | + |
| 442 | + def test_send_strips_message_context_text_before_emit(self): |
| 443 | + client, mock_logger, _, _, patcher, _ = _make_mocked_client() |
| 444 | + try: |
| 445 | + event = pb.AiPromptReceived() |
| 446 | + event.common.tenant_id = "tenant-123" |
| 447 | + event.message_context.prompt_text = "sensitive prompt" |
| 448 | + event.message_context.response_text = "sensitive response" |
| 449 | + |
| 450 | + client.send(event) |
| 451 | + |
| 452 | + # The event object itself is scrubbed in place before serialization. |
| 453 | + assert event.message_context.prompt_text == "" |
| 454 | + assert event.message_context.response_text == "" |
| 455 | + mock_logger.emit.assert_called_once() |
| 456 | + finally: |
| 457 | + patcher.stop() |
0 commit comments