use std::fs;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_FIXTURE: AtomicU64 = AtomicU64::new(0);
struct Fixture {
root: PathBuf,
}
impl Fixture {
fn new(label: &str) -> Self {
let ordinal = NEXT_FIXTURE.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!(
"noxid-wo21-stream-layout-{label}-{}-{ordinal}",
std::process::id()
));
fs::create_dir_all(&root).expect("create stream endpoint fixture");
fs::write(
root.join("Noxid.toml"),
"[app]\ntitle = \"WO-21 streams\"\n",
)
.expect("write project config");
Self { root }
}
fn write(&self, relative: &str, contents: &str) {
let path = self.root.join(relative);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).expect("create fixture parent");
}
fs::write(path, contents).expect("write fixture source");
}
fn build(&self) -> Output {
Command::new(env!("CARGO_BIN_EXE_noxid"))
.args(["build", self.root.to_str().expect("UTF-8 fixture")])
.arg("--out-dir")
.arg(self.root.join("dist"))
.output()
.expect("run stream endpoint build")
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.root);
}
}
fn assert_failure(output: &Output, code: &str, teaching: &str) {
assert!(
!output.status.success(),
"expected {code}, stdout:\n{}",
String::from_utf8_lossy(&output.stdout)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains(code), "missing {code}:\n{stderr}");
assert!(
stderr.contains(teaching),
"missing teaching text:\n{stderr}"
);
}
#[test]
fn stream_endpoint_is_restricted_to_server_api() {
let fixture = Fixture::new("routes");
fixture.write(
"server/routes/events.get.nox",
"stream endpoint Events { result: Stream<String> timeout: 30s }",
);
let output = fixture.build();
assert_failure(&output, "STREAM_ENDPOINT_API_LAYOUT_REQUIRED", "server/api");
assert!(!fixture.root.join("dist").exists());
}
#[test]
fn stream_endpoint_is_get_only() {
let fixture = Fixture::new("post");
fixture.write(
"server/api/events.post.nox",
"stream endpoint Events { result: Stream<String> timeout: 30s }",
);
let output = fixture.build();
assert_failure(&output, "STREAM_ENDPOINT_REQUIRES_GET", "events.get.nox");
assert!(!fixture.root.join("dist").exists());
}
#[test]
fn websocket_endpoint_syntax_is_not_admitted_in_sse_phase_one() {
let fixture = Fixture::new("websocket");
fixture.write(
"server/api/socket.get.nox",
"websocket endpoint Socket { result: Stream<String> }",
);
let output = fixture.build();
assert_failure(&output, "EXPECTED_DECLARATION", "expected an import");
assert!(!fixture.root.join("dist").exists());
}