Skip to content

Commit 5ddf9e4

Browse files
committed
Add core-to-SDK integration testing pipeline and matrix verification
This commit introduces: - A unified verification script (scripts/verify-all.sh) to test all SDKs against a live core server. - Comprehensive integration tests for Python (pytest) and TypeScript (jest/ts-jest). - Automated server lifecycle management during testing using pmxt-ensure-server. - Full assertions for data structure, type safety, and cross-exchange consistency. - Testing documentation in TESTING.md.
1 parent 8a94a6d commit 5ddf9e4

File tree

9 files changed

+464
-5
lines changed

9 files changed

+464
-5
lines changed

TESTING.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# PMXT Testing Strategy
2+
3+
To ensure 100% certainty that all individual SDKs function correctly within the monorepo structure, we implement a **Unified Integration Test Pipeline**.
4+
5+
## The Challenge
6+
7+
In a monorepo with a shared backend (`core`) and multiple client SDKs (`sdks/python`, `sdks/typescript`), unit tests are insufficient. They only verify isolated logic. We need to verify the *communication* between the SDKs and the running Sidecar Server.
8+
9+
## The Solution: Matrix Integration Testing
10+
11+
We define a "Golden Path" of operations that every SDK must support (e.g., `Fetch Markets`, `Get Orderbook`). We then run these operations against a live local server.
12+
13+
### Workflow
14+
15+
1. **Build Core**: Ensure the latest `core` server code is built.
16+
2. **Start Sidecar**: Launch the `pmxt` server in the background.
17+
3. **Run SDK Validation**:
18+
* **TypeScript**: execute `sdks/typescript/examples/market-data/get_event_prices.ts`
19+
* **Python**: execute `sdks/python/examples/market-data/get_event_prices.py`
20+
4. **Verify**: If all scripts exit with code `0`, the SDKs are correctly communicating with the server.
21+
22+
## How to Verify Locally
23+
24+
We have created a script to automate this:
25+
26+
```bash
27+
./scripts/verify-all.sh
28+
```
29+
30+
## Continuous Integration (CI)
31+
32+
This logic should be part of the CI pipeline (e.g., GitHub Actions). on every PR:
33+
1. Checkout code.
34+
2. Install dependencies.
35+
3. Run `./scripts/verify-all.sh`.

package-lock.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/verify-all.sh

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# PMXT Verification Script
5+
# Ensures 100% certainty that SDKs work correctly with the Core Server.
6+
# This runs REAL integration tests with assertions, not just examples.
7+
8+
echo "Starting PMXT Verification..."
9+
10+
# 1. Build Core
11+
echo "Building Core..."
12+
cd core
13+
npm install
14+
npm run build
15+
cd ..
16+
17+
# 2. Ensure Server is Running
18+
echo "Starting/Checking Server..."
19+
node core/bin/pmxt-ensure-server
20+
21+
# Give the server a moment to stabilize
22+
sleep 1
23+
24+
# 3. Run Integration Tests
25+
echo "Server is up. Running SDK Integration Tests..."
26+
27+
# Verify Python SDK
28+
echo ""
29+
echo "Running Python SDK Integration Tests..."
30+
if [ -d "sdks/python" ]; then
31+
cd sdks/python
32+
33+
# Install dependencies if needed
34+
if command -v python3 &> /dev/null; then
35+
# Install package in editable mode
36+
python3 -m pip install -e . --quiet 2>/dev/null || echo "Package already installed or install failed"
37+
38+
# Install test dependencies
39+
python3 -m pip install pytest pytest-asyncio --quiet 2>/dev/null || true
40+
41+
# Run integration tests
42+
if [ -f "tests/test_integration.py" ]; then
43+
echo "Running pytest..."
44+
python3 -m pytest tests/test_integration.py -v
45+
PYTHON_EXIT_CODE=$?
46+
47+
if [ $PYTHON_EXIT_CODE -eq 0 ]; then
48+
echo "Python SDK Integration Tests PASSED"
49+
else
50+
echo "Python SDK Integration Tests FAILED"
51+
cd ../..
52+
exit 1
53+
fi
54+
else
55+
echo "Python integration tests not found"
56+
fi
57+
else
58+
echo "python3 not found, skipping Python verification"
59+
fi
60+
61+
cd ../..
62+
else
63+
echo "Python SDK not found"
64+
fi
65+
66+
# Verify TypeScript SDK
67+
echo ""
68+
echo "Running TypeScript SDK Integration Tests..."
69+
if [ -d "sdks/typescript" ]; then
70+
cd sdks/typescript
71+
72+
# Install dependencies
73+
npm install --silent
74+
75+
# Install jest and ts-jest if not present
76+
npm install --save-dev jest ts-jest @types/jest --silent 2>/dev/null || true
77+
78+
# Create jest config if it doesn't exist
79+
if [ ! -f "jest.config.cjs" ] && [ ! -f "jest.config.js" ]; then
80+
cat > jest.config.cjs << 'EOF'
81+
module.exports = {
82+
preset: 'ts-jest',
83+
testEnvironment: 'node',
84+
testMatch: ['**/tests/**/*.test.ts'],
85+
moduleNameMapper: {
86+
'^(\\.{1,2}/.*)\\.js$': '$1',
87+
},
88+
};
89+
EOF
90+
fi
91+
92+
# Run integration tests
93+
if [ -f "tests/integration.test.ts" ]; then
94+
echo "Running jest..."
95+
# Force jest to use the cjs config if we just created it
96+
if [ -f "jest.config.cjs" ]; then
97+
npx jest --config jest.config.cjs
98+
else
99+
npm test
100+
fi
101+
TS_EXIT_CODE=$?
102+
103+
if [ $TS_EXIT_CODE -eq 0 ]; then
104+
echo "TypeScript SDK Integration Tests PASSED"
105+
else
106+
echo "TypeScript SDK Integration Tests FAILED"
107+
cd ../..
108+
exit 1
109+
fi
110+
else
111+
echo "TypeScript integration tests not found"
112+
fi
113+
114+
cd ../..
115+
else
116+
echo "TypeScript SDK not found"
117+
fi
118+
119+
echo ""
120+
echo "All SDK Integration Tests Passed!"
121+
echo "Both SDKs are verified to work correctly with the Core Server"
122+

sdks/python/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Python SDK Integration Tests
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
Python SDK Integration Tests
3+
4+
These tests verify that the Python SDK correctly communicates with the PMXT server
5+
and returns properly structured, validated data.
6+
7+
Prerequisites:
8+
- PMXT server must be running (use pmxt-ensure-server)
9+
- No API keys required for read-only operations
10+
"""
11+
12+
import pytest
13+
import pmxt
14+
from datetime import datetime
15+
16+
17+
class TestPolymarketIntegration:
18+
"""Test Polymarket SDK integration with live server"""
19+
20+
@pytest.fixture
21+
def client(self):
22+
"""Create a Polymarket client instance"""
23+
return pmxt.Polymarket()
24+
25+
def test_fetch_markets_returns_valid_structure(self, client):
26+
"""Verify fetchMarkets returns properly structured data"""
27+
markets = client.fetch_markets()
28+
29+
# Should return a list
30+
assert isinstance(markets, list), "fetchMarkets should return a list"
31+
assert len(markets) > 0, "Should return at least one market"
32+
33+
# Check first market structure
34+
market = markets[0]
35+
assert hasattr(market, 'id'), "Market should have id"
36+
assert hasattr(market, 'title'), "Market should have title"
37+
assert hasattr(market, 'outcomes'), "Market should have outcomes"
38+
assert hasattr(market, 'volume_24h'), "Market should have volume_24h"
39+
40+
# Validate types
41+
assert isinstance(market.id, str), "Market id should be string"
42+
assert isinstance(market.title, str), "Title should be string"
43+
assert isinstance(market.outcomes, list), "Outcomes should be list"
44+
assert len(market.outcomes) > 0, "Should have at least one outcome"
45+
46+
def test_market_outcomes_have_required_fields(self, client):
47+
"""Verify market outcomes contain all required fields"""
48+
markets = client.fetch_markets()
49+
market = markets[0]
50+
outcome = market.outcomes[0]
51+
52+
assert hasattr(outcome, 'label'), "Outcome should have label"
53+
assert hasattr(outcome, 'price'), "Outcome should have price"
54+
assert isinstance(outcome.label, str), "Outcome label should be string"
55+
assert isinstance(outcome.price, (int, float)), "Price should be numeric"
56+
assert 0 <= outcome.price <= 1, "Price should be between 0 and 1"
57+
58+
def test_get_markets_by_slug(self, client):
59+
"""Test fetching markets by slug"""
60+
# Use a known active market slug
61+
markets = client.get_markets_by_slug('presidential-election-winner-2024')
62+
63+
assert isinstance(markets, list), "Should return a list"
64+
# Note: Market might be resolved, so we don't assert length > 0
65+
66+
if len(markets) > 0:
67+
market = markets[0]
68+
assert hasattr(market, 'id')
69+
assert hasattr(market, 'outcomes')
70+
71+
def test_volume_fields_are_numeric(self, client):
72+
"""Verify volume fields are properly parsed as numbers"""
73+
markets = client.fetch_markets()
74+
market = markets[0]
75+
76+
assert isinstance(market.volume_24h, (int, float)), "volume_24h should be numeric"
77+
assert market.volume_24h >= 0, "volume_24h should be non-negative"
78+
79+
def test_resolution_date_is_datetime(self, client):
80+
"""Verify resolution date is properly parsed"""
81+
markets = client.fetch_markets()
82+
market = markets[0]
83+
84+
if hasattr(market, 'resolution_date') and market.resolution_date:
85+
assert isinstance(market.resolution_date, (datetime, str)), \
86+
"Resolution date should be datetime or ISO string"
87+
88+
89+
class TestKalshiIntegration:
90+
"""Test Kalshi SDK integration with live server"""
91+
92+
@pytest.fixture
93+
def client(self):
94+
"""Create a Kalshi client instance"""
95+
return pmxt.Kalshi()
96+
97+
def test_fetch_markets_returns_valid_structure(self, client):
98+
"""Verify fetchMarkets returns properly structured data"""
99+
markets = client.fetch_markets()
100+
101+
assert isinstance(markets, list), "fetchMarkets should return a list"
102+
assert len(markets) > 0, "Should return at least one market"
103+
104+
market = markets[0]
105+
assert hasattr(market, 'id'), "Market should have id"
106+
assert hasattr(market, 'title'), "Market should have title"
107+
assert hasattr(market, 'outcomes'), "Market should have outcomes"
108+
109+
def test_market_outcomes_structure(self, client):
110+
"""Verify Kalshi market outcomes are properly structured"""
111+
markets = client.fetch_markets()
112+
market = markets[0]
113+
114+
assert isinstance(market.outcomes, list), "Outcomes should be list"
115+
assert len(market.outcomes) > 0, "Should have at least one outcome"
116+
117+
outcome = market.outcomes[0]
118+
assert hasattr(outcome, 'label'), "Outcome should have label"
119+
assert hasattr(outcome, 'price'), "Outcome should have price"
120+
121+
122+
class TestCrossExchangeConsistency:
123+
"""Test that both exchanges return data in the same normalized format"""
124+
125+
def test_both_exchanges_return_same_structure(self):
126+
"""Verify Polymarket and Kalshi return identically structured data"""
127+
poly = pmxt.Polymarket()
128+
kalshi = pmxt.Kalshi()
129+
130+
poly_markets = poly.fetch_markets()
131+
kalshi_markets = kalshi.fetch_markets()
132+
133+
# Both should return lists
134+
assert isinstance(poly_markets, list)
135+
assert isinstance(kalshi_markets, list)
136+
137+
# Both should have markets with same field structure
138+
if len(poly_markets) > 0 and len(kalshi_markets) > 0:
139+
poly_market = poly_markets[0]
140+
kalshi_market = kalshi_markets[0]
141+
142+
# Check both have same core fields
143+
core_fields = ['id', 'title', 'outcomes']
144+
for field in core_fields:
145+
assert hasattr(poly_market, field), f"Polymarket missing {field}"
146+
assert hasattr(kalshi_market, field), f"Kalshi missing {field}"
147+
148+
149+
if __name__ == "__main__":
150+
pytest.main([__file__, "-v"])

sdks/typescript/jest.config.cjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
testMatch: ['**/tests/**/*.test.ts'],
5+
moduleNameMapper: {
6+
'^(\\.{1,2}/.*)\\.js$': '$1',
7+
},
8+
};

sdks/typescript/package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"clean": "rm -rf dist",
3131
"prebuild": "npm run clean",
3232
"build": "tsc && tsc -p tsconfig.esm.json",
33-
"prepare": "npm run build"
33+
"prepare": "npm run build",
34+
"test": "jest"
3435
},
3536
"keywords": [
3637
"prediction-markets",
@@ -41,8 +42,10 @@
4142
"unified"
4243
],
4344
"devDependencies": {
45+
"@types/jest": "^30.0.0",
4446
"@types/node": "^20.0.0",
47+
"jest": "^30.2.0",
48+
"ts-jest": "^29.4.6",
4549
"typescript": "^5.0.0"
46-
},
47-
"dependencies": {}
48-
}
50+
}
51+
}

0 commit comments

Comments
 (0)