Some checks failed
AITBC CI/CD Pipeline / lint-and-test (3.11) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.12) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.13) (push) Has been cancelled
AITBC CI/CD Pipeline / test-cli (push) Has been cancelled
AITBC CI/CD Pipeline / test-services (push) Has been cancelled
AITBC CI/CD Pipeline / test-production-services (push) Has been cancelled
AITBC CI/CD Pipeline / security-scan (push) Has been cancelled
AITBC CI/CD Pipeline / build (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-staging (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-production (push) Has been cancelled
AITBC CI/CD Pipeline / performance-test (push) Has been cancelled
AITBC CI/CD Pipeline / docs (push) Has been cancelled
AITBC CI/CD Pipeline / release (push) Has been cancelled
AITBC CI/CD Pipeline / notify (push) Has been cancelled
Security Scanning / Bandit Security Scan (apps/coordinator-api/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (cli/aitbc_cli) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-core/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-crypto/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-sdk/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (tests) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (javascript) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (python) (push) Has been cancelled
Security Scanning / Dependency Security Scan (push) Has been cancelled
Security Scanning / Container Security Scan (push) Has been cancelled
Security Scanning / OSSF Scorecard (push) Has been cancelled
Security Scanning / Security Summary Report (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.11) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.12) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.13) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-summary (push) Has been cancelled
- Remove debugging service documentation (DEBUgging_SERVICES.md) - Remove development logs policy and quick reference guides - Remove E2E test creation summary - Remove gift certificate example file - Remove GitHub pull summary documentation
225 lines
7.6 KiB
Python
Executable File
225 lines
7.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Comprehensive test for block import endpoint
|
|
Tests all functionality including validation, conflicts, and transaction import
|
|
"""
|
|
|
|
import json
|
|
import hashlib
|
|
import requests
|
|
from datetime import datetime
|
|
|
|
BASE_URL = "https://aitbc.bubuit.net/rpc"
|
|
CHAIN_ID = "ait-devnet"
|
|
|
|
def compute_block_hash(height, parent_hash, timestamp):
|
|
"""Compute block hash using the same algorithm as PoA proposer"""
|
|
payload = f"{CHAIN_ID}|{height}|{parent_hash}|{timestamp}".encode()
|
|
return "0x" + hashlib.sha256(payload).hexdigest()
|
|
|
|
def test_block_import_complete():
|
|
"""Complete test suite for block import endpoint"""
|
|
|
|
print("=" * 60)
|
|
print("BLOCK IMPORT ENDPOINT TEST SUITE")
|
|
print("=" * 60)
|
|
|
|
results = []
|
|
|
|
# Test 1: Invalid height (0)
|
|
print("\n[TEST 1] Invalid height (0)...")
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": 0,
|
|
"hash": "0x123",
|
|
"parent_hash": "0x00",
|
|
"proposer": "test",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 0
|
|
}
|
|
)
|
|
if response.status_code == 422 and "greater_than" in response.json()["detail"][0]["msg"]:
|
|
print("✅ PASS: Correctly rejected height 0")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 422, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 2: Block conflict
|
|
print("\n[TEST 2] Block conflict...")
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": 1,
|
|
"hash": "0xinvalidhash",
|
|
"parent_hash": "0x00",
|
|
"proposer": "test",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 0
|
|
}
|
|
)
|
|
if response.status_code == 409 and "already exists with different hash" in response.json()["detail"]:
|
|
print("✅ PASS: Correctly detected block conflict")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 409, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 3: Import existing block with correct hash
|
|
print("\n[TEST 3] Import existing block with correct hash...")
|
|
response = requests.get(f"{BASE_URL}/blocks/1")
|
|
block_data = response.json()
|
|
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": block_data["height"],
|
|
"hash": block_data["hash"],
|
|
"parent_hash": block_data["parent_hash"],
|
|
"proposer": block_data["proposer"],
|
|
"timestamp": block_data["timestamp"],
|
|
"tx_count": block_data["tx_count"]
|
|
}
|
|
)
|
|
if response.status_code == 200 and response.json()["status"] == "exists":
|
|
print("✅ PASS: Correctly handled existing block")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 200 with 'exists' status, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 4: Invalid block hash
|
|
print("\n[TEST 4] Invalid block hash...")
|
|
response = requests.get(f"{BASE_URL}/head")
|
|
head = response.json()
|
|
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": 999999,
|
|
"hash": "0xinvalid",
|
|
"parent_hash": head["hash"],
|
|
"proposer": "test",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 0
|
|
}
|
|
)
|
|
if response.status_code == 400 and "Invalid block hash" in response.json()["detail"]:
|
|
print("✅ PASS: Correctly rejected invalid hash")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 400, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 5: Parent not found
|
|
print("\n[TEST 5] Parent block not found...")
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": 999998,
|
|
"hash": compute_block_hash(999998, "0xnonexistent", "2026-01-29T10:20:00"),
|
|
"parent_hash": "0xnonexistent",
|
|
"proposer": "test",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 0
|
|
}
|
|
)
|
|
if response.status_code == 400 and "Parent block not found" in response.json()["detail"]:
|
|
print("✅ PASS: Correctly rejected missing parent")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 400, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 6: Import block without transactions
|
|
print("\n[TEST 6] Import block without transactions...")
|
|
response = requests.get(f"{BASE_URL}/head")
|
|
head = response.json()
|
|
|
|
height = head["height"] + 1
|
|
block_hash = compute_block_hash(height, head["hash"], "2026-01-29T10:20:00")
|
|
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": height,
|
|
"hash": block_hash,
|
|
"parent_hash": head["hash"],
|
|
"proposer": "test-proposer",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 0,
|
|
"transactions": []
|
|
}
|
|
)
|
|
if response.status_code == 200 and response.json()["status"] == "imported":
|
|
print("✅ PASS: Successfully imported block without transactions")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ FAIL: Expected 200, got {response.status_code}")
|
|
results.append(False)
|
|
|
|
# Test 7: Import block with transactions (KNOWN ISSUE)
|
|
print("\n[TEST 7] Import block with transactions...")
|
|
print("⚠️ KNOWN ISSUE: Transaction import currently fails with database constraint error")
|
|
print(" This appears to be a bug in the transaction field mapping")
|
|
|
|
height = height + 1
|
|
block_hash = compute_block_hash(height, head["hash"], "2026-01-29T10:20:00")
|
|
|
|
response = requests.post(
|
|
f"{BASE_URL}/blocks/import",
|
|
json={
|
|
"height": height,
|
|
"hash": block_hash,
|
|
"parent_hash": head["hash"],
|
|
"proposer": "test-proposer",
|
|
"timestamp": "2026-01-29T10:20:00",
|
|
"tx_count": 1,
|
|
"transactions": [{
|
|
"tx_hash": "0xtx123",
|
|
"sender": "0xsender",
|
|
"recipient": "0xrecipient",
|
|
"payload": {"test": "data"}
|
|
}]
|
|
}
|
|
)
|
|
if response.status_code == 500:
|
|
print("⚠️ EXPECTED FAILURE: Transaction import fails with 500 error")
|
|
print(" Error: NOT NULL constraint failed on transaction fields")
|
|
results.append(None) # Known issue, not counting as fail
|
|
else:
|
|
print(f"❓ UNEXPECTED: Got {response.status_code} instead of expected 500")
|
|
results.append(None)
|
|
|
|
# Summary
|
|
print("\n" + "=" * 60)
|
|
print("TEST SUMMARY")
|
|
print("=" * 60)
|
|
|
|
passed = sum(1 for r in results if r is True)
|
|
failed = sum(1 for r in results if r is False)
|
|
known_issues = sum(1 for r in results if r is None)
|
|
|
|
print(f"✅ Passed: {passed}")
|
|
print(f"❌ Failed: {failed}")
|
|
if known_issues > 0:
|
|
print(f"⚠️ Known Issues: {known_issues}")
|
|
|
|
print("\nFUNCTIONALITY STATUS:")
|
|
print("- ✅ Input validation (height, hash, parent)")
|
|
print("- ✅ Conflict detection")
|
|
print("- ✅ Block import without transactions")
|
|
print("- ❌ Block import with transactions (database constraint issue)")
|
|
|
|
if failed == 0:
|
|
print("\n🎉 All core functionality is working!")
|
|
print(" The block import endpoint is functional for basic use.")
|
|
else:
|
|
print(f"\n⚠️ {failed} test(s) failed - review required")
|
|
|
|
return passed, failed, known_issues
|
|
|
|
if __name__ == "__main__":
|
|
test_block_import_complete()
|