Today I'm going to implement HTTP request & response mocks for testing purposes. But first, some minor config tweaks...
This architecture allows me to impl Top for any HTTP implementation, and also allows me to use a custom mock for running the test suite, or for iocaine runtime decide.
One complicating factor is that different methods may return different types. Most of them return a String currently, but for example .header() would return an Option<String>.
This means that functions like Dom::method() and Dom::path() are essentially the same, and differ only in the type they tx.send(), and differ from .header() in the type they return. I wonder if I can use some generics trickery to remove the boilerplate.
Hah! I CAN!
impl Dom {
fn communicate<T, F>(&self, req: F) -> T
where
F: FnOnce(oneshot::Sender<T>) -> DomRequest,
{
let (resp_tx, resp_rx) = oneshot::channel();
let tx = self.channel.clone();
tx.send(req(resp_tx)).ok().unwrap();
tokio::task::block_in_place(move || resp_rx.blocking_recv().unwrap())
}
pub fn method(&self) -> String {
self.communicate(|responder| DomRequest::Method { responder })
}
}
Sweet.
Yes, this will panic when communicating on the channel fails. I think that's more suitable behaviour than trying to handle such a catastrophic situation on the scripting side.
And yes, I will change the .unwrap() to .expect() with a reasonable message.
Neat, neat, I can run a test suite with mock HTTP requests. No cookies or query strings yet, but those will be coming soon, too.
❯ cargo run -q -- runtime decide -m HEAD -p /foo -H host=example.com
DEBUG: HEAD /foo example.com
default
Whee!
Soon, soon, I shall start re-implementing Nam-Shub of Enki. Well, not that soon, because I need matchers and configuration support first. Or maybe I can get away without both?
I mean, to implement the Three Ifs In A Trenchcoat... I don't necessarily need any of that.
Hrmpf.
running 1 test
fatal runtime error: failed to initiate panic, error 5, aborting
error: test failed, to rerun pass `-p iocaine-powder --lib`
Caused by:
process didn't exit successfully: `.../target/debug/deps/iocaine_powder-b876a7fff1eec33a` (signal: 6, SIGABRT: process abort signal)
That's not very encouraging.
Ah, just needed to #[tokio::test(flavor = "multi_threaded")], and now it's fine.
I'll change decide()'s signature to fn decide(request: Request) -> Result[Decision, Report], where Decision on the Rust side is:
pub enum SomeName {
Passthrough(String),
Garbage(String),
}
I "just" have to find a good Rust-side unhinged name for SomeName, Passthrough, and Garbage. I'm currently thinking:
pub struct Politics {
Anarchism(String),
Populist(String),
}
Where Politics::Populist would be Garbage, of course.
The reason I want a type here, is because I've relied on convention before: if decide() returned "default", that was treated as the passthrough. I do not wish to rely on such convention anymore, need a way to explicitly label a decision as passthrough or garbage, and perhaps even allow multiple passthrough variants too.
Naturally, output() will then follow: fn output(request: Request, decision: Decision) -> Result[Response, Report].
There's gonna be a shakeup in error reporting too. I'm currently stuffing everything into a miette::Report directly, which is okay for iocaine's purposes, but it's not fine for iocaine-powder, which would want something more fine grained, so if anyone embeds it, they can handle different errors differently, if so they wish.
The error type(s?) will still impl Diagnostic, though.
Yay, I have the skeleton code for dealing with HTTP requests! The way I'm approaching this, to remain mostly HTTP implementation-agnostic, is that there's gonna be two main types exposed to the iocaine runtime:
RequestandResponse. On the Rust side, these are a simple struct that wrap anmpscchannel, and provide a new function:Where
Topis a trait that requires implementing a few functions like.method(&self) -> impl Into<String>,path(&self) -> impl Into<String>, and so on. Those implementations send a request over the wrappedmpscchannel, along with aoneshot::Senderto respond on, then wait for the response, and return that.