use std::sync::Arc;
use std::sync::Mutex;
use pointlock_ir::VerdictStatus;
use pointlock_vision::{OpenAiCompatVisionVerifier, VisionJudge, VisionRequest, VisionVerifier};
struct SeenRequest {
authorization: Option<String>,
url: String,
body: String,
}
struct CannedServer {
server: Arc<tiny_http::Server>,
seen: Arc<Mutex<Vec<SeenRequest>>>,
url: String,
}
impl CannedServer {
fn start(status: u16, body: impl Into<String>) -> CannedServer {
let body: String = body.into();
let server = Arc::new(tiny_http::Server::http("127.0.0.1:0").expect("bind canned server"));
let url = format!(
"http://127.0.0.1:{}/v1",
server.server_addr().to_ip().expect("ip").port()
);
let seen = Arc::new(Mutex::new(Vec::new()));
{
let server = Arc::clone(&server);
let seen = Arc::clone(&seen);
std::thread::spawn(move || {
while let Ok(mut request) = server.recv() {
let authorization = request
.headers()
.iter()
.find(|header| header.field.equiv("Authorization"))
.map(|header| header.value.as_str().to_owned());
let url = request.url().to_owned();
let mut raw = String::new();
let _ = request.as_reader().read_to_string(&mut raw);
seen.lock().expect("seen").push(SeenRequest {
authorization,
url,
body: raw,
});
let response = tiny_http::Response::from_string(body.clone())
.with_status_code(tiny_http::StatusCode(status));
let _ = request.respond(response);
}
});
}
CannedServer { server, seen, url }
}
fn verifier(&self, api_key: Option<&str>) -> OpenAiCompatVisionVerifier {
OpenAiCompatVisionVerifier::new(api_key.map(str::to_owned), "qwen2.5-vl-32b", &self.url)
}
}
impl Drop for CannedServer {
fn drop(&mut self) {
self.server.unblock();
}
}
fn request<'a>() -> VisionRequest<'a> {
VisionRequest {
prompt: "the SSID field visibly contains the requested name",
region: None,
screenshot: b"fake-png-bytes",
media_type: "image/png",
}
}
fn completions_body(content: serde_json::Value) -> String {
serde_json::json!({
"id": "chatcmpl-test",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": content },
"finish_reason": "stop",
}],
})
.to_string()
}
#[tokio::test]
async fn parses_the_verdict_with_judge_identity_and_observations() {
let server = CannedServer::start(
200,
completions_body(serde_json::json!(
"OBSERVED: the SSID field shows HomeWifi\n\
PASS: the field shows the requested name"
)),
);
let verdict = server.verifier(None).verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Pass);
assert!(verdict.reason.starts_with("vision: "), "{}", verdict.reason);
assert_eq!(
verdict.judge,
Some(VisionJudge {
provider: "openai-compat".to_owned(),
model: Some("qwen2.5-vl-32b".to_owned()),
})
);
assert_eq!(
verdict.observations,
vec!["the SSID field shows HomeWifi".to_owned()]
);
}
#[tokio::test]
async fn array_content_parts_are_concatenated() {
let server = CannedServer::start(
200,
completions_body(serde_json::json!([
{ "type": "text", "text": "FAIL: the field " },
{ "type": "text", "text": "is empty" },
])),
);
let verdict = server.verifier(None).verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Fail);
assert_eq!(verdict.reason, "vision: the field is empty");
}
#[tokio::test]
async fn oversized_response_bodies_fold_to_unknown() {
let padded = format!("PASS: ok{}", " ".repeat(1024 * 1024 + 64));
let server = CannedServer::start(200, completions_body(serde_json::json!(padded)));
let verdict = server.verifier(None).verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Unknown);
assert!(verdict.reason.contains("exceeds"), "{}", verdict.reason);
}
#[tokio::test]
async fn chatty_answers_fold_to_unknown() {
let server = CannedServer::start(
200,
completions_body(serde_json::json!(
"Sure! Looking at the screenshot, I believe it passes."
)),
);
let verdict = server.verifier(None).verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Unknown);
assert!(verdict.reason.contains("unparseable"), "{}", verdict.reason);
}
#[tokio::test]
async fn non_success_status_folds_to_unknown() {
let server = CannedServer::start(429, r#"{"error":{"type":"rate_limit"}}"#);
let verdict = server.verifier(None).verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Unknown);
assert!(verdict.reason.contains("429"), "{}", verdict.reason);
}
#[tokio::test]
async fn transport_failure_folds_to_unknown() {
let verifier = OpenAiCompatVisionVerifier::new(None, "m", "http://127.0.0.1:9/v1");
let verdict = verifier.verify(request()).await;
assert_eq!(verdict.status, VerdictStatus::Unknown);
assert!(verdict.reason.contains("transport"), "{}", verdict.reason);
}
#[tokio::test]
async fn the_request_carries_data_url_model_instruction_and_bearer_auth() {
let server = CannedServer::start(200, completions_body(serde_json::json!("PASS: ok")));
let _ = server.verifier(Some("test-key")).verify(request()).await;
let seen = server.seen.lock().expect("seen");
assert_eq!(seen[0].authorization.as_deref(), Some("Bearer test-key"));
assert_eq!(seen[0].url, "/v1/chat/completions");
let sent: serde_json::Value = serde_json::from_str(&seen[0].body).expect("request JSON");
assert_eq!(sent["model"], "qwen2.5-vl-32b");
let content = sent["messages"][0]["content"].as_array().expect("content");
assert_eq!(content[0]["type"], "image_url");
let url = content[0]["image_url"]["url"].as_str().expect("data url");
assert!(url.starts_with("data:image/png;base64,"), "{url}");
let text = content[1]["text"].as_str().expect("instruction");
assert!(text.contains("the SSID field visibly contains"), "{text}");
assert!(text.contains("OBSERVED:"), "{text}");
assert!(text.contains("Answer UNKNOWN unless"), "{text}");
}
#[tokio::test]
async fn keyless_requests_send_no_authorization_header() {
let server = CannedServer::start(200, completions_body(serde_json::json!("PASS: ok")));
let _ = server.verifier(None).verify(request()).await;
let seen = server.seen.lock().expect("seen");
assert_eq!(seen[0].authorization, None);
}