use super::*; #[test] fn enums_and_local_decode_use_shared_cmd_http_and_json_codecs() { let directory = Directory::new(); let root = directory.0.join("module/demo/input"); std::fs::create_dir_all(root.join("app")).unwrap(); std::fs::write(root.join("app/read.dever"), r#"type State { Ready Busy } type Packet { state: State optional: State? states: List payload: Json } type Decoded { Ready(value: Packet) error Invalid(cause: dever.json.JsonResult) } echo(value: State) (output: State) { output = value } packet(value: Packet) (output: Packet) { output = value } decode(payload: Json) (output: Packet) { value: Packet = dever.json.decode(payload) output = value } capture(payload: Json) (valid: Bool) { value: Decoded = result(dever.json.decode(payload)) valid = accepted(value) } accepted(value: Decoded.Ready(packet)) (valid: Bool) recover("无效输入返回 false") { valid = packet.state == State.Ready } accepted(value: Decoded.Invalid(cause)) (valid: Bool) recover("无效输入返回 false") { valid = false } encode(value: State) (output: Json) { output = dever.json.encode(value) } fault(divisor: Int) (valid: Bool) { value: Decoded = result(dever.json.decode(dever.json.encode(10 // divisor))) valid = accepted(value) } "#).unwrap(); std::fs::write(root.join("api.dever"), "cmd echo = app.echo\ncmd packet = app.packet\ncmd decode = app.decode\ncmd capture = app.capture\ncmd encode = app.encode\ncmd fault = app.fault\npublic get echo = app.echo\npublic post packet = app.packet\npublic post decode = app.decode\npublic post capture = app.capture\n").unwrap(); let project = Project::load(&directory.0).unwrap(); let program = project.check().unwrap(); let binary = build::build(&program, &project.sources, "main", &directory.0.join("app")).unwrap(); let packet = r#"{"state":"Ready","optional":null,"states":["Busy"],"payload":{"x":1}}"#; for (name, input, data) in [ ("echo", r#"{"value":"Ready"}"#.to_owned(), r#""Ready""#), ("packet", format!(r#"{{"value":{packet}}}"#), packet), ("decode", format!(r#"{{"payload":{packet}}}"#), packet), ("capture", format!(r#"{{"payload":{packet}}}"#), "true"), ( "capture", r#"{"payload":{"state":"Wrong"}}"#.into(), "false", ), ("encode", r#"{"value":"Busy"}"#.into(), r#""Busy""#), ] { let output = execute(&binary, &format!("demo.input.{name}"), &input); assert!( output.status.success(), "{name}: {}", String::from_utf8_lossy(&output.stderr) ); assert_eq!( String::from_utf8(output.stdout).unwrap(), format!("{{\"code\":0,\"message\":\"ok\",\"data\":{data}}}\n") ); } for input in [ r#"{"value":"Wrong"}"#, r#"{"value":0}"#, r#"{"value":{"Ready":null}}"#, ] { assert!(!execute(&binary, "demo.input.echo", input).status.success()); } let fault = execute(&binary, "demo.input.fault", r#"{"divisor":0}"#); assert!(!fault.status.success()); let error = String::from_utf8_lossy(&fault.stderr); assert!(error.contains("integer arithmetic fault"), "{error}"); let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let port = listener.local_addr().unwrap().port(); drop(listener); let config = directory.0.join("setting.json"); std::fs::write(&config, format!(r#"{{"http":{{"host":"127.0.0.1","port":{port}}},"sites":{{"public":{{"path":"","hosts":["fixture.test"]}}}}}}"#)).unwrap(); let mut server = Server( Command::new(&binary) .args(["--config", config.to_str().unwrap()]) .stdout(std::process::Stdio::null()) .spawn() .unwrap(), ); let deadline = Instant::now() + Duration::from_secs(5); while TcpStream::connect(("127.0.0.1", port)).is_err() { assert!(server.0.try_wait().unwrap().is_none()); assert!(Instant::now() < deadline); std::thread::sleep(Duration::from_millis(10)); } for (method, path, body, status, data) in [ ( "GET", "/demo/input/echo?value=Busy", "".into(), 200, r#""Busy""#, ), ( "GET", "/demo/input/echo?value=Wrong", "".into(), 400, "null", ), ( "POST", "/demo/input/packet", format!(r#"{{"value":{packet}}}"#), 200, packet, ), ( "POST", "/demo/input/decode", format!(r#"{{"payload":{packet}}}"#), 200, packet, ), ( "POST", "/demo/input/capture", r#"{"payload":null}"#.into(), 200, "false", ), ] { let (actual, output) = request( port, method, path, "fixture.test", "Content-Type: application/json\r\n", &body, ); assert_eq!(actual, status, "{path}: {output}"); if status == 200 { assert_eq!( output, format!("{{\"code\":0,\"message\":\"ok\",\"data\":{data}}}") ); } } }