First Commit
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
actool "github.com/Autumn-27/norma/tool"
|
||||
)
|
||||
|
||||
// The server-level ToolSet behind buildDomainReg carries a nil ExplorationStore,
|
||||
// and the tools table can bind any of its tools to any agent — including agents
|
||||
// that never run inside a task. Every domain tool must therefore survive being
|
||||
// called with nil stores: a nil deref here runs on the harness's own goroutine,
|
||||
// out of reach of every recover() in the server, and kills the whole process.
|
||||
func TestDomainToolsSurviveNilStores(t *testing.T) {
|
||||
inputs := []string{
|
||||
`{}`,
|
||||
`{"id":379,"asset_id":1,"goal_id":1,"evidence_id":1,"intent_id":1,"node_id":1,"work_id":1,` +
|
||||
`"summary":"x","reason":"x","name":"x","severity":"low","vulnclass":"x","text":"x","q":"x"}`,
|
||||
}
|
||||
for _, tool := range NewToolSet(nil, "").AllDomainTools() {
|
||||
for _, in := range inputs {
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("%s panicked with nil stores on %s: %v", tool.Name(), in, r)
|
||||
}
|
||||
}()
|
||||
if _, err := tool.Call(context.Background(), json.RawMessage(in), nil); err != nil {
|
||||
t.Fatalf("%s returned a transport error: %v", tool.Name(), err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// node_detail is the one that took the process down; assert it now answers with a
|
||||
// usable refusal rather than dying.
|
||||
func TestExplorationToolRefusesWithoutTask(t *testing.T) {
|
||||
ts := NewToolSet(nil, "")
|
||||
res, err := ts.NodeDetailTool().Call(context.Background(), json.RawMessage(`{"id":379}`), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !res.IsError || !strings.Contains(res.Flatten(), "任务上下文") {
|
||||
t.Fatalf("want an explanatory tool error, got IsError=%v %q", res.IsError, res.Flatten())
|
||||
}
|
||||
}
|
||||
|
||||
// A panicking tool must degrade to a tool error: the run continues and the process
|
||||
// survives, instead of the supervisor restarting into the same crash on replay.
|
||||
func TestGuardPanicConvertsPanicToToolError(t *testing.T) {
|
||||
boom := actool.Build(actool.Spec{
|
||||
Name: "boom",
|
||||
Run: func(context.Context, json.RawMessage, *actool.ToolContext) (actool.Result, error) {
|
||||
panic("nil map write")
|
||||
},
|
||||
})
|
||||
res, err := guardPanic(boom).Call(context.Background(), json.RawMessage(`{}`), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !res.IsError || !strings.Contains(res.Flatten(), "nil map write") {
|
||||
t.Fatalf("want the panic reported as a tool error, got IsError=%v %q", res.IsError, res.Flatten())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user