mcghidra/test_data_update.py
Teal Bauer 28870e9b54 feat: Improve data manipulation API
- 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
2025-04-14 19:24:14 +02:00

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)