Refine Telegram thread commands
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"codex-telegram-bot/internal/codexapp"
|
||||
"codex-telegram-bot/internal/store"
|
||||
)
|
||||
|
||||
@@ -197,16 +198,18 @@ func TestEditReplyMarkupClearsInlineKeyboard(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBotCommandsUseSingleThreadCommand(t *testing.T) {
|
||||
func TestBotCommandsExposeCurrentPromptList(t *testing.T) {
|
||||
commands := botCommands()
|
||||
seen := map[string]bool{}
|
||||
for _, command := range commands {
|
||||
seen[command.Command] = true
|
||||
}
|
||||
if !seen["thread"] {
|
||||
t.Fatal("bot command list should include /thread")
|
||||
for _, command := range []string{"start", "new", "resume", "rename", "fork", "archive", "unarchive", "delete", "status", "cancel", "workspace", "model", "sandbox", "pic"} {
|
||||
if !seen[command] {
|
||||
t.Fatalf("bot command list should include /%s", command)
|
||||
}
|
||||
}
|
||||
for _, removed := range []string{"threads", "resume"} {
|
||||
for _, removed := range []string{"help", "thread", "threads", "workspaces", "diff"} {
|
||||
if seen[removed] {
|
||||
t.Fatalf("bot command list should not include /%s", removed)
|
||||
}
|
||||
@@ -214,8 +217,8 @@ func TestBotCommandsUseSingleThreadCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseCommand(t *testing.T) {
|
||||
name, args, ok := parseCommand("/thread@my_bot 123")
|
||||
if !ok || name != "thread" || len(args) != 1 || args[0] != "123" {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -394,9 +397,9 @@ func TestRenderDynamicToolDetailsSelectsUsefulArguments(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRenderApprovalDetailsAvoidsRawJSONDump(t *testing.T) {
|
||||
raw := json.RawMessage(`{"command":"go test ./...","cwd":"/workspace/project","unused":{"nested":true}}`)
|
||||
raw := json.RawMessage(`{"command":"go test ./...","cwd":"/workspace/project","environmentId":"env_123","unused":{"nested":true}}`)
|
||||
text := renderApprovalHTML("item/commandExecution/requestApproval", raw, "")
|
||||
for _, want := range []string{"Codex requests command approval", "language-bash", "go test ./...", "CWD"} {
|
||||
for _, want := range []string{"Codex requests command approval", "language-bash", "go test ./...", "CWD", "Environment ID", "env_123"} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("approval render missing %q in %q", want, text)
|
||||
}
|
||||
@@ -534,10 +537,39 @@ func TestResumeCallbackData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestThreadActionCallbackData(t *testing.T) {
|
||||
action, threadID, ok := ParseThreadActionCallbackData(ThreadActionCallbackData(threadActionDelete, 123))
|
||||
if !ok || action != threadActionDelete || threadID != 123 {
|
||||
t.Fatalf("unexpected thread action callback: action=%q id=%d ok=%v", action, threadID, ok)
|
||||
}
|
||||
action, page, ok := ParseThreadActionPageCallbackData(ThreadActionPageCallbackData(threadActionUnarchive, 2))
|
||||
if !ok || action != threadActionUnarchive || page != 2 {
|
||||
t.Fatalf("unexpected thread action page callback: action=%q page=%d ok=%v", action, page, ok)
|
||||
}
|
||||
if _, _, ok := ParseThreadActionCallbackData("thread:unknown:123"); ok {
|
||||
t.Fatal("unknown thread action should not parse")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsMissingCodexThreadError(t *testing.T) {
|
||||
for _, message := range []string{
|
||||
"no rollout found for thread id 019ef2ea",
|
||||
"thread not loaded: 019ef2ea",
|
||||
} {
|
||||
err := codexapp.RPCError{Code: -32600, Message: message}
|
||||
if !isMissingCodexThreadError(err) {
|
||||
t.Fatalf("expected stale thread error for %q", message)
|
||||
}
|
||||
}
|
||||
if isMissingCodexThreadError(codexapp.RPCError{Code: -32600, Message: "permission denied"}) {
|
||||
t.Fatal("unrelated -32600 error should not be treated as stale thread")
|
||||
}
|
||||
}
|
||||
|
||||
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"} {
|
||||
for _, want := range []string{"Thread ID 42: do xyz", "Thread ID 43: executed xxx command", "/resume THREAD_ID"} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("resume list missing %q in %q", want, text)
|
||||
}
|
||||
@@ -554,6 +586,36 @@ func TestResumeThreadListText(t *testing.T) {
|
||||
if !ok || secondID != 43 {
|
||||
t.Fatalf("second resume button targets id=%d ok=%v", secondID, ok)
|
||||
}
|
||||
|
||||
deleteText := threadActionListText(threads, 0, threadActionDelete)
|
||||
if !strings.Contains(deleteText, "Choose a thread to delete") || !strings.Contains(deleteText, "/delete THREAD_ID") {
|
||||
t.Fatalf("delete list text missing action copy: %q", deleteText)
|
||||
}
|
||||
deleteMarkup := threadActionMarkup(threads, 0, true, threadActionDelete)
|
||||
if deleteMarkup.InlineKeyboard[0][0].Text != "Delete 42" {
|
||||
t.Fatalf("unexpected delete button label: %#v", deleteMarkup.InlineKeyboard)
|
||||
}
|
||||
action, deleteID, ok := ParseThreadActionCallbackData(deleteMarkup.InlineKeyboard[0][0].CallbackData)
|
||||
if !ok || action != threadActionDelete || deleteID != 42 {
|
||||
t.Fatalf("delete button targets action=%q id=%d ok=%v", action, deleteID, ok)
|
||||
}
|
||||
action, page, ok := ParseThreadActionPageCallbackData(deleteMarkup.InlineKeyboard[1][0].CallbackData)
|
||||
if !ok || action != threadActionDelete || page != 1 {
|
||||
t.Fatalf("delete next button targets action=%q page=%d ok=%v", action, page, ok)
|
||||
}
|
||||
|
||||
unarchiveText := threadActionListText(threads, 0, threadActionUnarchive)
|
||||
if !strings.Contains(unarchiveText, "Choose an archived thread to restore") || !strings.Contains(unarchiveText, "/unarchive THREAD_ID") {
|
||||
t.Fatalf("unarchive list text missing action copy: %q", unarchiveText)
|
||||
}
|
||||
unarchiveMarkup := threadActionMarkup(threads, 0, false, threadActionUnarchive)
|
||||
if unarchiveMarkup.InlineKeyboard[0][0].Text != "Restore 42" {
|
||||
t.Fatalf("unexpected unarchive button label: %#v", unarchiveMarkup.InlineKeyboard)
|
||||
}
|
||||
action, unarchiveID, ok := ParseThreadActionCallbackData(unarchiveMarkup.InlineKeyboard[0][0].CallbackData)
|
||||
if !ok || action != threadActionUnarchive || unarchiveID != 42 {
|
||||
t.Fatalf("unarchive button targets action=%q id=%d ok=%v", action, unarchiveID, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelEffortAndSandboxCallbackData(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user