use super::{Directory, fixture}; use dever::compiler::{build, project::Project}; use std::process::Command; mod settings; #[test] fn native_ports_execute_file_io_and_preserve_capture_and_faults() { let directory = Directory::new(); let project = Project::load(&fixture().with_file_name("ports")).unwrap(); let program = project.check().unwrap(); let connect = program .functions .iter() .find(|function| function.signature.name == "connect") .unwrap(); assert!(connect.signature.effects.network && connect.signature.effects.suspend); assert!(!connect.signature.effects.blocking && !connect.signature.effects.unmediated_io); let binary = build::build(&program, &project.sources, "main", &directory.0.join("app")).unwrap(); let file = directory.0.join("input.txt"); std::fs::write(&file, "owned Port test file").unwrap(); let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let address = listener.local_addr().unwrap(); for (name, input, expected) in [ ("open", serde_json::json!({"path": file}).to_string(), "1"), ("adjust", "{\"value\":41}".into(), "42"), ("captured", "{\"value\":41}".into(), "true"), ("captured", "{\"value\":-1}".into(), "false"), ( "connect", serde_json::json!({"host":"127.0.0.1", "port":address.port()}).to_string(), "true", ), ] { let result = Command::new(&binary) .args([format!("demo.files.{name}"), input]) .output() .unwrap(); assert!( result.status.success(), "{name}: {}", String::from_utf8_lossy(&result.stderr) ); assert_eq!( String::from_utf8(result.stdout).unwrap(), format!("{{\"code\":0,\"message\":\"ok\",\"data\":{expected}}}\n") ); } // TCP 握手可先进入监听队列;非阻塞 accept 确认应用确实建立了连接。 listener.set_nonblocking(true).unwrap(); let (peer, _) = listener.accept().unwrap(); drop(peer); for (name, input, expected, source) in [ ( "open", serde_json::json!({"path": directory.0.join("missing")}).to_string(), "OpenResult.Failed", "lib/dever/io.dever:", ), ( "adjust", "{\"value\":-1}".into(), "Problem.Missing", "adapter.dever:", ), ( "captured", "{\"value\":9223372036854775807}".into(), "integer arithmetic fault", "adapter.dever:", ), ] { let result = Command::new(&binary) .args([format!("demo.files.{name}"), input]) .output() .unwrap(); assert!(!result.status.success()); assert!(result.stdout.is_empty()); let error = String::from_utf8(result.stderr).unwrap(); assert!( error.contains(expected) && error.contains(source), "{error}" ); } } #[test] fn adapter_selection_remains_runtime_configuration() { let directory = Directory::new(); let module = directory.0.join("module/demo/files"); std::fs::create_dir_all(&module).unwrap(); std::fs::write(module.join("app.dever"), "not parsed before settings").unwrap(); let config = directory.0.join("config"); std::fs::create_dir(&config).unwrap(); for text in [ r#"{"adapter":{"demo.files":{"use":"default"}}}"#, r#"{"adapter":null}"#, ] { std::fs::write(config.join("setting.json"), text).unwrap(); assert!(Project::load(&directory.0).is_ok(), "{text}"); } std::fs::write( config.join("setting.json"), r#"{"adapter":{},"adapter":{}}"#, ) .unwrap(); assert!(Project::load(&directory.0).is_err()); std::fs::write( config.join("setting.json"), r#"{"package":{"registry":"https://packages.test","use":[],"fixture-only-secret":true}}"#, ) .unwrap(); let error = Project::load(&directory.0).err().unwrap(); assert!(error.contains("invalid project configuration JSON")); assert!(!error.contains("fixture-only-secret")); }