- Add comprehensive data manipulation capabilities - Implement separate pathways for rename-only, type-only, and combined operations - Fix HTTP request body consumption issue in DataEndpoints - Standardize on 'type' parameter name instead of 'dataType' - Add thorough test coverage with dedicated test_data_update.py script - Update API documentation to version 2 with full endpoint descriptions - Update CHANGELOG with detailed information about data manipulation features
181 lines
6.3 KiB
Python
Executable File
181 lines
6.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Dedicated test script for the GhydraMCP data handling API.
|
|
|
|
This script has standalone tests to validate the three key data manipulation operations:
|
|
1. Rename only - Change the name without changing the data type
|
|
2. Type change only - Change the data type while preserving the name
|
|
3. Update both - Change both name and type simultaneously
|
|
|
|
These tests operate on a low level and can be run independently of the main test suite
|
|
to diagnose issues with the API's data handling capabilities.
|
|
|
|
Usage:
|
|
python test_data_update.py
|
|
"""
|
|
import json
|
|
import requests
|
|
import sys
|
|
import argparse
|
|
|
|
BASE_URL = "http://localhost:8192"
|
|
|
|
def test_data_update(verbose=True, base_url=None):
|
|
"""Test data update operations
|
|
|
|
Args:
|
|
verbose: Whether to print detailed output
|
|
base_url: Base URL for the Ghidra HTTP API (default: http://localhost:8192)
|
|
|
|
Returns:
|
|
bool: True if all tests pass, False otherwise
|
|
"""
|
|
if base_url:
|
|
global BASE_URL
|
|
BASE_URL = base_url
|
|
|
|
# Track test results
|
|
all_tests_passed = True
|
|
|
|
# First find a suitable data item to test with
|
|
if verbose:
|
|
print("Fetching data items...")
|
|
response = requests.get(f"{BASE_URL}/data?limit=1")
|
|
|
|
if response.status_code != 200:
|
|
print(f"Error: Failed to fetch data items, status {response.status_code}")
|
|
print(response.text)
|
|
return False
|
|
|
|
data = response.json()
|
|
if not data.get("success"):
|
|
print(f"Error: API call failed: {data.get('error', 'Unknown error')}")
|
|
return False
|
|
|
|
# Extract address from first data item
|
|
result = data.get("result", [])
|
|
if not result or not isinstance(result, list) or not result[0].get("address"):
|
|
print("Error: No data items found or invalid response format")
|
|
if result and verbose:
|
|
print(f"Result: {json.dumps(result, indent=2)}")
|
|
return False
|
|
|
|
address = result[0]["address"]
|
|
if verbose:
|
|
print(f"Using data item at address: {address}")
|
|
|
|
# Test 1: Renaming only
|
|
if verbose:
|
|
print("\n--- Test 1: Rename Only ---")
|
|
test_name = "TEST_DATA_RENAME"
|
|
payload = {
|
|
"address": address,
|
|
"newName": test_name
|
|
}
|
|
|
|
if verbose:
|
|
print(f"Request: POST {BASE_URL}/data")
|
|
print(f"Payload: {json.dumps(payload, indent=2)}")
|
|
|
|
response = requests.post(f"{BASE_URL}/data", json=payload)
|
|
if verbose:
|
|
print(f"Status: {response.status_code}")
|
|
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
|
|
|
# Check Test 1 results
|
|
test1_passed = response.status_code == 200 and response.json().get("success")
|
|
if not test1_passed:
|
|
print(f"ERROR: Test 1 (Rename Only) failed: {response.status_code}")
|
|
all_tests_passed = False
|
|
|
|
# Test 2: Type change only
|
|
if verbose:
|
|
print("\n--- Test 2: Type Change Only ---")
|
|
payload = {
|
|
"address": address,
|
|
"type": "int" # Using 'type' as parameter name
|
|
}
|
|
|
|
if verbose:
|
|
print(f"Request: POST {BASE_URL}/data/type")
|
|
print(f"Payload: {json.dumps(payload, indent=2)}")
|
|
|
|
response = requests.post(f"{BASE_URL}/data/type", json=payload)
|
|
if verbose:
|
|
print(f"Status: {response.status_code}")
|
|
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
|
|
|
# Check Test 2 results
|
|
test2_passed = response.status_code == 200 and response.json().get("success")
|
|
if not test2_passed:
|
|
print(f"ERROR: Test 2 (Type Change Only) failed: {response.status_code}")
|
|
all_tests_passed = False
|
|
|
|
# Test 3: Both name and type change
|
|
if verbose:
|
|
print("\n--- Test 3: Both Name and Type Change ---")
|
|
payload = {
|
|
"address": address,
|
|
"newName": "TEST_DATA_BOTH",
|
|
"type": "byte" # Using 'type' as parameter name
|
|
}
|
|
|
|
if verbose:
|
|
print(f"Request: POST {BASE_URL}/data/update")
|
|
print(f"Payload: {json.dumps(payload, indent=2)}")
|
|
|
|
response = requests.post(f"{BASE_URL}/data/update", json=payload)
|
|
if verbose:
|
|
print(f"Status: {response.status_code}")
|
|
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
|
|
|
# Check Test 3 results
|
|
test3_passed = response.status_code == 200 and response.json().get("success")
|
|
if not test3_passed:
|
|
print(f"ERROR: Test 3 (Both Name and Type Change via /data/update) failed: {response.status_code}")
|
|
all_tests_passed = False
|
|
|
|
# Test 4: Direct raw request using the /data endpoint
|
|
if verbose:
|
|
print("\n--- Test 4: Direct Request to /data endpoint ---")
|
|
payload = {
|
|
"address": address,
|
|
"newName": "TEST_DIRECT_UPDATE",
|
|
"type": "int" # Using 'type' parameter name
|
|
}
|
|
|
|
if verbose:
|
|
print(f"Request: POST {BASE_URL}/data")
|
|
print(f"Payload: {json.dumps(payload, indent=2)}")
|
|
|
|
response = requests.post(f"{BASE_URL}/data", json=payload)
|
|
if verbose:
|
|
print(f"Status: {response.status_code}")
|
|
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
|
|
|
# Check Test 4 results
|
|
test4_passed = response.status_code == 200 and response.json().get("success")
|
|
if not test4_passed:
|
|
print(f"ERROR: Test 4 (Both Name and Type Change via /data) failed: {response.status_code}")
|
|
all_tests_passed = False
|
|
|
|
# Print summary
|
|
if verbose:
|
|
print("\n--- Test Summary ---")
|
|
print(f"Test 1 (Rename Only): {'PASSED' if test1_passed else 'FAILED'}")
|
|
print(f"Test 2 (Type Change Only): {'PASSED' if test2_passed else 'FAILED'}")
|
|
print(f"Test 3 (Both Name and Type Change via /data/update): {'PASSED' if test3_passed else 'FAILED'}")
|
|
print(f"Test 4 (Both Name and Type Change via /data): {'PASSED' if test4_passed else 'FAILED'}")
|
|
print(f"Overall: {'ALL TESTS PASSED' if all_tests_passed else 'SOME TESTS FAILED'}")
|
|
|
|
return all_tests_passed
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Test data operations in the GhydraMCP HTTP API")
|
|
parser.add_argument("--quiet", "-q", action="store_true", help="Suppress detailed output")
|
|
parser.add_argument("--url", "-u", help="Base URL for the Ghidra HTTP API")
|
|
args = parser.parse_args()
|
|
|
|
success = test_data_update(not args.quiet, args.url)
|
|
if not success:
|
|
sys.exit(1) |