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-wo24-qa1-{label}-{}-{ordinal}",
std::process::id()
));
fs::create_dir_all(root.join("server/queues")).expect("create queue fixture");
fs::write(
root.join("Noxid.toml"),
"[app]\ntitle = \"WO-24 QA round 1\"\n",
)
.expect("write config");
fs::write(root.join("package.json"), "{\"type\":\"module\"}\n")
.expect("write package marker");
fs::write(
root.join("server/host.js"),
"export const queues = Object.freeze({});\n",
)
.expect("write inert queue host");
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 noxid(&self, arguments: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_noxid"))
.args(arguments)
.current_dir(&self.root)
.output()
.expect("run noxid")
}
fn build(&self) -> Output {
self.noxid(&["build", ".", "--out-dir", "dist"])
}
fn run_node(&self, source: &str) -> Output {
self.write("dist/qa.mjs", source);
Command::new("node")
.arg("qa.mjs")
.current_dir(self.root.join("dist"))
.env_remove("DATABASE_URL")
.output()
.expect("run generated queue module")
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.root);
}
}
fn assert_success(output: &Output, context: &str) {
assert!(
output.status.success(),
"{context}:\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn qa_round1_result_wrapped_named_payload_reaches_database_boundary() {
let fixture = Fixture::new("result-named");
fixture.write(
"server/queues/Outcome.nox",
r#"type Receipt { id: String }
queue Outcome {
payload { result: Result<Receipt, String> }
retry: 1
backoff: 1s
}
"#,
);
assert_success(&fixture.build(), "build recursive payload fixture");
let node = fixture.run_node(
r#"import { enqueue } from "./server/handler.js";
async function code(operation) {
try { await operation(); } catch (error) { return error?.code; }
return null;
}
const accepted = await code(() => enqueue("Outcome", { result: { tag: "Ok", value: { id: "r-1" } } }));
if (accepted !== "QUEUE_DATABASE_URL_REQUIRED") throw new Error(`valid Result<Receipt, String> did not reach storage: ${accepted}`);
const rejected = await code(() => enqueue("Outcome", { result: { tag: "Ok", value: { id: 1 } } }));
if (rejected !== "QUEUE_PAYLOAD_TYPE") throw new Error(`invalid nested Receipt was not refused: ${rejected}`);
"#,
);
assert_success(&node, "validate Result-wrapped named queue payload");
}
#[test]
fn qa_round1_payload_validation_never_invokes_accessors_or_accepts_symbols() {
let fixture = Fixture::new("ordinary-data");
fixture.write(
"server/queues/Safe.nox",
"queue Safe { payload { label: String } retry: 0 backoff: 1s }\n",
);
assert_success(&fixture.build(), "build ordinary-data fixture");
let node = fixture.run_node(
r#"import { enqueue } from "./server/handler.js";
let reads = 0;
const accessor = {};
Object.defineProperty(accessor, "label", { enumerable: true, get() { reads += 1; return "stolen"; } });
const symbol = { label: "ok", [Symbol("smuggled")]: true };
const inherited = Object.create({ label: "inherited" });
for (const [label, payload] of [["accessor", accessor], ["symbol", symbol], ["prototype", inherited]]) {
let code = null;
try { await enqueue("Safe", payload); } catch (error) { code = error?.code; }
if (code !== "QUEUE_PAYLOAD_TYPE") throw new Error(`${label} payload escaped: ${code}`);
}
if (reads !== 0) throw new Error(`payload getter executed ${reads} time(s)`);
"#,
);
assert_success(&node, "exercise ordinary-data queue payload boundary");
}
#[test]
fn qa_round1_empty_and_optional_payload_boundaries_are_closed_but_usable() {
let fixture = Fixture::new("empty-optional");
fixture.write(
"server/queues/Empty.nox",
"queue Empty { payload {} retry: 0 backoff: 1s }\n",
);
fixture.write(
"server/queues/Maybe.nox",
"queue Maybe { payload { note: Optional<String> } retry: 0 backoff: 1s }\n",
);
assert_success(&fixture.build(), "build boundary-value fixture");
let node = fixture.run_node(
r#"import { enqueue } from "./server/handler.js";
async function code(operation) {
try { await operation(); } catch (error) { return error?.code; }
return null;
}
for (const operation of [
() => enqueue("Empty", Object.create(null)),
() => enqueue("Maybe", {}),
() => enqueue("Maybe", { note: null }),
]) {
const observed = await code(operation);
if (observed !== "QUEUE_DATABASE_URL_REQUIRED") throw new Error(`valid boundary value was refused before storage: ${observed}`);
}
for (const operation of [
() => enqueue("Empty", { extra: 1 }),
() => enqueue("Maybe", { note: "ok", extra: 1 }),
]) {
const observed = await code(operation);
if (observed !== "QUEUE_PAYLOAD_TYPE") throw new Error(`undeclared key escaped: ${observed}`);
}
"#,
);
assert_success(&node, "exercise empty and optional queue payloads");
}
#[test]
fn qa_round1_queue_scenario_executes_conditional_handler_at_stubbed_clock() {
let fixture = Fixture::new("scenario-if");
fixture.write(
"server/queues/Classify.nox",
r#"queue Classify {
payload { count: Int }
handler {
if count < 0 { return "negative" }
else { return "nonnegative" }
}
retry: 2
backoff: 7s
scenario Negative {
description: "conditional queue handlers execute without live storage"
clock: "2026-08-29T12:00:00Z"
when: enqueue(count: -1)
expect: value == "negative", attempts == 1, refusal == ""
}
}
"#,
);
let output = fixture.noxid(&["test", ".", "--gate", "--json"]);
assert_success(&output, "execute conditional queue scenario");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("\"summary\":{\"total\":1,\"passed\":1,\"failed\":0"));
assert!(stdout.contains("\"semanticUnit\":\"queue:Classify\""));
}
#[test]
fn qa_round1_queue_layout_and_handler_side_effects_fail_closed() {
let fixture = Fixture::new("fail-closed");
fixture.write(
"server/queues/Unsafe.nox",
r#"queue Unsafe {
payload { value: Int }
handler { value = 2 }
retry: 1
backoff: 1s
}
"#,
);
let rejected = fixture.build();
assert!(!rejected.status.success());
let stderr = String::from_utf8_lossy(&rejected.stderr);
assert!(
stderr.contains("QUEUE_HANDLER_INVALID"),
"mutable queue handler did not fail closed:\n{stderr}"
);
}