@@ -2314,3 +2314,295 @@ func TestV2_DownloadTelegramFile_DownloadFailure(t *testing.T) {
23142314 require .Error (t , err )
23152315 assert .Contains (t , err .Error (), "download file" )
23162316}
2317+
2318+ // --- Code span restoration tests ---
2319+
2320+ func TestV2_HandleGroupMessage_CodeSpanPreserved (t * testing.T ) {
2321+ tgSrv := newFakeTGServerV2 (t )
2322+ hub := newFakeHubClient ()
2323+ hub .agents ["proj-1" ] = []AgentInfo {{Slug : "coder" }}
2324+ b := newTestBrokerV2WithHub (t , tgSrv , hub )
2325+
2326+ ctx := context .Background ()
2327+ require .NoError (t , b .store .SaveUserMapping (ctx , & TelegramUserMapping {
2328+ TelegramUserID : "456" ,
2329+ ScionEmail : "alice@example.com" ,
2330+ LinkedAt : time .Now ().UTC (),
2331+ }))
2332+ require .NoError (t , b .store .SaveGroupLink (ctx , & GroupLink {
2333+ ChatID : - 200 ,
2334+ ProjectID : "proj-1" ,
2335+ ProjectSlug : "my-project" ,
2336+ DefaultAgent : "coder" ,
2337+ LinkedAt : time .Now ().UTC (),
2338+ Active : true ,
2339+ }))
2340+ require .NoError (t , b .store .SaveProjectAgents (ctx , & ProjectAgents {
2341+ ProjectID : "proj-1" ,
2342+ Agents : []AgentInfo {{Slug : "coder" }},
2343+ RefreshedAt : time .Now (),
2344+ }))
2345+
2346+ var deliveredMsg * messages.StructuredMessage
2347+ done := make (chan struct {}, 1 )
2348+ b .InboundHandler = func (_ string , msg * messages.StructuredMessage ) {
2349+ deliveredMsg = msg
2350+ select {
2351+ case done <- struct {}{}:
2352+ default :
2353+ }
2354+ }
2355+
2356+ // Simulate: user typed "check the `config` file" in Telegram.
2357+ // Telegram strips backticks and puts formatting in entities.
2358+ b .handleGroupMessage (& TGMessage {
2359+ MessageID : 42 ,
2360+ From : & TGUser {ID : 456 , Username : "alice" },
2361+ Chat : TGChat {ID : - 200 , Type : "group" },
2362+ Date : time .Now ().Unix (),
2363+ Text : "@coder check the config file" ,
2364+ Entities : []MessageEntity {
2365+ {Type : "mention" , Offset : 0 , Length : 6 },
2366+ {Type : "code" , Offset : 17 , Length : 6 },
2367+ },
2368+ })
2369+
2370+ select {
2371+ case <- done :
2372+ case <- time .After (2 * time .Second ):
2373+ t .Fatal ("timed out waiting for delivery" )
2374+ }
2375+
2376+ assert .Equal (t , "check the `config` file" , deliveredMsg .Msg )
2377+ }
2378+
2379+ func TestV2_HandleGroupMessage_MultipleCodeSpans (t * testing.T ) {
2380+ tgSrv := newFakeTGServerV2 (t )
2381+ hub := newFakeHubClient ()
2382+ hub .agents ["proj-1" ] = []AgentInfo {{Slug : "coder" }}
2383+ b := newTestBrokerV2WithHub (t , tgSrv , hub )
2384+
2385+ ctx := context .Background ()
2386+ require .NoError (t , b .store .SaveUserMapping (ctx , & TelegramUserMapping {
2387+ TelegramUserID : "456" ,
2388+ ScionEmail : "alice@example.com" ,
2389+ LinkedAt : time .Now ().UTC (),
2390+ }))
2391+ require .NoError (t , b .store .SaveGroupLink (ctx , & GroupLink {
2392+ ChatID : - 200 ,
2393+ ProjectID : "proj-1" ,
2394+ ProjectSlug : "my-project" ,
2395+ DefaultAgent : "coder" ,
2396+ LinkedAt : time .Now ().UTC (),
2397+ Active : true ,
2398+ }))
2399+ require .NoError (t , b .store .SaveProjectAgents (ctx , & ProjectAgents {
2400+ ProjectID : "proj-1" ,
2401+ Agents : []AgentInfo {{Slug : "coder" }},
2402+ RefreshedAt : time .Now (),
2403+ }))
2404+
2405+ var deliveredMsg * messages.StructuredMessage
2406+ done := make (chan struct {}, 1 )
2407+ b .InboundHandler = func (_ string , msg * messages.StructuredMessage ) {
2408+ deliveredMsg = msg
2409+ select {
2410+ case done <- struct {}{}:
2411+ default :
2412+ }
2413+ }
2414+
2415+ // Simulate: "run foo and bar" where foo and bar were in backticks.
2416+ b .handleGroupMessage (& TGMessage {
2417+ MessageID : 43 ,
2418+ From : & TGUser {ID : 456 , Username : "alice" },
2419+ Chat : TGChat {ID : - 200 , Type : "group" },
2420+ Date : time .Now ().Unix (),
2421+ Text : "@coder run foo and bar" ,
2422+ Entities : []MessageEntity {
2423+ {Type : "mention" , Offset : 0 , Length : 6 },
2424+ {Type : "code" , Offset : 11 , Length : 3 },
2425+ {Type : "code" , Offset : 19 , Length : 3 },
2426+ },
2427+ })
2428+
2429+ select {
2430+ case <- done :
2431+ case <- time .After (2 * time .Second ):
2432+ t .Fatal ("timed out waiting for delivery" )
2433+ }
2434+
2435+ assert .Equal (t , "run `foo` and `bar`" , deliveredMsg .Msg )
2436+ }
2437+
2438+ func TestV2_HandleGroupMessage_PreBlockPreserved (t * testing.T ) {
2439+ tgSrv := newFakeTGServerV2 (t )
2440+ hub := newFakeHubClient ()
2441+ hub .agents ["proj-1" ] = []AgentInfo {{Slug : "coder" }}
2442+ b := newTestBrokerV2WithHub (t , tgSrv , hub )
2443+
2444+ ctx := context .Background ()
2445+ require .NoError (t , b .store .SaveUserMapping (ctx , & TelegramUserMapping {
2446+ TelegramUserID : "456" ,
2447+ ScionEmail : "alice@example.com" ,
2448+ LinkedAt : time .Now ().UTC (),
2449+ }))
2450+ require .NoError (t , b .store .SaveGroupLink (ctx , & GroupLink {
2451+ ChatID : - 200 ,
2452+ ProjectID : "proj-1" ,
2453+ ProjectSlug : "my-project" ,
2454+ DefaultAgent : "coder" ,
2455+ LinkedAt : time .Now ().UTC (),
2456+ Active : true ,
2457+ }))
2458+ require .NoError (t , b .store .SaveProjectAgents (ctx , & ProjectAgents {
2459+ ProjectID : "proj-1" ,
2460+ Agents : []AgentInfo {{Slug : "coder" }},
2461+ RefreshedAt : time .Now (),
2462+ }))
2463+
2464+ var deliveredMsg * messages.StructuredMessage
2465+ done := make (chan struct {}, 1 )
2466+ b .InboundHandler = func (_ string , msg * messages.StructuredMessage ) {
2467+ deliveredMsg = msg
2468+ select {
2469+ case done <- struct {}{}:
2470+ default :
2471+ }
2472+ }
2473+
2474+ // Simulate: user sent a code block (triple backticks) in Telegram.
2475+ b .handleGroupMessage (& TGMessage {
2476+ MessageID : 44 ,
2477+ From : & TGUser {ID : 456 , Username : "alice" },
2478+ Chat : TGChat {ID : - 200 , Type : "group" },
2479+ Date : time .Now ().Unix (),
2480+ Text : "@coder apply this:\n fmt.Println(\" hello\" )" ,
2481+ Entities : []MessageEntity {
2482+ {Type : "mention" , Offset : 0 , Length : 6 },
2483+ {Type : "pre" , Offset : 19 , Length : 20 },
2484+ },
2485+ })
2486+
2487+ select {
2488+ case <- done :
2489+ case <- time .After (2 * time .Second ):
2490+ t .Fatal ("timed out waiting for delivery" )
2491+ }
2492+
2493+ assert .Equal (t , "apply this:\n ```\n fmt.Println(\" hello\" )\n ```" , deliveredMsg .Msg )
2494+ }
2495+
2496+ func TestV2_HandleGroupMessage_PreBlockWithLanguage (t * testing.T ) {
2497+ tgSrv := newFakeTGServerV2 (t )
2498+ hub := newFakeHubClient ()
2499+ hub .agents ["proj-1" ] = []AgentInfo {{Slug : "coder" }}
2500+ b := newTestBrokerV2WithHub (t , tgSrv , hub )
2501+
2502+ ctx := context .Background ()
2503+ require .NoError (t , b .store .SaveUserMapping (ctx , & TelegramUserMapping {
2504+ TelegramUserID : "456" ,
2505+ ScionEmail : "alice@example.com" ,
2506+ LinkedAt : time .Now ().UTC (),
2507+ }))
2508+ require .NoError (t , b .store .SaveGroupLink (ctx , & GroupLink {
2509+ ChatID : - 200 ,
2510+ ProjectID : "proj-1" ,
2511+ ProjectSlug : "my-project" ,
2512+ DefaultAgent : "coder" ,
2513+ LinkedAt : time .Now ().UTC (),
2514+ Active : true ,
2515+ }))
2516+ require .NoError (t , b .store .SaveProjectAgents (ctx , & ProjectAgents {
2517+ ProjectID : "proj-1" ,
2518+ Agents : []AgentInfo {{Slug : "coder" }},
2519+ RefreshedAt : time .Now (),
2520+ }))
2521+
2522+ var deliveredMsg * messages.StructuredMessage
2523+ done := make (chan struct {}, 1 )
2524+ b .InboundHandler = func (_ string , msg * messages.StructuredMessage ) {
2525+ deliveredMsg = msg
2526+ select {
2527+ case done <- struct {}{}:
2528+ default :
2529+ }
2530+ }
2531+
2532+ // Simulate: user sent ```go\nfmt.Println("hello")\n``` in Telegram.
2533+ b .handleGroupMessage (& TGMessage {
2534+ MessageID : 45 ,
2535+ From : & TGUser {ID : 456 , Username : "alice" },
2536+ Chat : TGChat {ID : - 200 , Type : "group" },
2537+ Date : time .Now ().Unix (),
2538+ Text : "fmt.Println(\" hello\" )" ,
2539+ Entities : []MessageEntity {
2540+ {Type : "pre" , Offset : 0 , Length : 20 , Language : "go" },
2541+ },
2542+ })
2543+
2544+ select {
2545+ case <- done :
2546+ case <- time .After (2 * time .Second ):
2547+ t .Fatal ("timed out waiting for delivery" )
2548+ }
2549+
2550+ assert .Equal (t , "```go\n fmt.Println(\" hello\" )\n ```" , deliveredMsg .Msg )
2551+ }
2552+
2553+ func TestV2_HandleGroupMessage_CodeSpanWithDefaultAgent (t * testing.T ) {
2554+ tgSrv := newFakeTGServerV2 (t )
2555+ hub := newFakeHubClient ()
2556+ hub .agents ["proj-1" ] = []AgentInfo {{Slug : "coder" }}
2557+ b := newTestBrokerV2WithHub (t , tgSrv , hub )
2558+
2559+ ctx := context .Background ()
2560+ require .NoError (t , b .store .SaveUserMapping (ctx , & TelegramUserMapping {
2561+ TelegramUserID : "456" ,
2562+ ScionEmail : "alice@example.com" ,
2563+ LinkedAt : time .Now ().UTC (),
2564+ }))
2565+ require .NoError (t , b .store .SaveGroupLink (ctx , & GroupLink {
2566+ ChatID : - 200 ,
2567+ ProjectID : "proj-1" ,
2568+ ProjectSlug : "my-project" ,
2569+ DefaultAgent : "coder" ,
2570+ LinkedAt : time .Now ().UTC (),
2571+ Active : true ,
2572+ }))
2573+ require .NoError (t , b .store .SaveProjectAgents (ctx , & ProjectAgents {
2574+ ProjectID : "proj-1" ,
2575+ Agents : []AgentInfo {{Slug : "coder" }},
2576+ RefreshedAt : time .Now (),
2577+ }))
2578+
2579+ var deliveredMsg * messages.StructuredMessage
2580+ done := make (chan struct {}, 1 )
2581+ b .InboundHandler = func (_ string , msg * messages.StructuredMessage ) {
2582+ deliveredMsg = msg
2583+ select {
2584+ case done <- struct {}{}:
2585+ default :
2586+ }
2587+ }
2588+
2589+ // No mention — routes to default agent. Code span should be preserved.
2590+ b .handleGroupMessage (& TGMessage {
2591+ MessageID : 46 ,
2592+ From : & TGUser {ID : 456 , Username : "alice" },
2593+ Chat : TGChat {ID : - 200 , Type : "group" },
2594+ Date : time .Now ().Unix (),
2595+ Text : "check the config file" ,
2596+ Entities : []MessageEntity {
2597+ {Type : "code" , Offset : 10 , Length : 6 },
2598+ },
2599+ })
2600+
2601+ select {
2602+ case <- done :
2603+ case <- time .After (2 * time .Second ):
2604+ t .Fatal ("timed out waiting for delivery" )
2605+ }
2606+
2607+ assert .Equal (t , "check the `config` file" , deliveredMsg .Msg )
2608+ }
0 commit comments