use std::collections::BTreeMap;
use serde_json::json;
use subako_function_sdk::ToolError;
use subako_function_sdk::http::{
Answer, HttpError, HttpErrorKind, Request, RequestBody, Response, ResponseBody, ResponseOutput,
request,
};
#[test]
fn a_request_needs_only_a_url() {
let parsed: Request = serde_json::from_value(json!({"url": "https://a.example/"})).unwrap();
assert_eq!(parsed, Request::get("https://a.example/"));
assert!(parsed.follow_redirects);
assert_eq!(parsed.output, ResponseOutput::Inline);
assert_eq!(
serde_json::to_value(&parsed).unwrap(),
json!({
"method": "GET",
"url": "https://a.example/",
"headers": {},
"output": {"type": "inline"},
"follow_redirects": true,
})
);
}
#[test]
fn every_field_has_its_tagged_wire_shape() {
let full = json!({
"method": "POST",
"url": "https://a.example/upload",
"headers": {"x-a": "1"},
"body": {"type": "file", "path": "/volume/tmp/in"},
"output": {"type": "file", "path": "/volume/tmp/out"},
"follow_redirects": false,
"timeout_ms": 5000,
});
let parsed: Request = serde_json::from_value(full.clone()).unwrap();
assert_eq!(
parsed.body,
Some(RequestBody::File {
path: "/volume/tmp/in".to_string()
})
);
assert_eq!(serde_json::to_value(&parsed).unwrap(), full);
for body in [
json!({"type": "text", "text": "hi"}),
json!({"type": "json", "json": {"a": [1]}}),
] {
let parsed: RequestBody = serde_json::from_value(body.clone()).unwrap();
assert_eq!(serde_json::to_value(&parsed).unwrap(), body);
}
}
#[test]
fn an_unknown_field_is_refused() {
for bad in [
json!({"url": "https://a.example/", "follow": true}),
json!({"url": "https://a.example/", "body": {"type": "text", "text": "", "x": 1}}),
] {
assert!(serde_json::from_value::<Request>(bad).is_err());
}
}
#[test]
fn an_answer_is_ok_or_error() {
let response = Response {
status: 201,
headers: BTreeMap::from([("a".to_string(), "b".to_string())]),
body: ResponseBody::File {
path: "/volume/tmp/out".to_string(),
bytes: 3,
},
};
let ok = json!({"ok": {
"status": 201,
"headers": {"a": "b"},
"body": {"type": "file", "path": "/volume/tmp/out", "bytes": 3},
}});
let answer: Answer = serde_json::from_value(ok).unwrap();
assert_eq!(Result::from(answer), Ok(response));
let error = HttpError {
kind: HttpErrorKind::HostNotAllowed,
message: "no".to_string(),
};
let answer: Answer =
serde_json::from_value(json!({"error": {"kind": "host_not_allowed", "message": "no"}}))
.unwrap();
assert_eq!(Result::from(answer), Err::<Response, _>(error.clone()));
assert_eq!(error.to_string(), "no");
assert_eq!(ToolError::from(error).message(), "no");
}
#[test]
fn outside_the_worker_a_request_fails_without_trapping() {
let error = request(&Request::get("https://a.example/")).unwrap_err();
assert_eq!(error.kind, HttpErrorKind::Abi);
assert_eq!(
error.message,
"subako_http exists only inside the function worker"
);
}
#[test]
fn a_method_is_spelled_as_its_display() {
use subako_function_sdk::http::Method;
for method in [
Method::Get,
Method::Head,
Method::Post,
Method::Put,
Method::Patch,
Method::Delete,
Method::Options,
] {
assert_eq!(
serde_json::to_value(method).unwrap(),
json!(method.to_string())
);
assert_eq!(
serde_json::from_value::<Method>(json!(method.as_str())).unwrap(),
method
);
}
for refused in ["TRACE", "CONNECT", "get"] {
assert!(serde_json::from_value::<Method>(json!(refused)).is_err());
}
}