Skip to content

Commit e32ba97

Browse files
committed
Add chain test suite
1 parent cc128f1 commit e32ba97

File tree

4 files changed

+206
-6
lines changed

4 files changed

+206
-6
lines changed

cmd/mock-handler/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ func handleRequest(line string, testIndex map[string]string) error {
148148
return writeResponse(runner.Response{
149149
ID: req.ID,
150150
Success: testCase.Expected.Success,
151+
Result: testCase.Expected.Result,
151152
Error: testCase.Expected.Error,
152153
})
153154
}

runner/runner.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,31 @@ func (tr *TestRunner) RunTestSuite(suite TestSuite) TestResult {
100100
TotalTests: len(suite.Tests),
101101
}
102102

103+
// Track if any test has failed in a stateful suite
104+
suiteHasFailed := false
105+
103106
for _, test := range suite.Tests {
104-
testResult := tr.runTest(test)
107+
var testResult SingleTestResult
108+
109+
// In stateful suites, if any previous test failed, fail all subsequent tests
110+
if suite.Stateful && suiteHasFailed {
111+
testResult = SingleTestResult{
112+
TestID: test.ID,
113+
Passed: false,
114+
Message: "Skipped due to previous test failure in stateful suite",
115+
}
116+
} else {
117+
testResult = tr.runTest(test)
118+
}
119+
105120
result.TestResults = append(result.TestResults, testResult)
106121
if testResult.Passed {
107122
result.PassedTests++
108123
} else {
109124
result.FailedTests++
125+
if suite.Stateful {
126+
suiteHasFailed = true
127+
}
110128
}
111129
}
112130

@@ -205,6 +223,45 @@ func validateResponse(test TestCase, resp *Response) SingleTestResult {
205223
Message: fmt.Sprintf("Expected success with no error, but got error: %s.%s", resp.Error.Code.Type, resp.Error.Code.Member),
206224
}
207225
}
226+
227+
// If we expect specific result data, validate it
228+
if test.Expected.Result != nil {
229+
if resp.Result == nil {
230+
return SingleTestResult{
231+
TestID: test.ID,
232+
Passed: false,
233+
Message: "Expected result data, but got none",
234+
}
235+
}
236+
237+
// Compare JSON result data (normalize whitespace by comparing unmarshaled values)
238+
var expectedResult, actualResult interface{}
239+
if err := json.Unmarshal(test.Expected.Result, &expectedResult); err != nil {
240+
return SingleTestResult{
241+
TestID: test.ID,
242+
Passed: false,
243+
Message: fmt.Sprintf("Failed to parse expected result: %v", err),
244+
}
245+
}
246+
if err := json.Unmarshal(resp.Result, &actualResult); err != nil {
247+
return SingleTestResult{
248+
TestID: test.ID,
249+
Passed: false,
250+
Message: fmt.Sprintf("Failed to parse actual result: %v", err),
251+
}
252+
}
253+
254+
expectedJSON, _ := json.Marshal(expectedResult)
255+
actualJSON, _ := json.Marshal(actualResult)
256+
if string(expectedJSON) != string(actualJSON) {
257+
return SingleTestResult{
258+
TestID: test.ID,
259+
Passed: false,
260+
Message: fmt.Sprintf("Result mismatch: expected %s, got %s", string(expectedJSON), string(actualJSON)),
261+
}
262+
}
263+
}
264+
208265
return SingleTestResult{
209266
TestID: test.ID,
210267
Passed: true,

runner/types.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ type TestCase struct {
1515

1616
// TestExpectation defines what response is expected
1717
type TestExpectation struct {
18-
Success bool `json:"success"` // Whether operation should succeed
19-
Error *Error `json:"error,omitempty"` // Expected error details
18+
Success bool `json:"success"` // Whether operation should succeed
19+
Result json.RawMessage `json:"result,omitempty"` // Expected result data
20+
Error *Error `json:"error,omitempty"` // Expected error details
2021
}
2122

2223
// TestSuite represents a collection of test cases
2324
type TestSuite struct {
2425
Name string `json:"name"`
2526
Description string `json:"description,omitempty"`
27+
Stateful bool `json:"stateful,omitempty"` // If true, spawn new handler and fail subsequent tests on error
2628
Tests []TestCase `json:"tests"`
2729
}
2830

@@ -35,9 +37,10 @@ type Request struct {
3537

3638
// Response represents a response from the handler
3739
type Response struct {
38-
ID string `json:"id"`
39-
Success bool `json:"success"` // Whether operation succeeded
40-
Error *Error `json:"error,omitempty"` // Error details (if success=false)
40+
ID string `json:"id"`
41+
Success bool `json:"success"` // Whether operation succeeded
42+
Result json.RawMessage `json:"result,omitempty"` // Result data (if success=true)
43+
Error *Error `json:"error,omitempty"` // Error details (if success=false)
4144
}
4245

4346
// Error represents an error response

testdata/chain.json

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"name": "Chain",
3+
"description": "Tests chain state operations including block processing, active chain tracking, and handling chain reorganizations",
4+
"stateful": true,
5+
"tests": [
6+
{
7+
"id": "setup_regtest_chainstate_manager",
8+
"description": "Setup chainstate manager for regtest with default options",
9+
"method": "btck_chainstate_manager_create",
10+
"params": {
11+
"context": {
12+
"chain_type": "regtest"
13+
}
14+
},
15+
"expected": {
16+
"success": true
17+
}
18+
},
19+
{
20+
"id": "assert_chain_height_zero",
21+
"description": "Assert chain height is 0 (genesis block only)",
22+
"method": "btck_chain_get_height",
23+
"params": {},
24+
"expected": {
25+
"success": true,
26+
"result": {
27+
"height": 0
28+
}
29+
}
30+
},
31+
{
32+
"id": "process_blocks_1_to_3",
33+
"description": "Process blocks 1-3 sequentially",
34+
"method": "btck_chainstate_manager_process_block",
35+
"params": {
36+
"blocks_hex": [
37+
"0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f35b99ed4e2e165de2ad77f1bba48049358c9bb740445f3c83ebdb3e83aa5bca8dbe5494dffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025100feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
38+
"000000205e2f859d70e29641f32371f3bf17a282466ad851f9e51b44a70738abeace314a9cf876c62dbbe036af4ea4a7363cd4ca1c14c8572095cba3b76a87daa1303ed8dce5494dffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025200feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000001000000",
39+
"0000002077622c1ae937c9fec6be84d01521cb31b0e6f88ec48150965323dba6a1e36e19354352df0f2a5d635ca7d3a52064f9c95f070d2c13c0a6c087acba03dfeeae66dde5494dffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025300feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000002000000"
40+
]
41+
},
42+
"expected": {
43+
"success": true
44+
}
45+
},
46+
{
47+
"id": "assert_chain_height_three",
48+
"description": "Assert chain height is 3 after processing blocks",
49+
"method": "btck_chain_get_height",
50+
"params": {},
51+
"expected": {
52+
"success": true,
53+
"result": {
54+
"height": 3
55+
}
56+
}
57+
},
58+
{
59+
"id": "get_block_tree_entry_at_height_three",
60+
"description": "Get block tree entry at height 3",
61+
"method": "btck_chain_get_by_height",
62+
"params": {
63+
"height": 3
64+
},
65+
"expected": {
66+
"success": true,
67+
"result": {
68+
"height": 3,
69+
"block_hash": "1a81e97231fb262ccf464f937553a1deff996ac6901b062d0b35afe025ee2886"
70+
}
71+
}
72+
},
73+
{
74+
"id": "process_reorg_blocks",
75+
"description": "Process 3 blocks that cause a chain reorganization",
76+
"method": "btck_chainstate_manager_process_block",
77+
"params": {
78+
"blocks_hex": [
79+
"000000205e2f859d70e29641f32371f3bf17a282466ad851f9e51b44a70738abeace314a9cf876c62dbbe036af4ea4a7363cd4ca1c14c8572095cba3b76a87daa1303ed8dee5494dffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025200feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000001000000",
80+
"000000202c6c418b1f714cbe22c9c2906a5c1a3f5c0df22d32989b71f579b2289a0ccd4c354352df0f2a5d635ca7d3a52064f9c95f070d2c13c0a6c087acba03dfeeae66dfe5494dffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025300feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000002000000",
81+
"00000020732f2f7a1035b802d670218031da2b11d0fe7297ddaeb4a428fc51bf588770417bd00ba57498a2dfcf4e3f0d7ef7f279b254fc422133f300a49aed3c8ed7717fe0e5494dffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025400feffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000003000000"
82+
]
83+
},
84+
"expected": {
85+
"success": true
86+
}
87+
},
88+
{
89+
"id": "verify_reorg_old_block_not_in_chain",
90+
"description": "Verify that the old block at height 3 is no longer in the active chain after reorg",
91+
"method": "btck_chain_contains",
92+
"params": {
93+
"block_hash": "1a81e97231fb262ccf464f937553a1deff996ac6901b062d0b35afe025ee2886"
94+
},
95+
"expected": {
96+
"success": true,
97+
"result": {
98+
"contains": false
99+
}
100+
}
101+
},
102+
{
103+
"id": "assert_chain_height_four",
104+
"description": "Assert chain height is 4 after reorg",
105+
"method": "btck_chain_get_height",
106+
"params": {},
107+
"expected": {
108+
"success": true,
109+
"result": {
110+
"height": 4
111+
}
112+
}
113+
},
114+
{
115+
"id": "get_block_tree_entry_at_height_four",
116+
"description": "Get block tree entry at height 4 after reorg",
117+
"method": "btck_chain_get_by_height",
118+
"params": {
119+
"height": 4
120+
},
121+
"expected": {
122+
"success": true,
123+
"result": {
124+
"height": 4,
125+
"block_hash": "18618dcf64dddb10ea15d7850bc4c7965c9a72b613da8530b83057672f29bbfa"
126+
}
127+
}
128+
},
129+
{
130+
"id": "teardown_chainstate_manager",
131+
"description": "Teardown chainstate manager and cleanup resources",
132+
"method": "btck_chainstate_manager_destroy",
133+
"params": {},
134+
"expected": {
135+
"success": true
136+
}
137+
}
138+
]
139+
}

0 commit comments

Comments
 (0)