Files
codex-telegram-bot/internal/telegram/render_test.go
2026-05-21 08:40:16 +00:00

153 lines
5.0 KiB
Go

package telegram
import (
"strings"
"testing"
"codex-telegram-bot/internal/store"
)
func TestEscapeHTML(t *testing.T) {
got := EscapeHTML(`<run & "test">`)
want := "&lt;run &amp; &#34;test&#34;&gt;"
if got != want {
t.Fatalf("EscapeHTML() = %q, want %q", got, want)
}
}
func TestChunkText(t *testing.T) {
text := strings.Repeat("a", 25)
chunks := ChunkText(text, 10)
if len(chunks) != 3 {
t.Fatalf("got %d chunks", len(chunks))
}
for _, chunk := range chunks {
if len([]rune(chunk)) > 10 {
t.Fatalf("chunk too long: %q", chunk)
}
}
}
func TestApprovalCallbackData(t *testing.T) {
data := ApprovalCallbackData(12, "accept")
id, decision, ok := ParseApprovalCallbackData(data)
if !ok || id != 12 || decision != "accept" {
t.Fatalf("unexpected callback parse: id=%d decision=%s ok=%v", id, decision, ok)
}
if _, _, ok := ParseApprovalCallbackData("approval:12:unknown"); ok {
t.Fatal("unknown decisions should be rejected")
}
}
func TestApprovalResponseForPermissions(t *testing.T) {
approval := store.PendingApproval{
Kind: "item/permissions/requestApproval",
PayloadJSON: `{"permissions":{"network":{"enabled":true}}}`,
}
response, ok := approvalResponse(approval, "accept").(map[string]any)
if !ok {
t.Fatal("approval response should be a map")
}
if response["scope"] != "turn" {
t.Fatalf("scope = %v, want turn", response["scope"])
}
permissions, ok := response["permissions"].(map[string]any)
if !ok {
t.Fatal("permissions should be a map")
}
network, ok := permissions["network"].(map[string]any)
if !ok || network["enabled"] != true {
t.Fatalf("unexpected permissions: %#v", permissions)
}
denied, ok := approvalResponse(approval, "decline").(map[string]any)
if !ok {
t.Fatal("denied response should be a map")
}
deniedPermissions, ok := denied["permissions"].(map[string]any)
if !ok || len(deniedPermissions) != 0 {
t.Fatalf("denied permissions = %#v, want empty map", denied["permissions"])
}
}
func TestParseCommand(t *testing.T) {
name, args, ok := parseCommand("/resume@my_bot 123")
if !ok || name != "resume" || len(args) != 1 || args[0] != "123" {
t.Fatalf("unexpected command parse: %q %#v %v", name, args, ok)
}
}
func TestRenderCodexCommandExecutionItem(t *testing.T) {
output := "line 1\nline 2"
exitCode := 0
item := codexThreadItemView{
Type: "commandExecution",
Command: "go test ./...",
AggregatedOutput: &output,
ExitCode: &exitCode,
}
text := renderCodexItemCompleted(item)
for _, want := range []string{"Tool call: command finished", "Command: go test ./...", "Exit code: 0", "line 1"} {
if !strings.Contains(text, want) {
t.Fatalf("rendered command item missing %q in %q", want, text)
}
}
}
func TestRenderCodexStartedItems(t *testing.T) {
text := renderCodexItemStarted(codexThreadItemView{Type: "webSearch", Query: "telegram bot api"})
if !strings.Contains(text, "web search started") || !strings.Contains(text, "telegram bot api") {
t.Fatalf("unexpected web search render: %q", text)
}
}
func TestResumeCallbackData(t *testing.T) {
threadID, ok := ParseResumeThreadCallbackData(ResumeThreadCallbackData(123))
if !ok || threadID != 123 {
t.Fatalf("unexpected resume thread callback: id=%d ok=%v", threadID, ok)
}
page, ok := ParseResumePageCallbackData(ResumePageCallbackData(2))
if !ok || page != 2 {
t.Fatalf("unexpected resume page callback: page=%d ok=%v", page, ok)
}
}
func TestResumeThreadListText(t *testing.T) {
threads := []store.Thread{{ID: 42, Title: "do xyz"}, {ID: 43, Title: "executed xxx command"}}
text := resumeThreadListText(threads, 0)
for _, want := range []string{"Thread ID 42: do xyz", "Thread ID 43: executed xxx command"} {
if !strings.Contains(text, want) {
t.Fatalf("resume list missing %q in %q", want, text)
}
}
markup := resumeThreadMarkup(threads, 0, true)
if len(markup.InlineKeyboard) != 2 || markup.InlineKeyboard[0][0].Text != "ID 42" || markup.InlineKeyboard[0][1].Text != "ID 43" {
t.Fatalf("unexpected resume buttons: %#v", markup.InlineKeyboard)
}
firstID, ok := ParseResumeThreadCallbackData(markup.InlineKeyboard[0][0].CallbackData)
if !ok || firstID != 42 {
t.Fatalf("first resume button targets id=%d ok=%v", firstID, ok)
}
secondID, ok := ParseResumeThreadCallbackData(markup.InlineKeyboard[0][1].CallbackData)
if !ok || secondID != 43 {
t.Fatalf("second resume button targets id=%d ok=%v", secondID, ok)
}
}
func TestModelAndEffortCallbackData(t *testing.T) {
modelID := strings.Join([]string{"server", "model", "id"}, "-")
data, ok := ModelCallbackData(modelID)
if !ok {
t.Fatal("model callback should fit")
}
model, ok := ParseModelCallbackData(data)
if !ok || model != modelID {
t.Fatalf("unexpected model callback parse: model=%q ok=%v", model, ok)
}
effortName := strings.Join([]string{"server", "effort"}, "-")
effort, ok := ParseEffortCallbackData(EffortCallbackData(effortName))
if !ok || effort != effortName {
t.Fatalf("unexpected effort callback parse: effort=%q ok=%v", effort, ok)
}
}