Address #130 review: one toolCaller helper for the tool tests
Build image / build-and-push (push) Successful in 7s

The call/mustCall closures were copied between TestRecordKeepingTools and
TestCatalogAndGardenTools; both now use a file-level toolCaller. The other
notes are left as they are: the 'nothing to change' guard enumerates the
args on purpose (it is the tool's own contract, next to the struct it
checks), and wrapping a sentinel with %w is how every readable refusal in
this package is built.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-08-23 02:11:27 -04:00
co-authored by Claude Fable 5
parent b4c8007977
commit c9076e84c4
+30 -32
View File
@@ -123,6 +123,34 @@ func TestToolboxScenario(t *testing.T) {
}
}
// toolCaller returns the two ways a test drives a toolbox: call executes a
// tool with JSON-encoded args and hands back the raw result (for asserting on
// refusals), and mustCall fails the test on a tool error and decodes the
// result into `into` when one is given.
func toolCaller(t *testing.T, ctx context.Context, box *llm.Toolbox) (
call func(name string, args any) llm.ToolResult,
mustCall func(name string, args any, into any),
) {
t.Helper()
call = func(name string, args any) llm.ToolResult {
t.Helper()
return box.Execute(ctx, llm.ToolCall{ID: "1", Name: name, Arguments: mustJSON(t, args)})
}
mustCall = func(name string, args any, into any) {
t.Helper()
res := call(name, args)
if res.IsError {
t.Fatalf("%s: %s", name, res.Content)
}
if into != nil {
if err := json.Unmarshal([]byte(res.Content), into); err != nil {
t.Fatalf("decode %s: %v (%s)", name, err, res.Content)
}
}
}
return call, mustCall
}
func mustJSON(t *testing.T, v any) json.RawMessage {
t.Helper()
b, err := json.Marshal(v)
@@ -750,22 +778,7 @@ func TestRecordKeepingTools(t *testing.T) {
svc, owner := newAgentTestService(t)
box := NewToolbox(svc, owner, "2026-08-23")
call := func(name string, args any) llm.ToolResult {
t.Helper()
return box.Execute(ctx, llm.ToolCall{ID: "1", Name: name, Arguments: mustJSON(t, args)})
}
mustCall := func(name string, args any, into any) {
t.Helper()
res := call(name, args)
if res.IsError {
t.Fatalf("%s: %s", name, res.Content)
}
if into != nil {
if err := json.Unmarshal([]byte(res.Content), into); err != nil {
t.Fatalf("decode %s: %v (%s)", name, err, res.Content)
}
}
}
call, mustCall := toolCaller(t, ctx, box)
g, err := svc.CreateGarden(ctx, owner, service.GardenInput{Name: "Home", WidthCM: 1200, HeightCM: 800, Notes: "Zone 6a."})
if err != nil {
@@ -940,22 +953,7 @@ func TestCatalogAndGardenTools(t *testing.T) {
svc, owner := newAgentTestService(t)
box := NewToolbox(svc, owner, "2026-08-23")
call := func(name string, args any) llm.ToolResult {
t.Helper()
return box.Execute(ctx, llm.ToolCall{ID: "1", Name: name, Arguments: mustJSON(t, args)})
}
mustCall := func(name string, args any, into any) {
t.Helper()
res := call(name, args)
if res.IsError {
t.Fatalf("%s: %s", name, res.Content)
}
if into != nil {
if err := json.Unmarshal([]byte(res.Content), into); err != nil {
t.Fatalf("decode %s: %v (%s)", name, err, res.Content)
}
}
}
call, mustCall := toolCaller(t, ctx, box)
// --- create_garden: defaults, then an imperial one with notes.
var g domain.Garden