8 "github.com/stretchr/testify/require"
11 func TestRootRouteToolsPersistAndRemoveRoutes(t *testing.T) {
13 server := newModelFake(t)
14 test := newDaemonMessageTest(t, dir, server)
16 source := "whatsapp:" + whatsappTestFamily.String()
17 arguments := `{"source":"` + source + `","agent":"Garden"}`
18 require.Equal(t, "Route set.", root.tools.run(t.Context(), "route", arguments))
19 require.Equal(t, source+" -> Garden", root.tools.run(t.Context(), "routes", `{}`))
21 info, err := os.Stat(filepath.Join(root.agent.dir, "routes.json"))
22 require.NoError(t, err)
23 require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
24 require.Contains(t, test.logs.String(), "route set kind=whatsapp name=Garden")
25 require.NotContains(t, test.logs.String(), whatsappTestFamily.String())
28 restored := newDaemonMessageTest(t, dir, server)
29 require.Equal(t, source+" -> Garden", restored.loops[0].tools.run(t.Context(), "routes", `{}`))
30 require.Equal(t, "Route removed.", restored.loops[0].tools.run(t.Context(), "unroute", `{"source":"`+source+`"}`))
31 require.Equal(t, "No routes.", restored.loops[0].tools.run(t.Context(), "routes", `{}`))
32 require.Contains(t, restored.logs.String(), "route removed kind=whatsapp")
35 empty := newDaemonMessageTest(t, dir, server)
36 require.Equal(t, "No routes.", empty.loops[0].tools.run(t.Context(), "routes", `{}`))
37 require.Zero(t, server.Calls())
40 func TestRoutesRefuseUnknownSourcesAndNonchildren(t *testing.T) {
41 test := newDaemonMessageTest(t, t.TempDir(), newModelFake(t))
42 testCases := []struct {
52 cause: "unknown source kind",
56 source: whatsappTestFamily.String(),
58 cause: "unknown source kind",
64 cause: "invalid WhatsApp source",
67 name: "unknown chat kind",
68 source: "whatsapp:example@unknown",
70 cause: "invalid WhatsApp source",
73 name: "root is not a child",
74 source: "whatsapp:" + whatsappTestFamily.String(),
76 cause: "must be a child",
79 name: "unknown child",
80 source: "whatsapp:" + whatsappTestFamily.String(),
82 cause: "must be a child",
86 source: "whatsapp:" + whatsappTestFamily.String(),
88 cause: "must be a child",
91 for _, tc := range testCases {
92 t.Run(tc.name, func(t *testing.T) {
93 arguments := `{"source":"` + tc.source + `","agent":"` + tc.agent + `"}`
94 require.Contains(t, test.loops[0].tools.run(t.Context(), "route", arguments), tc.cause)
98 require.Contains(t, test.loops[0].tools.run(t.Context(), "unroute", `{"source":"mail:inbox"}`), "unknown source kind")
99 require.Equal(t, "No routes.", test.messages.routes.List())
102 func TestRouteToolsBelongOnlyToRoot(t *testing.T) {
103 test := newDaemonMessageTest(t, t.TempDir(), newModelFake(t))
104 for _, name := range []string{"route", "unroute", "routes"} {
105 require.Contains(t, promptToolNames(test.agents[0].agent.Tools()), name)
106 require.NotContains(t, promptToolNames(test.agents[1].agent.Tools()), name)
107 require.NotContains(t, promptToolNames(promptSubagentTools()), name)
108 require.Contains(t, test.loops[1].tools.run(t.Context(), name, `{}`), "unknown tool")
111 require.NotContains(t, promptChild("Garden", "OneChat", "Keep plans.", "/CHILD/instructions.md", ""), "Propose routing a source")
114 func TestWhatsAppStatusListsRoutes(t *testing.T) {
115 whatsapp, _, _ := whatsappStartTest(t, t.TempDir(), newTestClock())
116 test := newDaemonMessageTest(t, t.TempDir(), newModelFake(t))
117 whatsapp.routes = test.messages.routes
118 require.NoError(t, test.messages.routes.Set("whatsapp:"+whatsappTestFamily.String(), "Garden"))
122 whatsapp.Command("status", func(text string) {
125 require.Contains(t, answer, "WhatsApp is not linked.")
126 require.Contains(t, answer, "WhatsApp routes:\nwhatsapp:"+whatsappTestFamily.String()+" -> Garden")
129 func TestInvalidRouteFileStopsStartup(t *testing.T) {
130 testCases := []struct {
135 name: "invalid JSON",
139 name: "unknown source",
140 text: `{"mail:inbox":"Garden"}`,
143 name: "empty destination",
144 text: `{"whatsapp:example@lid":""}`,
147 for _, tc := range testCases {
148 t.Run(tc.name, func(t *testing.T) {
149 path := filepath.Join(t.TempDir(), "routes.json")
150 require.NoError(t, os.WriteFile(path, []byte(tc.text), 0o600))
152 _, err := openRoutes(path, discardLog())
153 require.Error(t, err)