Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ jobs:
- name: Build
run: make build

- name: Run tests against mock handler
- name: Run tests
run: make test
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ mock-handler:
go build -o $(MOCK_HANDLER_BIN) ./cmd/mock-handler

test:
@echo "Running runner unit tests..."
go test ./runner/...
@echo "Running conformance tests with mock handler..."
$(RUNNER_BIN) -handler $(MOCK_HANDLER_BIN)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ make runner

### Testing the Runner

Build and test the runner using the mock handler:
Build and test the runner:

```bash
# Build both runner and mock handler
make build

# Run the test runner against the mock handler
# Run runner unit tests and integration tests with mock handler
make test
```
30 changes: 19 additions & 11 deletions cmd/mock-handler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ func handleRequest(line string, testIndex map[string]string) error {
resp := runner.Response{
ID: req.ID,
Error: &runner.Error{
Type: "Handler",
Variant: "UnknownTest",
Code: &runner.ErrorCode{
Type: "Handler",
Member: "UNKNOWN_TEST",
},
},
}
return writeResponse(resp)
Expand All @@ -94,8 +96,10 @@ func handleRequest(line string, testIndex map[string]string) error {
resp := runner.Response{
ID: req.ID,
Error: &runner.Error{
Type: "Handler",
Variant: "LoadError",
Code: &runner.ErrorCode{
Type: "Handler",
Member: "LOAD_ERROR",
},
},
}
return writeResponse(resp)
Expand All @@ -113,8 +117,10 @@ func handleRequest(line string, testIndex map[string]string) error {
resp := runner.Response{
ID: req.ID,
Error: &runner.Error{
Type: "Handler",
Variant: "TestNotFound",
Code: &runner.ErrorCode{
Type: "Handler",
Member: "TEST_NOT_FOUND",
},
},
}
return writeResponse(resp)
Expand All @@ -125,18 +131,20 @@ func handleRequest(line string, testIndex map[string]string) error {
resp := runner.Response{
ID: req.ID,
Error: &runner.Error{
Type: "Handler",
Variant: "MethodMismatch",
Code: &runner.ErrorCode{
Type: "Handler",
Member: "METHOD_MISMATCH",
},
},
}
return writeResponse(resp)
}

// Build response based on expected result
return writeResponse(runner.Response{
ID: req.ID,
Success: testCase.Expected.Success,
Error: testCase.Expected.Error,
ID: req.ID,
Result: testCase.Expected.Result,
Error: testCase.Expected.Error,
})
}

Expand Down
74 changes: 36 additions & 38 deletions docs/handler-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,32 @@ Handlers communicate with the test runner via **stdin/stdout**:
**Fields:**
- `id` (string, required): Unique identifier for this request
- `method` (string, required): The operation to perform. Each unique method must be implemented by the handler to exercise the corresponding binding API operation.
- `params` (object, required): Method-specific parameters (can be `null` or `{}`)
- `params` (object, optional): Method-specific parameters

### Response

```json
{
"id": "unique-request-id",
"success": { /* method-specific result */ }
}
```

**Success response fields:**
- `id` (string, required): Must match the request ID
- `success` (any, required): Method-specific result data. Must be present on success (can be empty `{}`)
- `error` (null or omitted): Must not be present on success

### Error Response

```json
{
"id": "unique-request-id",
"result": null,
"error": {
"type": "error_category",
"variant": "specific_error"
"code": {
"type": "error_type",
"member": "ERROR_MEMBER_NAME"
}
}
}
```

**Error response fields:**
**Fields:**
- `id` (string, required): Must match the request ID
- `success` (null or omitted): Must not be present on error
- `error` (object, required): Error details
- `type` (string, required): Error category/type
- `variant` (string, optional): Specific error variant within the type. Whether the runner expects this field depends on the specific test case
- `result` (any, optional): The return value, or `null` for void/nullptr operations. Must be `null` on error
- `error` (object, optional): Error details. Must be `null` on success. An empty object `{}` is used to indicate an error is raised without further details, it is NOT equivalent to `null`
- `code` (object, optional): Error code details
- `type` (string, required): Error type (e.g., "btck_ScriptVerifyStatus")
- `member` (string, required): Specific error member (e.g., "ERROR_INVALID_FLAGS_COMBINATION")

**Note:** Throughout this protocol, an omitted field is semantically equivalent to `null`.

## Handler Requirements

Expand All @@ -74,43 +66,49 @@ The conformance tests are organized into suites, each testing a specific aspect
### Script Verification Success Cases
**File:** [`script_verify_success.json`](../testdata/script_verify_success.json)

Tests valid Bitcoin script verification scenarios across different transaction types.
Test cases where the script verification operation executes successfully and returns a boolean result (true for valid scripts, false for invalid scripts).

**Method:** `script_pubkey.verify`
**Method:** `btck_script_pubkey_verify`

**Expected Response Format:**
```json
{
"id": "test-id",
"success": {}
"result": true
}
```
or
```json
{
"id": "test-id",
"result": false
}
```

### Script Verification Error Cases
**File:** [`script_verify_errors.json`](../testdata/script_verify_errors.json)

Tests error handling for invalid script verification scenarios.
Test cases where the verification operation fails to determine validity of the script due to bad user input.

**Method:** `script_pubkey.verify`
**Method:** `btck_script_pubkey_verify`

**Expected Response Format:**
```json
{
"id": "test-id",
"result": null,
"error": {
"type": "ScriptVerify",
"variant": "ErrorVariant"
"code": {
"type": "btck_ScriptVerifyStatus",
"member": "ERROR_MEMBER_NAME"
}
}
}
```

**Error Variants:**
**Error Members:**

| Variant | Description |
|---------|-------------|
| `TxInputIndex` | The specified input index is out of bounds. The `input_index` parameter is greater than or equal to the number of inputs in the transaction. |
| `InvalidFlags` | Invalid verification flags were provided. The flags parameter contains bits that don't correspond to any defined verification flag. |
| `InvalidFlagsCombination` | Invalid or inconsistent verification flags were provided. This occurs when the supplied `script_verify_flags` combination violates internal consistency rules. |
| `SpentOutputsMismatch` | The spent_outputs array length doesn't match the input count. When spent_outputs is non-empty, it must contain exactly one output for each input in the transaction. |
| `SpentOutputsRequired` | Spent outputs are required but were not provided. |
| `Invalid` | Script verification failed. |
| Member | Description |
|--------|-------------|
| `ERROR_INVALID_FLAGS_COMBINATION` | Invalid or inconsistent verification flags were provided. This occurs when the supplied `script_verify_flags` combination violates internal consistency rules. |
| `ERROR_SPENT_OUTPUTS_REQUIRED` | Spent outputs are required but were not provided (e.g., for Taproot verification). |
Loading
Loading