| 989 |
989
|
|
);
|
| 990 |
990
|
|
}
|
| 991 |
991
|
|
|
|
992
|
+ |
// -------------------------------------------- `--headless` with no prompt
|
|
993
|
+ |
|
|
994
|
+ |
/// A headless run with no prompt refuses, and opens no thread.
|
|
995
|
+ |
///
|
|
996
|
+ |
/// It used to substitute the literal `Analyze workspace and run tests`, open a
|
|
997
|
+ |
/// thread, and spend the grant on an instruction nobody gave — one screen
|
|
998
|
+ |
/// below where `--offline` refuses the identical omission by name. The exit
|
|
999
|
+ |
/// code alone does not catch that: an invented turn can fail afterwards and
|
|
1000
|
+ |
/// exit non-zero too. What catches it is the server, which is never asked to
|
|
1001
|
+ |
/// open anything.
|
|
1002
|
+ |
#[test]
|
|
1003
|
+ |
fn headless_without_a_prompt_refuses_and_opens_no_thread() {
|
|
1004
|
+ |
let server = RouteServer::start(coder_routes);
|
|
1005
|
+ |
let origin = server.origin();
|
|
1006
|
+ |
|
|
1007
|
+ |
let base = origin.as_str();
|
|
1008
|
+ |
for bare in [
|
|
1009
|
+ |
vec!["--api-url", base, "coder", "--headless"],
|
|
1010
|
+ |
// Whitespace is not a prompt either; it is the same omission with a
|
|
1011
|
+ |
// space in it.
|
|
1012
|
+ |
vec!["--api-url", base, "coder", "--headless", " "],
|
|
1013
|
+ |
] {
|
|
1014
|
+ |
let run = oa_env(&bare, &[("OPENAGENTS_TOKEN", "t")]);
|
|
1015
|
+ |
// Asserted first, because it is the assertion that carries the test:
|
|
1016
|
+ |
// an invented prompt that opens a thread and then fails would satisfy
|
|
1017
|
+ |
// an exit code and nothing else.
|
|
1018
|
+ |
let paths: Vec<String> = server.hits().into_iter().map(|hit| hit.path).collect();
|
|
1019
|
+ |
assert!(
|
|
1020
|
+ |
!paths.iter().any(|p| p == "/api/v1/threads"),
|
|
1021
|
+ |
"{bare:?} still opened a thread: {paths:?}"
|
|
1022
|
+ |
);
|
|
1023
|
+ |
assert!(
|
|
1024
|
+ |
!run.stdout.contains("Analyze workspace"),
|
|
1025
|
+ |
"a prompt nobody gave was run anyway: {}",
|
|
1026
|
+ |
run.stdout
|
|
1027
|
+ |
);
|
|
1028
|
+ |
assert_eq!(run.status, Some(2), "{bare:?} stdout: {}", run.stdout);
|
|
1029
|
+ |
assert!(
|
|
1030
|
+ |
run.stderr.contains("--headless") && run.stderr.contains("<prompt>"),
|
|
1031
|
+ |
"the refusal did not say what is missing or how to give it: {}",
|
|
1032
|
+ |
run.stderr
|
|
1033
|
+ |
);
|
|
1034
|
+ |
}
|
|
1035
|
+ |
|
|
1036
|
+ |
// The control, on the same fixture: with a prompt it does open one. Without
|
|
1037
|
+ |
// this the assertion above would also pass against a binary that could not
|
|
1038
|
+ |
// reach the server at all.
|
|
1039
|
+ |
let given = oa_env(
|
|
1040
|
+ |
&["--api-url", &origin, "coder", "--headless", "hello"],
|
|
1041
|
+ |
&[("OPENAGENTS_TOKEN", "t")],
|
|
1042
|
+ |
);
|
|
1043
|
+ |
assert_eq!(given.status, Some(0), "stderr: {}", given.stderr);
|
|
1044
|
+ |
let paths: Vec<String> = server.hits().into_iter().map(|hit| hit.path).collect();
|
|
1045
|
+ |
assert!(
|
|
1046
|
+ |
paths.iter().any(|p| p == "/api/v1/threads"),
|
|
1047
|
+ |
"the fixture never opens a thread, so the test proves nothing: {paths:?}"
|
|
1048
|
+ |
);
|
|
1049
|
+ |
}
|
|
1050
|
+ |
|
|
1051
|
+ |
/// The same omission, refused the same way on both coder paths.
|
|
1052
|
+ |
///
|
|
1053
|
+ |
/// This is the defect stated as a property: `--offline` and `--headless` were
|
|
1054
|
+ |
/// two arms of one function handling one missing input two opposite ways, and
|
|
1055
|
+ |
/// the reader who forgot the prompt got a refusal or an invented instruction
|
|
1056
|
+ |
/// depending on which arm they were in.
|
|
1057
|
+ |
#[test]
|
|
1058
|
+ |
fn a_missing_prompt_is_refused_the_same_way_offline_and_headless() {
|
|
1059
|
+ |
let dead = "http://127.0.0.1:1";
|
|
1060
|
+ |
let offline = oa_env(
|
|
1061
|
+ |
&["--api-url", dead, "coder", "--offline"],
|
|
1062
|
+ |
&[("OPENAGENTS_TOKEN", "t")],
|
|
1063
|
+ |
);
|
|
1064
|
+ |
let headless = oa_env(
|
|
1065
|
+ |
&["--api-url", dead, "coder", "--headless"],
|
|
1066
|
+ |
&[("OPENAGENTS_TOKEN", "t")],
|
|
1067
|
+ |
);
|
|
1068
|
+ |
assert_eq!(offline.status, Some(2), "stdout: {}", offline.stdout);
|
|
1069
|
+ |
assert_eq!(
|
|
1070
|
+ |
headless.status,
|
|
1071
|
+ |
Some(2),
|
|
1072
|
+ |
"the headless path accepted a missing prompt: {} {}",
|
|
1073
|
+ |
headless.stdout,
|
|
1074
|
+ |
headless.stderr
|
|
1075
|
+ |
);
|
|
1076
|
+ |
for run in [&offline, &headless] {
|
|
1077
|
+ |
assert!(
|
|
1078
|
+ |
run.stderr.contains("<prompt>"),
|
|
1079
|
+ |
"the refusal did not show the form that works: {}",
|
|
1080
|
+ |
run.stderr
|
|
1081
|
+ |
);
|
|
1082
|
+ |
}
|
|
1083
|
+ |
}
|
|
1084
|
+ |
|
| 992 |
1085
|
|
// ----------------------------------------------------------------- `--model`
|
| 993 |
1086
|
|
|
| 994 |
1087
|
|
/// `--model` decides the id sent at thread open; without it the default lane's
|