mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 00:09:22 +00:00
### **User description** - Add test_alert_bulk_enable.py: bulk enable/disable alerts (5 tests) - Add test_function_test_endpoint.py: VRL function testing (7 tests) - Add test_kvstore.py: KV store CRUD operations (8 tests) - Add test_multi_stream_search.py: multi-stream SSE search (6 tests) - Add test_org_passcode.py: organization passcode API (4 tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) ___ ### **PR Type** Tests ___ ### **Description** - Bulk alert enable/disable tests for existing alerts - VRL/JS function test endpoint happy and error flows - KV store create, read, list, update, delete tests - Multi-stream SSE search tests for various scenarios - Organization passcode get, update, persistence tests ___ <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><table> <tr> <td> <details> <summary><strong>test_alert_bulk_enable.py</strong><dd><code>Bulk alert enable/disable tests</code> </dd></summary> <hr> tests/api-testing/tests/test_alert_bulk_enable.py <ul><li>Fixture setup for session and existing alerts retrieval<br> <li> Tests bulk disable and enable single alerts<br> <li> Tests bulk disable for multiple alerts<br> <li> Idempotency and response structure verification</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10155/files#diff-bb438bbffe5aad82142e50aa7765cb47c711463ffc0fc1ce72b221cbcadafe9a">+241/-0</a> </td> </tr> <tr> <td> <details> <summary><strong>test_function_test_endpoint.py</strong><dd><code>Function test endpoint tests</code> </dd></summary> <hr> tests/api-testing/tests/test_function_test_endpoint.py <ul><li>Fixture setup for VRL/JS function test endpoint<br> <li> Tests basic function and value modification<br> <li> Multi-event and conditional logic tests<br> <li> Invalid syntax and response structure verification</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10155/files#diff-8f8ba09e2a96c246c77ae4512443842d0e8accb855bac0f949971941b9956b6c">+230/-0</a> </td> </tr> <tr> <td> <details> <summary><strong>test_kvstore.py</strong><dd><code>KV store CRUD API tests</code> </dd></summary> <hr> tests/api-testing/tests/test_kvstore.py <ul><li>Added KV store CRUD tests (create, read, list)<br> <li> Tests prefix filtering and update behavior<br> <li> Delete operations, 404 and idempotency tests</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10155/files#diff-b353df992b5db6b02190e9232e077967b8c30d702f24bb1903c8d5c8c32fd14e">+226/-0</a> </td> </tr> <tr> <td> <details> <summary><strong>test_multi_stream_search.py</strong><dd><code>Multi-stream search SSE tests</code> </dd></summary> <hr> tests/api-testing/tests/test_multi_stream_search.py <ul><li>Fixture setup and SSE response parser added<br> <li> Tests single and multi-stream search queries<br> <li> SSE format and metadata structure verification<br> <li> Size limit and invalid stream handling tests</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10155/files#diff-c20d33fef67f42eb651e7a0d5d97d053c1d0d58307c13fb57ea0a19ffeedb329">+223/-0</a> </td> </tr> <tr> <td> <details> <summary><strong>test_org_passcode.py</strong><dd><code>Organization passcode API tests</code> </dd></summary> <hr> tests/api-testing/tests/test_org_passcode.py <ul><li>Tests organization passcode get and update endpoints<br> <li> Verifies persistence and response structure</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10155/files#diff-819841cc5f7f2475248a5edd31f9d44c79159fa6fb97a9c2db9ea70139904379">+126/-0</a> </td> </tr> </table></td></tr></tr></tbody></table> </details> ___ --------- Co-authored-by: Claude <noreply@anthropic.com>
233 lines
7.8 KiB
Python
233 lines
7.8 KiB
Python
"""
|
|
Function Test Endpoint API Tests
|
|
|
|
Tests for Function Test operations:
|
|
- POST /api/{org_id}/functions/test - Test a VRL/JS function without saving it
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
class TestFunctionTestEndpoint:
|
|
"""Test class for Function Test API endpoint."""
|
|
|
|
ORG_ID = "default"
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def setup(self, create_session, base_url):
|
|
"""Setup test fixtures."""
|
|
self.session = create_session
|
|
self.base_url = base_url
|
|
|
|
def test_01_basic_vrl_function(self):
|
|
"""Test a basic VRL function that adds a field"""
|
|
|
|
payload = {
|
|
"function": ".test_field = \"added_by_test\"\n.",
|
|
"events": [
|
|
{
|
|
"_timestamp": 1735128523652186,
|
|
"job": "pytest",
|
|
"level": "info",
|
|
"log": "test message"
|
|
}
|
|
]
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200, \
|
|
f"Expected 200, got {response.status_code}: {response.text}"
|
|
|
|
data = response.json()
|
|
assert "results" in data, f"Response should have 'results' field: {data}"
|
|
assert len(data["results"]) == 1, f"Should have 1 result: {data}"
|
|
|
|
result = data["results"][0]
|
|
assert "event" in result, f"Result should have 'event' field: {result}"
|
|
assert result["event"].get("test_field") == "added_by_test", \
|
|
f"Function should add 'test_field': {result}"
|
|
|
|
print(f"✓ VRL function successfully added field")
|
|
|
|
def test_02_vrl_function_modifies_value(self):
|
|
"""Test a VRL function that modifies an existing value"""
|
|
|
|
# Use upcase! (infallible version) to avoid VRL error handling requirements
|
|
payload = {
|
|
"function": ".level = upcase!(.level)\n.",
|
|
"events": [
|
|
{
|
|
"_timestamp": 1735128523652186,
|
|
"level": "info"
|
|
}
|
|
],
|
|
"trans_type": 0 # Explicitly set to VRL
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200, \
|
|
f"Expected 200, got {response.status_code}: {response.text}"
|
|
|
|
data = response.json()
|
|
assert "results" in data and len(data["results"]) > 0, \
|
|
f"Response should have results: {data}"
|
|
result = data["results"][0]
|
|
assert result["event"].get("level") == "INFO", \
|
|
f"Function should uppercase 'level': {result}"
|
|
|
|
print(f"✓ VRL function successfully modified field")
|
|
|
|
def test_03_vrl_function_with_multiple_events(self):
|
|
"""Test a VRL function with multiple events"""
|
|
|
|
payload = {
|
|
"function": ".processed = true\n.",
|
|
"events": [
|
|
{"id": 1, "name": "event1"},
|
|
{"id": 2, "name": "event2"},
|
|
{"id": 3, "name": "event3"}
|
|
]
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200, \
|
|
f"Expected 200, got {response.status_code}: {response.text}"
|
|
|
|
data = response.json()
|
|
assert len(data["results"]) == 3, \
|
|
f"Should have 3 results for 3 events: {data}"
|
|
|
|
# Verify all events have the added field
|
|
for i, result in enumerate(data["results"]):
|
|
assert result["event"].get("processed") is True, \
|
|
f"Event {i} should have 'processed' = true: {result}"
|
|
|
|
print(f"✓ VRL function successfully processed {len(data['results'])} events")
|
|
|
|
def test_04_vrl_function_with_conditional(self):
|
|
"""Test a VRL function with conditional logic"""
|
|
|
|
payload = {
|
|
"function": "if .level == \"error\" { .priority = \"high\" } else { .priority = \"normal\" }\n.",
|
|
"events": [
|
|
{"level": "error", "message": "something failed"},
|
|
{"level": "info", "message": "all good"}
|
|
],
|
|
"trans_type": 0 # Explicitly set to VRL
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200, \
|
|
f"Expected 200, got {response.status_code}: {response.text}"
|
|
|
|
data = response.json()
|
|
assert data["results"][0]["event"].get("priority") == "high", \
|
|
f"Error event should have high priority: {data['results'][0]}"
|
|
assert data["results"][1]["event"].get("priority") == "normal", \
|
|
f"Info event should have normal priority: {data['results'][1]}"
|
|
|
|
print(f"✓ VRL conditional logic works correctly")
|
|
|
|
def test_05_invalid_vrl_function(self):
|
|
"""Test that invalid VRL syntax returns an error"""
|
|
|
|
payload = {
|
|
"function": ".test = invalid syntax here!!!",
|
|
"events": [{"field": "value"}]
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
# Invalid syntax should return 400 or have error in results
|
|
# The API behavior may vary, so we check both possibilities
|
|
if response.status_code == 400:
|
|
print(f"✓ Invalid VRL correctly returned 400 error")
|
|
else:
|
|
data = response.json()
|
|
# Check if error message is present in results
|
|
has_error = any(
|
|
result.get("message", "") != ""
|
|
for result in data.get("results", [])
|
|
)
|
|
assert has_error or response.status_code == 400, \
|
|
f"Invalid VRL should produce an error: {response.status_code} - {data}"
|
|
print(f"✓ Invalid VRL correctly returned error in results")
|
|
|
|
def test_06_response_structure(self):
|
|
"""Test that response has correct structure"""
|
|
|
|
payload = {
|
|
"function": ".", # Identity function - returns input unchanged
|
|
"events": [{"test": "value"}]
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
|
|
# Verify response structure
|
|
assert "results" in data, "Response should have 'results' field"
|
|
assert isinstance(data["results"], list), "'results' should be a list"
|
|
|
|
for result in data["results"]:
|
|
assert "message" in result, "Each result should have 'message' field"
|
|
assert "event" in result, "Each result should have 'event' field"
|
|
|
|
print(f"✓ Response structure is correct: {data}")
|
|
|
|
def test_07_vrl_delete_field(self):
|
|
"""Test a VRL function that deletes a field"""
|
|
|
|
payload = {
|
|
"function": "del(.sensitive_data)\n.",
|
|
"events": [
|
|
{
|
|
"id": 1,
|
|
"name": "test",
|
|
"sensitive_data": "secret123"
|
|
}
|
|
],
|
|
"trans_type": 0 # Explicitly set to VRL
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}api/{self.ORG_ID}/functions/test",
|
|
json=payload
|
|
)
|
|
|
|
assert response.status_code == 200, \
|
|
f"Expected 200, got {response.status_code}: {response.text}"
|
|
|
|
data = response.json()
|
|
result = data["results"][0]
|
|
assert "sensitive_data" not in result["event"], \
|
|
f"Field 'sensitive_data' should be deleted: {result}"
|
|
assert result["event"].get("name") == "test", \
|
|
f"Other fields should remain: {result}"
|
|
|
|
print(f"✓ VRL function successfully deleted field")
|