/* Copyright (C) 2026 Kingsrook, LLC. * SPDX-License-Identifier: Apache-2.0 */ //! Fixed trusted compatibility cases. These are not hostile-runtime qualification. #![allow(clippy::expect_used)] use std::cell::Cell; use std::rc::Rc; use std::thread; use std::time::Duration; use qqq_javy_runtime::{Failure, process}; use serde_json::{Value, json}; use sha2::{Digest, Sha256}; /// Synthetic exact runtime identity; an actual supervisor binds its loaded binary digest. fn runtime_id() -> String { "ab".repeat(32) } /// Prepare only fixed test module sources; this is not TypeScript compilation. fn prepare(files: Vec<(&str, &str)>) -> Value { prepare_result(files).expect("trusted preparation") } /// Keep compiler refusals observable without relaxing the trusted cache boundary. fn prepare_result(files: Vec<(&str, &str)>) -> Result { let bundle = json!({"format":"qqq-esm/1","entryPoint":"main.js","files":files.into_iter().map(|(path,content)|json!({"path":path,"content":content})).collect::>()}).to_string(); let input = json!({"operation":"prepare","runtimeSha256":runtime_id(),"artifactSha256":format!("{:x}",Sha256::digest(bundle.as_bytes())),"bundle":bundle}); let output = process(input.to_string().as_bytes(), |_| panic!("prepare must not broker"))?; Ok(serde_json::from_slice(&output).expect("prepared JSON")) } /// Create the exact hot request shape used by the persistent transport, without bundle. fn request(prepared: &Value) -> Value { json!({"protocol":"qbit-scripting-worker/1","type":"execute","requestId":"trusted-test","artifactId":"fixed", "artifactSha256":prepared["artifactSha256"],"entryPoint":"main.ts","event":"CONTEXT","payload":{"actor":"alpha"},"capabilityToken":"synthetic-only", "limits":{"wallMillis":2000,"cpuMillis":1000,"memoryBytes":64*1024*1024,"outputBytes":65536,"brokerCalls":2}}) } /// Invoke through the public private-envelope boundary. fn run(prepared: &Value, request: Value, broker: impl Fn(&[u8]) -> Result, Failure> + 'static) -> Result { let input = json!({"operation":"run","runtimeSha256":runtime_id(),"preparedSha256":prepared["preparedSha256"],"prepared":prepared,"request":request}); let output = process(input.to_string().as_bytes(), broker)?; Ok(serde_json::from_slice(&output).expect("result JSON")) } /// Exact existing broker response, containing only synthetic data. fn allow(bytes: &[u8]) -> Result, Failure> { let request: Value = serde_json::from_slice(bytes).expect("broker request"); assert_eq!(request["operation"], "query"); assert_eq!(request["token"], "synthetic-only"); Ok( json!({"protocol":"qbit-scripting-worker/1","type":"brokerResult","ok":true,"rows":[{"id":7}],"truncated":false}) .to_string() .into_bytes(), ) } /// Preparation must not execute module top-level effects or throw statements. #[test] fn preparation_does_not_evaluate_author_module() { let prepared = prepare(vec![( "main.js", "throw new Error('fixed top level'); export function main(){return {ok:true}}", )]); let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); } /// Java's artifact character limit is not an ASCII-only byte limit. #[test] fn unicode_artifact_above_one_megabyte_remains_compatible() { let source = format!( "const marker='{}'; export function main(){{return {{length:marker.length}}}}", "界".repeat(400_000) ); assert!(source.len() > 1024 * 1024); assert!(source.encode_utf16().count() < 1024 * 1024); let prepared = prepare(vec![("main.js", source.as_str())]); let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded Unicode result"); assert_eq!(result["message"]["outcome"], "OK"); assert_eq!(result["message"]["result"]["length"], 400_000); } /// Separate fixed module, await and broker cases localize compatibility failures. #[test] fn fixed_compatibility_boundaries() { for (name, source) in [ ("relative", "import {id} from './state.js'; export function main(){return {id}}"), ("tla", "await Promise.resolve(); export function main(){return {id:7}}"), ( "broker", "export async function main(c,q){const rows=await q.query('purchaseOrder',['id']);return {id:rows[0].id}}", ), ] { let prepared = prepare(vec![("main.js", source), ("state.js", "export const id=7;")]); let result = run(&prepared, request(&prepared), allow).expect("fixed boundary result"); assert_eq!(result["message"]["outcome"], "OK", "boundary: {name}"); assert_eq!(result["message"]["result"]["id"], 7); } } /// Bytecode loading restores strict runtime import checks after nested module reads. #[test] fn nested_bytecode_and_dynamic_imports_preserve_attribute_policy() { for attributes in ["", ", {with:{type:'json'}}"] { let source = format!( "import {{id}} from './nested/state.js'; export async function main(){{const extra=await import('./extra.js'{attributes});return {{id,extra:extra.id}}}}" ); let prepared = prepare(vec![ ("main.js", &source), ("nested/state.js", "export {id} from '../value.js';"), ("value.js", "export const id=7;"), ("extra.js", "export const id=8;"), ]); let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded dynamic result"); assert_eq!(result["message"]["outcome"], if attributes.is_empty() { "OK" } else { "SCRIPT_ERROR" }); if attributes.is_empty() { assert_eq!(result["message"]["result"], json!({"id":7,"extra":8})); } } } /// Attributed source imports never enter the private compiled-bytecode cache. #[test] fn attributed_static_import_is_refused_at_preparation() { for attributed_path in ["main.js", "unused.js"] { let mut files = vec![ ( attributed_path, "import {id} from './state.js' with {type:'json'}; export function main(){return {id}}", ), ("state.js", "export const id=7;"), ]; if attributed_path != "main.js" { files.push(("main.js", "export function main(){return {ok:true}}")); } assert_eq!(prepare_result(files).expect_err("unsupported import attributes"), Failure::Compile); } } /// Relative modules, live bindings, TLA, async main and Promise broker remain observable. #[test] fn fresh_modules_time_random_and_async_broker() { let prepared = prepare(vec![ ( "state.js", "export let count=0; export const stamp=Date.now(); export const random=Math.random(); export function bump(){count++}", ), ( "main.js", "import {count,stamp,random,bump} from './state.js'; await Promise.resolve(); export async function main(context,qqq){const rows=await qqq.query('purchaseOrder',['id']); bump(); return {count,stamp,random,id:rows[0].id,actor:context.payload.actor}}", ), ]); let first = run(&prepared, request(&prepared), allow).expect("first invocation"); thread::sleep(Duration::from_millis(5)); let mut second_request = request(&prepared); second_request["payload"]["actor"] = json!("beta"); let second = run(&prepared, second_request, allow).expect("second invocation"); assert_eq!(first["message"]["outcome"], "OK"); assert_eq!(second["message"]["outcome"], "OK"); let a = &first["message"]["result"]; let b = &second["message"]["result"]; assert_eq!(a["count"], 1); assert_eq!(b["count"], 1); assert_eq!(a["id"], 7); assert_eq!(b["actor"], "beta"); assert_ne!(a["stamp"], b["stamp"]); assert_ne!(a["random"], b["random"]); } /// Pending promises cannot be treated as successful completion. #[test] fn idle_pending_main_is_refused() { let prepared = prepare(vec![("main.js", "export function main(){return new Promise(()=>{})}")]); let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); } /// A second unawaited SDK call is refused by the existing one-pending-call rule. #[test] fn simultaneous_broker_calls_are_refused() { let prepared = prepare(vec![( "main.js", "export async function main(c,q){await Promise.all([q.query('purchaseOrder',['id']),q.query('purchaseOrder',['id'])]);return {ok:true}}", )]); let calls = Rc::new(Cell::new(0)); let observed = Rc::clone(&calls); let result = run(&prepared, request(&prepared), move |bytes| { observed.set(observed.get() + 1); allow(bytes) }) .expect("bounded result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); assert!(calls.get() <= 1); } /// Current broker refusal is not converted into an authorized row result. #[test] fn broker_denial_is_generic() { let prepared = prepare(vec![( "main.js", "export async function main(c,q){return {rows:await q.query('purchaseOrder',['id'])}}", )]); let result = run(&prepared, request(&prepared), |_| Err(Failure::Broker)).expect("bounded result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); assert_eq!(result["message"]["error"]["message"], "Script execution failed"); } /// Cached code is bound to both publication bytes and runtime identity before execution. #[test] fn altered_bytecode_or_runtime_never_brokers() { let prepared = prepare(vec![("main.js", "export function main(){return {ok:true}}")]); let mut changed = prepared.clone(); changed["modules"][0]["bytecode"] = json!("AA=="); assert_eq!( run(&changed, request(&prepared), |_| panic!("must not broker")).expect_err("changed bytes"), Failure::Identity ); changed = prepared.clone(); changed["runtimeSha256"] = json!("cd".repeat(32)); assert_eq!( run(&changed, request(&prepared), |_| panic!("must not broker")).expect_err("changed runtime"), Failure::Identity ); } /// Author serialization callbacks cannot create an unchecked detached rejection. #[test] fn detached_rejection_during_result_serialization_is_refused() { let prepared = prepare(vec![( "main.js", "export function main(){return {toJSON(){Promise.reject(new Error('fixed trusted rejection'));return {ok:true}}}}", )]); let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded serialization result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); } /// Return values are bounded plain objects and preserve declared output limits. #[test] fn output_limit_and_array_results_refused() { for source in ["export function main(){return {text:'x'.repeat(200)}}", "export function main(){return [1,2]}"] { let prepared = prepare(vec![("main.js", source)]); let mut input = request(&prepared); input["limits"]["outputBytes"] = json!(64); let result = run(&prepared, input, |_| panic!("no broker")).expect("bounded result"); assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR"); } }