use super::{ADAPTER, APP, PORT, packages}; #[test] fn roles_cannot_call_around_the_port_boundary() { for call in [ "app.read(value)", "domain.read(value)", "port.read(value)", "foreign.library.read(value)", "shop.account.adapter.other.helper(value)", ] { let adapter = format!("port.read(value: Int) (answer: Int) {{ answer = {call} }}"); let error = packages(&[ ("shop.account.app", APP), ( "shop.account.domain", "read(value: Int) (answer: Int) { answer = value }", ), ("shop.account.port", PORT), ("shop.account.adapter.local", &adapter), ( "foreign.library", "public read(value: Int) (answer: Int) { answer = value }", ), ]) .unwrap_err(); assert!( matches!(error.code, "C004" | "C005" | "C006"), "{call}: {error:?}" ); } for caller in ["shop.account.domain", "shop.other.app", "foreign"] { let error = packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", ADAPTER), ( caller, "entry(value: Int) (answer: Int) { answer = shop.account.port.read(value) }", ), ]) .unwrap_err(); assert_eq!(error.code, "C006", "{caller}: {error:?}"); } for call in [ "adapter.helper(value)", "adapter.port.read(value)", "shop.account.adapter.port.read(value)", ] { assert!( packages(&[ ("shop.account.app", &APP.replace("port.read(value)", call)), ("shop.account.port", PORT), ("shop.account.adapter", ADAPTER) ]) .is_err(), "{call}" ); } } #[test] fn only_app_types_in_the_complete_contract_graph_are_visible() { let app = "type Problem { error Missing(message: Text) }\ntype Person { name: Text }\ntype Unrelated { number: Int }\nread(value: Person) (answer: Text) { answer = port.read(port.Row { person = value }) }"; let port = "type Row { person: app.Person }\nread(value: Row) (answer: Text) fails app.Problem"; let adapter = "port.read(value: port.Row) (answer: Text) { answer = label(value.person) }\nlabel(value: app.Person) (answer: Text) { answer = value.name }"; assert!( packages(&[ ("shop.account.adapter", adapter), ("shop.account.port", port), ("shop.account.app", app) ]) .is_ok() ); for bad in [ format!("{adapter}\nunused(value: app.Unrelated) () {{}}"), adapter.replace( "answer = label(value.person)", "hidden = app.Unrelated { number = 1 }\nanswer = label(value.person)", ), format!("{adapter}\ntype Hidden {{ value: List }}"), ] { assert_eq!( packages(&[ ("shop.account.adapter", &bad), ("shop.account.port", port), ("shop.account.app", app) ]) .unwrap_err() .code, "C006" ); } let bad = format!("{port}\ntype Unused {{ value: app.Unrelated }}"); assert_eq!( packages(&[ ("shop.account.app", app), ("shop.account.port", &bad), ("shop.account.adapter", adapter) ]) .unwrap_err() .code, "C006" ); } #[test] fn adapter_cannot_read_another_ports_private_dto() { let error = packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ( "shop.account.port.other", "type Hidden { value: Int }\nwrite(value: Int) () fails app.Problem", ), ( "shop.account.adapter.first", &format!("{ADAPTER}\nunused(value: port.other.Hidden) () {{}}"), ), ( "shop.account.adapter.second", "port.other.write(value: Int) () {}", ), ]) .unwrap_err(); assert_eq!(error.code, "C006"); }