use dever::{ compiler::{build, model::Program, project::Project}, syntax::source::Source, }; use std::path::Path; #[path = "http/live.rs"] mod live; #[path = "http/streaming.rs"] mod streaming; #[path = "http/websocket.rs"] mod websocket; fn linked(text: &str) -> Result { let mut project = Project::load( &Path::new(env!("CARGO_MANIFEST_DIR")).join("../test/rust/fixtures/http.dever"), ) .unwrap(); let source = project .sources .iter_mut() .find(|source| source.path.ends_with("http.dever")) .unwrap(); *source = Source::new(source.id, "case.dever", text.into()); project.check() } const ROUTE: &str = "route(request: dever.http.Request) (response: dever.http.Response) { response = dever.http.Response { status = 200, headers = [], body = request.body } }"; #[test] fn leaf_http_handlers_do_not_convert_unused_request_fields() { let project = Project::load( &Path::new(env!("CARGO_MANIFEST_DIR")) .join("../test/performance/fixtures/http/concurrent.dever"), ) .unwrap(); let program = project.check().unwrap(); let main = program .functions .iter() .position(|function| function.signature.name == "main") .unwrap(); let source = dever::compiler::emit::generate(&program, &project.sources, &[main].into()); assert!(source.contains("http::serve_context"), "{source}"); assert!(source.contains("struct RequestView"), "{source}"); assert!(!source.contains("parts.method.to_string()"), "{source}"); assert!( !source.contains("received_headers(&parts.headers)"), "{source}" ); } #[test] fn tls_pool_types_are_resources_and_configuration_failure_is_explicit() { for (source, code) in [ ( "main() () { lost = result(dever.tls.client(dever.bytes.fromText(\"invalid\"))) }", "C012", ), ( "main(config: dever.system.ClientTls) (same: Bool) { same = config == config }", "C005", ), ( "main() (config: dever.system.ClientTls) { config = dever.system.tlsSystem() }", "C006", ), ( "main() (client: dever.system.HttpClient) pure { client = dever.http.client(\"http://localhost\", dever.tls.system(), dever.http.defaultLimits(), dever.http.defaultPoolLimits()) }", "C013", ), ] { let error = linked(source).unwrap_err(); assert!(error.contains(code), "{error}"); } let program = linked("main() (client: dever.system.HttpClient) { client = dever.http.client(\"http://localhost\", dever.tls.system(), dever.http.defaultLimits(), dever.http.defaultPoolLimits()) }").unwrap(); let main = program .functions .iter() .find(|function| function.signature.name == "main") .unwrap(); assert!(main.signature.effects.schedule && main.signature.effects.suspend); } #[test] fn http_effects_and_runtime_selection_follow_reachable_calls() { for (source, http, schedule) in [ ("main() (answer: Int) { answer = 1 }".into(), false, false), ("main(request: dever.http.Request) (response: dever.http.Response) { response = dever.http.send(\"127.0.0.1\", 80, request, dever.http.defaultLimits()) }".into(), true, false), (format!("{ROUTE}\nmain(listener: dever.system.Listener) () {{ dever.http.serve(route, listener, dever.http.defaultLimits()) }}"), true, true), ] { let program = linked(&source).unwrap(); let main = program.functions.iter().position(|function| function.signature.name == "main").unwrap(); let effects = program.functions[main].signature.effects; assert_eq!(effects.network, http); assert_eq!(effects.suspend, http); assert_eq!(effects.schedule, schedule); assert_eq!(build::runtime_features(&program, &program.reachable([main])).contains(&"http"), http); } } #[test] fn http_preserves_handler_types_effect_boundaries_and_failure_obligations() { for (source, code) in [ (format!("{ROUTE}\nmain(listener: dever.system.Listener) () {{ dever.system.httpServe(route, listener, dever.http.defaultLimits()) }}"), "C006"), (format!("{ROUTE}\nmain(listener: dever.system.Listener) () pure {{ dever.http.serve(route, listener, dever.http.defaultLimits()) }}"), "C013"), ("route(request: Int) (response: Int) { response = request }\nmain(listener: dever.system.Listener) () { dever.http.serve(route, listener, dever.http.defaultLimits()) }".into(), "C005"), ("route(request: dever.http.Request) (response: dever.http.Response) { dever.io.println(\"blocking\")\nresponse = dever.http.Response { status = 200, headers = [], body = request.body } }\nmain(listener: dever.system.Listener) () { dever.http.serve(route, listener, dever.http.defaultLimits()) }".into(), "C013"), ("main(request: dever.http.Request) () { lost = result(dever.http.send(\"127.0.0.1\", 80, request, dever.http.defaultLimits())) }".into(), "C012"), ] { let error = linked(&source).unwrap_err(); assert!(error.contains(code), "{source}\n{error}"); } }