feat(v3-data-store): harden search security and standardize test suite
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
|
||||
# --- Configuration ---
|
||||
@@ -9,61 +8,61 @@ AGENT_API_KEY = "PMM4n50teUCaOMMTN8qOJA"
|
||||
|
||||
# Default Contexts for Testing
|
||||
CONTEXTS = {
|
||||
"account_1": "_XY7DXtc9MY",
|
||||
"account_1": "_XY7DXtc9MY", # Standard Test Account
|
||||
"account_5": "xFP7AhU8Zlc",
|
||||
"event_1358": "nmBfuGFeR0k"
|
||||
"event_1": "nmBfuGFeR0k"
|
||||
}
|
||||
|
||||
def run_lookup(code, description, account_id=None, for_type=None, for_id=None, limit=1, delay_ms=0):
|
||||
"""
|
||||
Performs a Data Store lookup and prints standardized results.
|
||||
"""
|
||||
print(f"[V3] {description} (Limit: {limit}, Delay: {delay_ms}ms)")
|
||||
|
||||
def get_headers(account_id=None):
|
||||
headers = {
|
||||
"X-Aether-API-Key": AGENT_API_KEY,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
if account_id:
|
||||
headers["x-account-id"] = account_id
|
||||
else:
|
||||
headers["x-no-account-id"] = "bypass"
|
||||
return headers
|
||||
|
||||
def print_result(label, success, message=""):
|
||||
"""Standardized output helper."""
|
||||
status = "✅ PASS" if success else "❌ FAIL"
|
||||
print(f"[{status}] {label} {message}")
|
||||
|
||||
def test_standard_lookup():
|
||||
"""Verifies the cascading lookup (Object > Account > Global)."""
|
||||
print("\n--- Testing Standard Cascading Lookup ---")
|
||||
code = "event_launcher_main_info"
|
||||
url = f"{BASE_URL}/v3/data_store/code/{code}"
|
||||
params = {"for_type": for_type, "for_id": for_id, "limit": limit, "delay_ms": delay_ms}
|
||||
|
||||
# Test Account Lookup
|
||||
resp = requests.get(url, headers=get_headers(CONTEXTS["account_1"]))
|
||||
print_result("Lookup: Account Context", resp.status_code == 200)
|
||||
|
||||
# Test Object Override Lookup
|
||||
params = {"for_type": "event", "for_id": CONTEXTS["event_1"]}
|
||||
resp = requests.get(url, headers=get_headers(CONTEXTS["account_1"]), params=params)
|
||||
print_result("Lookup: Object Context Override", resp.status_code == 200)
|
||||
|
||||
def test_delay_simulation():
|
||||
"""Verifies X-Delay-ms header and delay_ms query param."""
|
||||
print("\n--- Testing Latency Simulation ---")
|
||||
code = "event_launcher_main_info"
|
||||
url = f"{BASE_URL}/v3/data_store/code/{code}"
|
||||
|
||||
delay_ms = 500
|
||||
start_time = time.time()
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
resp = requests.get(url, headers=get_headers(CONTEXTS["account_1"]), params={"delay_ms": delay_ms})
|
||||
duration = (time.time() - start_time) * 1000
|
||||
|
||||
print(f" Status: {response.status_code} ({duration:.0f}ms)")
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json().get('data')
|
||||
if data:
|
||||
if isinstance(data, list):
|
||||
print(f" Result: SUCCESS (List of {len(data)})")
|
||||
else:
|
||||
print(f" Result: SUCCESS (Single Object)")
|
||||
else:
|
||||
print(f" Result: NULL (No record found)")
|
||||
else:
|
||||
print(f" Result: ERROR - {response.text[:200]}")
|
||||
print("-" * 60)
|
||||
# Allow for some network overhead but check if it's at least the delay
|
||||
print_result(f"Delay: query param ({delay_ms}ms)", resp.status_code == 200 and duration >= delay_ms)
|
||||
|
||||
def run_search(code, description):
|
||||
"""
|
||||
Tests the new POST /search endpoint for Data Store codes.
|
||||
"""
|
||||
print(f"[V3-SEARCH] {description}")
|
||||
|
||||
def test_advanced_search():
|
||||
"""Verifies POST /search with hierarchical logic and ID Vision."""
|
||||
print("\n--- Testing Advanced POST Search ---")
|
||||
code = "event_launcher_main_info"
|
||||
url = f"{BASE_URL}/v3/data_store/code/{code}/search"
|
||||
headers = {
|
||||
"X-Aether-API-Key": AGENT_API_KEY,
|
||||
"x-no-account-id": "bypass",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
search_query = {
|
||||
"and_filters": [
|
||||
@@ -71,27 +70,31 @@ def run_search(code, description):
|
||||
]
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=search_query)
|
||||
print(f" Status: {response.status_code}")
|
||||
resp = requests.post(url, headers=get_headers(), json=search_query)
|
||||
success = resp.status_code == 200
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json().get('data', [])
|
||||
print(f" Result: SUCCESS (Found {len(data)} results via POST Search)")
|
||||
else:
|
||||
print(f" Result: ERROR - {response.text[:200]}")
|
||||
print("-" * 60)
|
||||
vision_ok = True
|
||||
if success:
|
||||
data = resp.json().get('data', [])
|
||||
if data:
|
||||
# Check ID Vision (strings, not ints)
|
||||
item = data[0]
|
||||
if not isinstance(item.get('id'), str) or not isinstance(item.get('account_id'), (str, type(None))):
|
||||
vision_ok = False
|
||||
print(f" ❌ ID Vision Failure: id={item.get('id')}, account_id={item.get('account_id')}")
|
||||
|
||||
print_result("Search: POST /search with ID Vision", success and vision_ok)
|
||||
|
||||
if __name__ == "__main__":
|
||||
CODE = "event_launcher_main_info"
|
||||
print(f"=== Aether Data Store V3 Parity Tester ===\n")
|
||||
|
||||
# 1. Standard Lookup
|
||||
run_lookup(CODE, "Scenario: Standard Lookup", account_id=CONTEXTS["account_1"])
|
||||
|
||||
# 2. Delay Simulation
|
||||
run_lookup(CODE, "Scenario: Delay Simulation (500ms)", account_id=CONTEXTS["account_1"], delay_ms=500)
|
||||
|
||||
# 3. Advanced Search (POST)
|
||||
run_search(CODE, "Scenario: Advanced Search via POST")
|
||||
|
||||
print("\nTests Complete.")
|
||||
print(f"=== Aether Data Store V3 E2E Suite ===")
|
||||
print(f"Target: {BASE_URL}")
|
||||
|
||||
start_time = time.time()
|
||||
try:
|
||||
test_standard_lookup()
|
||||
test_delay_simulation()
|
||||
test_advanced_search()
|
||||
except Exception as e:
|
||||
print(f"💥 Suite Error: {e}")
|
||||
|
||||
print(f"\nSuite completed in {time.time() - start_time:.2f}s")
|
||||
|
||||
Reference in New Issue
Block a user