diff --git a/core/application/studio_connectors.go b/core/application/studio_connectors.go index 9b17872ca7..8382d5f55e 100644 --- a/core/application/studio_connectors.go +++ b/core/application/studio_connectors.go @@ -108,6 +108,14 @@ func (a *Application) startStudioConnectors(svc *studio.Service) { var tr studio.ConnectorTransport = connectorTransport{a} if connectorTransportForTests != nil { tr = connectorTransportForTests + // A test transport that can move files by handle gets the table of + // this application, so its add-ons declare the files ability. + if x, ok := tr.(interface { + EnableTransfers(*hostops.Transfers) func() + }); ok { + // The servers it starts live as long as the test process. + _ = x.EnableTransfers(a.transfers()) + } } // A token is checked with the platform when it is saved, so a typo is // refused at once instead of switching the binding off later. diff --git a/tests/e2e/studio_slack_test.go b/tests/e2e/studio_slack_test.go index e794659584..9662f095f9 100644 --- a/tests/e2e/studio_slack_test.go +++ b/tests/e2e/studio_slack_test.go @@ -171,12 +171,25 @@ var _ = Describe("Chats Slack connector", Label("Chats", "Slack"), func() { }) It("hands a file of a message to the chat", func() { - bot, appToken, id := app(obj{"allow": []string{"U100"}}) + // A binding holds no capability until the owner enables it, and a sender + // without a role needs a pairing to receive files. + bot, appToken, id := app(obj{"allow": []string{"U100"}, "caps": []string{"files.upload"}}) + code, out := chatsDo(http.MethodPost, chatsBase()+"/connectors/"+id+"/pairing", nil) + Expect(code).To(Equal(http.StatusOK), string(out)) + f().Push(appToken, slacktest.DM("D100", "U100", decode(out)["code"].(string))) + Eventually(func() int { + code, out := chatsDo(http.MethodGet, chatsBase()+"/connectors/"+id, nil) + Expect(code).To(Equal(http.StatusOK), string(out)) + paired, _ := decode(out)["paired"].([]any) + return len(paired) + }, 30*time.Second, 200*time.Millisecond).Should(Equal(1)) + // The bot confirms the pairing with a post of its own. + before := len(answers(bot)()) f().AddFile("F777", "brief.txt", "text/plain", []byte("the brief")) ev := slacktest.DM("D100", "U100", "read the brief") ev["files"] = []any{map[string]any{"id": "F777"}} f().Push(appToken, ev) - Eventually(answers(bot), 90*time.Second, 200*time.Millisecond).Should(HaveLen(1)) + Eventually(answers(bot), 90*time.Second, 200*time.Millisecond).Should(HaveLen(before + 1)) chats := chatsOf(id) Expect(chats).To(HaveLen(1)) // The file is an attachment of the user line, apart from its words. diff --git a/tests/e2e/studio_telegram_test.go b/tests/e2e/studio_telegram_test.go index 1530a84359..8dbfcef398 100644 --- a/tests/e2e/studio_telegram_test.go +++ b/tests/e2e/studio_telegram_test.go @@ -40,7 +40,7 @@ var _ = Describe("Chats Telegram connector", Label("Chats", "Telegram"), func() bind := func(token string, agent string, extra obj) string { // The specs written before the reply modes count the answers as the // messages the bot sends; the live modes have their own specs. - body := obj{"name": fmt.Sprintf("Bot %d", time.Now().UnixNano()), "token": token, "agent_id": agent, "enabled": true, "reply_mode": "off"} + body := obj{"kind": "telegram", "name": fmt.Sprintf("Bot %d", time.Now().UnixNano()), "token": token, "agent_id": agent, "enabled": true, "reply_mode": "off"} for k, v := range extra { body[k] = v } @@ -260,9 +260,9 @@ var _ = Describe("Chats Telegram connector", Label("Chats", "Telegram"), func() Expect(code).To(Equal(http.StatusCreated), string(out)) agent := decode(out)["id"].(string) DeferCleanup(func() { chatsDo(http.MethodDelete, chatsBase()+"/agents/"+agent, nil) }) - code, out = chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"name": "x", "token": "7010:x", "agent_id": agent}) + code, out = chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"kind": "telegram", "name": "x", "token": "7010:x", "agent_id": agent}) Expect(code).To(Equal(http.StatusBadRequest), string(out)) - code, out = chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"name": "x", "token": "7010:x"}) + code, out = chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"kind": "telegram", "name": "x", "token": "7010:x"}) Expect(code).To(Equal(http.StatusBadRequest), "a binding without an agent: "+string(out)) }) @@ -323,7 +323,7 @@ var _ = Describe("Chats Telegram connector", Label("Chats", "Telegram"), func() token := "7014:typo" telegram.refuseToken(token, 401) DeferCleanup(func() { telegram.refuseToken(token, 0) }) - code, out := chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"name": "Typo", "token": token, "agent_id": newAgent()}) + code, out := chatsDo(http.MethodPost, chatsBase()+"/connectors", obj{"kind": "telegram", "name": "Typo", "token": token, "agent_id": newAgent()}) Expect(code).To(Equal(http.StatusBadRequest), string(out)) Expect(string(out)).To(ContainSubstring("refused the bot token")) })