Skip to content

Commit fe6fb4b

Browse files
committed
Add object reference support via ref field in protocol
Introduces the object reference and registry pattern for persisting objects across requests. Test cases now include a ref field directly in the request when the expected result is a reference, allowing handlers to store and reuse objects like context, block, and chain throughout the tests. Extends response validation unit tests to test scenarios where reference is expected in response.
1 parent a958350 commit fe6fb4b

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

runner/runner.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ func validateResponseForSuccess(test TestCase, resp *Response) error {
254254
return fmt.Errorf("expected result with value, got null or omitted result")
255255
}
256256

257+
// If the request has a ref field, validate that the response is a reference object
258+
if test.Request.Ref != "" {
259+
refValue, ok := ParseRefObject(resp.Result)
260+
if !ok {
261+
return fmt.Errorf("expected reference object result for request with ref field, got: %s", string(resp.Result))
262+
}
263+
if refValue != test.Request.Ref {
264+
return fmt.Errorf("reference mismatch: expected ref %q, got %q", test.Request.Ref, refValue)
265+
}
266+
return nil
267+
}
268+
269+
// For non-ref results, normalize and compare
257270
expectedNorm, err := test.ExpectedResponse.Result.Normalize()
258271
if err != nil {
259272
return fmt.Errorf("failed to normalize expected result: %w", err)

runner/runner_test.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func TestValidateResponse(t *testing.T) {
170170
{
171171
name: "protocol violation with result not null when error present",
172172
testCaseJSON: `{
173-
"request": {"id": "11"},
173+
"request": {"id": "10"},
174174
"expected_response": {
175175
"error": {
176176
"code": {
@@ -195,7 +195,7 @@ func TestValidateResponse(t *testing.T) {
195195
{
196196
name: "error generic without code",
197197
testCaseJSON: `{
198-
"request": {"id": "12"},
198+
"request": {"id": "11"},
199199
"expected_response": {
200200
"error": {}
201201
}
@@ -206,6 +206,41 @@ func TestValidateResponse(t *testing.T) {
206206
}`,
207207
wantErr: false,
208208
},
209+
{
210+
name: "ref object success",
211+
testCaseJSON: `{
212+
"request": {"id": "12", "ref": "$ctx1"},
213+
"expected_response": {"result": {"ref": "$ctx1"}}
214+
}`,
215+
responseJSON: `{
216+
"result": {"ref": "$ctx1"}
217+
}`,
218+
wantErr: false,
219+
},
220+
{
221+
name: "ref object mismatch",
222+
testCaseJSON: `{
223+
"request": {"id": "13", "ref": "$ctx1"},
224+
"expected_response": {"result": {"ref": "$ctx1"}}
225+
}`,
226+
responseJSON: `{
227+
"result": {"ref": "$ctx2"}
228+
}`,
229+
wantErr: true,
230+
wantErrMsg: "reference mismatch",
231+
},
232+
{
233+
name: "ref in request but response not a ref object",
234+
testCaseJSON: `{
235+
"request": {"id": "14", "ref": "$ctx1"},
236+
"expected_response": {"result": {"ref": "$ctx1"}}
237+
}`,
238+
responseJSON: `{
239+
"result": true
240+
}`,
241+
wantErr: true,
242+
wantErrMsg: "expected reference object result",
243+
},
209244
}
210245

211246
for _, tt := range tests {

runner/types.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ type Request struct {
3030
ID string `json:"id"`
3131
Method string `json:"method"`
3232
Params json.RawMessage `json:"params,omitempty"`
33+
// Ref specifies the name the handler should use to store the returned object reference
34+
// in its registry. Required for methods that return object handles.
35+
Ref string `json:"ref,omitempty"`
3336
}
3437

3538
// Response represents a response from the handler.
@@ -82,3 +85,22 @@ func (r Result) Normalize() (string, error) {
8285
}
8386
return string(normalized), nil
8487
}
88+
89+
// RefObject represents a reference type result structure.
90+
type RefObject struct {
91+
Ref string `json:"ref"`
92+
}
93+
94+
// ParseRefObject extracts the ref value from a reference object.
95+
// Returns the ref string and true if the result is a valid ref object,
96+
// or empty string and false otherwise.
97+
func ParseRefObject[T ~[]byte](r T) (string, bool) {
98+
var refObj RefObject
99+
if err := json.Unmarshal(r, &refObj); err != nil {
100+
return "", false
101+
}
102+
if refObj.Ref == "" {
103+
return "", false
104+
}
105+
return refObj.Ref, true
106+
}

0 commit comments

Comments
 (0)