use super::{APP, PORT, packages, signature}; #[test] fn external_worker_secrets_belong_only_in_settings() { let adapter = "setting { token: Secret }\nexternal exec \"worker\" {}"; for kind in [ "Text", "Secret", "Secret?", "List", "app.Credentials", ] { let app = format!( "type Problem {{ error Failed(message: Text) }}\ntype Credentials {{ tokens: List }}\nread(value: {kind}) (answer: Int) {{ answer = port.read(value) }}" ); let port = format!("read(value: {kind}) (answer: Int) fails app.Problem"); let result = packages(&[ ("shop.account.app", &app), ("shop.account.port", &port), ("shop.account.adapter", adapter), ]); if kind == "Text" { assert!(result.is_ok(), "{result:?}"); } else { let error = result.unwrap_err(); assert!( error.message.contains("Secret belongs only in setting"), "{kind}: {error:?}" ); } } } #[test] fn settings_are_private_readonly_typed_values() { let adapter = "setting { offset: Int\ntoken: Secret }\nport.read(value: Int) (answer: Int) { answer = helper(value) }\nhelper(value: Int) (answer: Int) pure { answer = value + setting.offset }"; let program = packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", adapter), ]) .unwrap(); assert!(signature(&program, "read").effects.configuration); assert!(signature(&program, "helper").pure); assert_eq!(signature(&program, "read").failures.len(), 2); for body in [ "setting.offset = value\nanswer = value", "setting = value\nanswer = value", "answer = setting.missing", "answer = setting.offset()", "answer = setting.token", ] { let bad = format!( "setting {{ offset: Int\ntoken: Secret }}\nport.read(value: Int) (answer: Int) {{ {body} }}" ); assert!( packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", &bad) ]) .is_err(), "{body}" ); } let empty = "setting {}\nport.read(value: Int) (answer: Int) { setting = value\nanswer = value }"; assert!( packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", empty) ]) .is_err() ); } #[test] fn settings_reject_wrong_roles_invalid_wire_fields_and_shadowing() { for owner in [ "main", "shop.account.app", "shop.account.port", "shop.account.domain", ] { assert!( packages(&[(owner, "setting { offset: Int }")]).is_err(), "{owner}" ); } for setting in [ "setting { Ready }", "setting { private offset: Int }", "setting { offset: Int > 0 }", "setting { file: dever.system.File }", "setting { values: Map }", "setting { offset: Int }\nsetting { token: Secret }", "setting { missing: Unknown }", ] { let adapter = format!("{setting}\nport.read(value: Int) (answer: Int) {{ answer = value }}"); assert!( packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", &adapter) ]) .is_err(), "{setting}" ); } for helper in [ "helper(setting: Int) () {}", "helper() (setting: Int) { setting = 1 }", ] { let adapter = format!( "setting {{ offset: Int }}\nport.read(value: Int) (answer: Int) {{ answer = value }}\n{helper}" ); assert!( packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter", &adapter) ]) .is_err() ); } } #[test] fn unused_adapter_candidates_still_enforce_failures_and_complete_contracts() { let valid = "port.read(value: Int) (answer: Int) { answer = value }"; let invalid = "type Private { error Failed }\nport.read(value: Int) (answer: Int) { fail(Private.Failed()) }"; assert_eq!( packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter.local", valid), ("shop.account.adapter.remote", invalid) ]) .unwrap_err() .code, "C012" ); let extra_port = format!("{PORT}\nwrite(value: Int) () fails app.Problem"); let complete = format!("{valid}\nport.write(value: Int) () {{}}"); assert!( packages(&[ ("shop.account.app", APP), ("shop.account.port", &extra_port), ("shop.account.adapter.local", &complete), ("shop.account.adapter.remote", valid) ]) .is_err() ); } #[test] fn selection_keeps_all_candidate_effects_and_failure_identity() { let quiet = "setting { offset: Int }\nport.read(value: Int) (answer: Int) { answer = value + setting.offset }"; let scheduled = "port.read(value: Int) (answer: Int) { answer = wait(run(helper(value))) }\nhelper(value: Int) (answer: Int) { answer = value }"; let program = packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter.local", quiet), ("shop.account.adapter.remote", scheduled), ]) .unwrap(); let effects = signature(&program, "read").effects; assert!(effects.configuration && effects.suspend && effects.schedule); let pure = APP.replace("(answer: Int) {", "(answer: Int) pure {"); assert_eq!( packages(&[ ("shop.account.app", &pure), ("shop.account.port", PORT), ("shop.account.adapter.local", quiet), ("shop.account.adapter.remote", scheduled), ]) .unwrap_err() .code, "C013" ); let error = "port.read(value: Int) (answer: Int) { fail(app.Problem.Missing(\"missing\")) }"; let program = packages(&[ ("shop.account.app", APP), ("shop.account.port", PORT), ("shop.account.adapter.local", quiet), ("shop.account.adapter.remote", error), ]) .unwrap(); assert_eq!(signature(&program, "read").failures.len(), 2); assert!(signature(&program, "read").effects.configuration); assert!(!program.api_snapshot().contains("setting.offset")); }