use std::error::Error;
use std::io::Read;
use std::num::NonZeroU32;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::Instant;
use haem_frames::embedding::read_message;
use haematite_query::worker_owner::{Control, OwnedChild};
use super::Handshake;
use crate::serve::embed::{Options, Settings};
use crate::serve::query::Setting;
type TestResult = Result<(), Box<dyn Error>>;
fn settings() -> Result<Settings, Box<dyn Error>> {
Options {
worker: Setting::Present(PathBuf::from("/bin/sh")),
model_dir: Setting::Present(PathBuf::from("/")),
model_sha256: Setting::Present("1".repeat(64)),
memory_bytes: Setting::Present(1 << 30),
threads: Setting::Present(1),
batch_items: Setting::Present(4),
input_bytes: Setting::Present(16_384),
tokens: Setting::Present(512),
frame_bytes: Setting::Present(1 << 20),
ready_ms: Setting::Present(2_000),
call_ms: Setting::Present(30_000),
waiting: Setting::Present(4),
request_items: Setting::Present(64),
page_records: Setting::Present(64),
writer_wait_ms: Setting::Absent,
windows: Setting::Absent,
max_windows: Setting::Absent,
overlap_tokens: Setting::Absent,
}
.resolve()?
.ok_or_else(|| "no embedding settings".into())
}
fn started() -> Result<Handshake, Box<dyn Error>> {
let mut child = OwnedChild(
Command::new("/bin/sh")
.args([
"-c",
"printf 'loading the model: out of memory\\n' >&2; printf 'started\\n'; \
while IFS= read -r request; do :; done",
])
.env_clear()
.current_dir("/")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?,
);
let input = child.0.stdin.take().ok_or("no stand-in input")?;
let mut output = child.0.stdout.take().ok_or("no stand-in output")?;
let stderr = child.0.stderr.take().ok_or("no stand-in diagnostics")?;
let mut signal = [0_u8; 8];
output.read_exact(&mut signal)?;
assert_eq!(
&signal, b"started\n",
"the stand-in must signal after writing its diagnostic and keep its output open"
);
let control = Control::start(child, stderr, Instant::now())?;
Ok(Handshake {
input,
output,
control,
frame: NonZeroU32::new(1 << 20).ok_or("no frame bound")?,
})
}
#[test]
fn a_start_timeout_says_the_status_and_what_the_worker_wrote() -> TestResult {
let settings = settings()?;
let mut handshake = started()?;
let refused = handshake.before_ready(&settings, None);
let answer = read_message(&mut handshake.output, handshake.frame)?;
assert!(
answer.is_none(),
"the owner must close the stand-in's output without a Ready"
);
assert_eq!(
refused.code, "embed_worker_start_timeout",
"{}",
refused.message
);
assert!(
refused.message.contains("status signal: 9"),
"the owner must kill and reap the held stand-in: {}",
refused.message
);
assert!(
refused
.message
.contains("stderr: loading the model: out of memory"),
"the timeout must retain the diagnostic written before the started signal: {}",
refused.message
);
assert!(
handshake.control.finish(false).is_err(),
"Ready refusal must have joined the owner already"
);
Ok(())
}