@@ -15,6 +15,19 @@ import (
1515 aiService "github.com/EvolutionAPI/evo-bot-runtime/pkg/ai/service"
1616)
1717
18+ // crmMetadata builds the nested CRM metadata map the Rails AgentBotListener sends,
19+ // carrying the conversation UUID at evoai_crm_data.conversation.id.
20+ func crmMetadata (conversationUUID string ) map [string ]any {
21+ return map [string ]any {
22+ "evoai_crm_data" : map [string ]any {
23+ "conversation" : map [string ]any {
24+ "id" : conversationUUID ,
25+ "display_id" : 7 ,
26+ },
27+ },
28+ }
29+ }
30+
1831func TestCall_Success (t * testing.T ) {
1932 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2033 // Verify X-API-Key header (per-event auth)
@@ -25,7 +38,7 @@ func TestCall_Success(t *testing.T) {
2538 t .Errorf ("Content-Type = %q, want application/json" , got )
2639 }
2740
28- // Verify URL path contains agent_bot_id
41+ // Verify URL path is the OutgoingURL provided by the CRM (already contains the agent ID)
2942 if ! strings .HasSuffix (r .URL .Path , "/api/v1/a2a/agent-123" ) {
3043 t .Errorf ("URL path = %q, want suffix /api/v1/a2a/agent-123" , r .URL .Path )
3144 }
@@ -41,8 +54,9 @@ func TestCall_Success(t *testing.T) {
4154 if req .Method != "message/send" {
4255 t .Errorf ("req.Method = %q, want %q" , req .Method , "message/send" )
4356 }
44- if req .Params .ContextID != "7" {
45- t .Errorf ("req.Params.ContextID = %q, want %q" , req .Params .ContextID , "7" )
57+ // contextId must be the conversation UUID from metadata, not the numeric ID.
58+ if req .Params .ContextID != "conv-uuid-abc" {
59+ t .Errorf ("req.Params.ContextID = %q, want %q" , req .Params .ContextID , "conv-uuid-abc" )
4660 }
4761 if req .Params .UserID != "42" {
4862 t .Errorf ("req.Params.UserID = %q, want %q" , req .Params .UserID , "42" )
@@ -65,13 +79,14 @@ func TestCall_Success(t *testing.T) {
6579 }))
6680 defer server .Close ()
6781
68- adapter := aiService .NewAIAdapter (server . URL , 30 )
82+ adapter := aiService .NewAIAdapter (30 )
6983 resp , err := adapter .Call (context .Background (), & aiModel.A2ARequest {
70- AgentBotID : " agent-123" ,
84+ OutgoingURL : server . URL + "/api/v1/a2a/ agent-123" ,
7185 Message : "hello world" ,
7286 ContactID : 42 ,
7387 ConversationID : 7 ,
7488 ApiKey : "test-key" ,
89+ Metadata : crmMetadata ("conv-uuid-abc" ),
7590 })
7691 if err != nil {
7792 t .Fatalf ("Call returned unexpected error: %v" , err )
@@ -81,6 +96,41 @@ func TestCall_Success(t *testing.T) {
8196 }
8297}
8398
99+ // TestCall_ContextID_FallsBackToNumericID asserts that when the CRM metadata is
100+ // absent the adapter falls back to the numeric conversation ID (legacy behaviour),
101+ // so callers that do not send metadata keep working.
102+ func TestCall_ContextID_FallsBackToNumericID (t * testing.T ) {
103+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
104+ var req aiModel.JSONRPCRequest
105+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
106+ t .Errorf ("decode request body: %v" , err )
107+ }
108+ if req .Params .ContextID != "7" {
109+ t .Errorf ("req.Params.ContextID = %q, want fallback %q" , req .Params .ContextID , "7" )
110+ }
111+ json .NewEncoder (w ).Encode (aiModel.A2AResponse {
112+ Result : & aiModel.A2AResult {
113+ Artifacts : []aiModel.A2AArtifact {
114+ {Parts : []aiModel.A2APart {{Type : "text" , Text : "ok" }}},
115+ },
116+ },
117+ })
118+ }))
119+ defer server .Close ()
120+
121+ adapter := aiService .NewAIAdapter (30 )
122+ _ , err := adapter .Call (context .Background (), & aiModel.A2ARequest {
123+ OutgoingURL : server .URL ,
124+ Message : "test" ,
125+ ConversationID : 7 ,
126+ ApiKey : "key" ,
127+ // No Metadata → must fall back to the numeric ConversationID.
128+ })
129+ if err != nil {
130+ t .Fatalf ("Call returned unexpected error: %v" , err )
131+ }
132+ }
133+
84134func TestCall_Success_MessageFormat (t * testing.T ) {
85135 // Test the fallback response format (result.message instead of result.artifacts)
86136 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -95,11 +145,11 @@ func TestCall_Success_MessageFormat(t *testing.T) {
95145 }))
96146 defer server .Close ()
97147
98- adapter := aiService .NewAIAdapter (server . URL , 30 )
148+ adapter := aiService .NewAIAdapter (30 )
99149 resp , err := adapter .Call (context .Background (), & aiModel.A2ARequest {
100- AgentBotID : "bot-1" ,
101- Message : "test" ,
102- ApiKey : "key" ,
150+ OutgoingURL : server . URL ,
151+ Message : "test" ,
152+ ApiKey : "key" ,
103153 })
104154 if err != nil {
105155 t .Fatalf ("Call returned unexpected error: %v" , err )
@@ -122,15 +172,15 @@ func TestCall_ContextCancellation_ReturnsPipelineCancelled(t *testing.T) {
122172 server .Close ()
123173 })
124174
125- adapter := aiService .NewAIAdapter (server . URL , 30 )
175+ adapter := aiService .NewAIAdapter (30 )
126176 ctx , cancel := context .WithCancel (context .Background ())
127177
128178 go func () {
129179 time .Sleep (50 * time .Millisecond )
130180 cancel ()
131181 }()
132182
133- _ , err := adapter .Call (ctx , & aiModel.A2ARequest {AgentBotID : "bot-1" , Message : "test" , ApiKey : "key" })
183+ _ , err := adapter .Call (ctx , & aiModel.A2ARequest {OutgoingURL : server . URL , Message : "test" , ApiKey : "key" })
134184 if ! errors .Is (err , brtErrors .ErrPipelineCancelled ) {
135185 t .Errorf ("expected ErrPipelineCancelled, got %v" , err )
136186 }
@@ -149,8 +199,8 @@ func TestCall_Timeout_ReturnsAITimeout(t *testing.T) {
149199 server .Close ()
150200 })
151201
152- adapter := aiService .NewAIAdapter (server . URL , 1 ) // 1 s timeout
153- _ , err := adapter .Call (context .Background (), & aiModel.A2ARequest {AgentBotID : "bot-1" , Message : "test" , ApiKey : "key" })
202+ adapter := aiService .NewAIAdapter (1 ) // 1 s timeout
203+ _ , err := adapter .Call (context .Background (), & aiModel.A2ARequest {OutgoingURL : server . URL , Message : "test" , ApiKey : "key" })
154204 if ! errors .Is (err , brtErrors .ErrAITimeout ) {
155205 t .Errorf ("expected ErrAITimeout, got %v" , err )
156206 }
@@ -162,8 +212,8 @@ func TestCall_NonOKStatus_ReturnsError(t *testing.T) {
162212 }))
163213 defer server .Close ()
164214
165- adapter := aiService .NewAIAdapter (server . URL , 30 )
166- _ , err := adapter .Call (context .Background (), & aiModel.A2ARequest {AgentBotID : "bot-1" , Message : "test" , ApiKey : "key" })
215+ adapter := aiService .NewAIAdapter (30 )
216+ _ , err := adapter .Call (context .Background (), & aiModel.A2ARequest {OutgoingURL : server . URL , Message : "test" , ApiKey : "key" })
167217 if err == nil {
168218 t .Fatal ("expected error for non-200 response, got nil" )
169219 }
0 commit comments