File size: 4,356 Bytes
afa0cbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::collections::HashMap;
use std::io::Cursor;

use codex_app_server_protocol::ToolRequestUserInputAnswer;
use codex_app_server_protocol::ToolRequestUserInputOption;
use codex_app_server_protocol::ToolRequestUserInputParams;
use codex_app_server_protocol::ToolRequestUserInputQuestion;
use codex_app_server_protocol::ToolRequestUserInputResponse;
use pretty_assertions::assert_eq;

use super::prompt_for_answers_with;

#[test]
fn collects_option_and_free_form_answers() {
    let params = ToolRequestUserInputParams {
        thread_id: "thread-1".to_string(),
        turn_id: "turn-1".to_string(),
        item_id: "item-1".to_string(),
        questions: vec![
            ToolRequestUserInputQuestion {
                id: "target".to_string(),
                header: "Target".to_string(),
                question: "Which target?".to_string(),
                is_other: true,
                is_secret: false,
                options: Some(vec![
                    ToolRequestUserInputOption {
                        label: "Core".to_string(),
                        description: "Inspect core".to_string(),
                    },
                    ToolRequestUserInputOption {
                        label: "TUI".to_string(),
                        description: "Inspect TUI".to_string(),
                    },
                ]),
            },
            ToolRequestUserInputQuestion {
                id: "details".to_string(),
                header: "Details".to_string(),
                question: "Anything else?".to_string(),
                is_other: true,
                is_secret: false,
                options: None,
            },
        ],
        is_blocking: false,
        auto_resolution_ms: Some(60_000),
    };
    let mut input = Cursor::new(b"2\ninclude snapshots\n");
    let mut output = Vec::new();

    let response = prompt_for_answers_with(&mut input, &mut output, &params).unwrap();

    assert_eq!(
        response,
        ToolRequestUserInputResponse {
            answers: HashMap::from([
                (
                    "target".to_string(),
                    ToolRequestUserInputAnswer {
                        answers: vec!["TUI".to_string()],
                    },
                ),
                (
                    "details".to_string(),
                    ToolRequestUserInputAnswer {
                        answers: vec!["user_note: include snapshots".to_string()],
                    },
                ),
            ]),
        }
    );
    assert_eq!(
        String::from_utf8(output).unwrap(),
        concat!(
            "\n[request_user_input for thread thread-1, turn turn-1]\n",
            "This request is non-blocking.\n",
            "\nTarget: Which target?\n",
            "  1. Core - Inspect core\n",
            "  2. TUI - Inspect TUI\n",
            "  o. Other (free-form)\n",
            "Choose 1-2 or o: ",
            "\nDetails: Anything else?\n",
            "Answer: ",
        )
    );
}

#[test]
fn retries_invalid_selection_and_collects_other_answer() {
    let params = ToolRequestUserInputParams {
        thread_id: "thread-1".to_string(),
        turn_id: "turn-1".to_string(),
        item_id: "item-1".to_string(),
        questions: vec![ToolRequestUserInputQuestion {
            id: "target".to_string(),
            header: "Target".to_string(),
            question: "Which target?".to_string(),
            is_other: true,
            is_secret: false,
            options: Some(vec![ToolRequestUserInputOption {
                label: "Core".to_string(),
                description: "Inspect core".to_string(),
            }]),
        }],
        is_blocking: true,
        auto_resolution_ms: None,
    };
    let mut input = Cursor::new(b"9\no\nSDK wrapper\n");
    let mut output = Vec::new();

    let response = prompt_for_answers_with(&mut input, &mut output, &params).unwrap();

    assert_eq!(
        response,
        ToolRequestUserInputResponse {
            answers: HashMap::from([(
                "target".to_string(),
                ToolRequestUserInputAnswer {
                    answers: vec!["user_note: SDK wrapper".to_string()],
                },
            )]),
        }
    );
    let output = String::from_utf8(output).unwrap();
    assert!(output.contains("Invalid selection; try again."));
}