1#[derive(Clone, Eq, PartialEq, Debug, darling::FromMeta)]
17#[darling(rename_all = "snake_case")]
18pub enum Backend {
19 Node(Node),
22
23 #[cfg(any(test, feature = "sandbox"))]
28 RuntimeOnly(RuntimeOnly),
29}
30
31impl Default for Backend {
32 fn default() -> Self {
33 Backend::Node(Node::Auto)
34 }
35}
36
37#[derive(Clone, Eq, PartialEq, Debug, darling::FromMeta)]
40pub enum Node {
41 #[darling(word)]
43 #[darling(skip)]
44 Auto,
45 Url(String),
47}
48
49impl Node {
50 pub fn url(&self) -> Option<String> {
55 let url = std::env::var("CONTRACTS_NODE_URL").ok().or_else(|| {
56 match self {
57 Node::Auto => None,
58 Node::Url(url) => Some(url.clone()),
59 }
60 });
61 tracing::debug!("[E2E] Using node url {:?}", url);
62 url
63 }
64}
65
66#[cfg(any(test, feature = "sandbox"))]
68#[derive(Clone, Eq, PartialEq, Debug, darling::FromMeta)]
69pub enum RuntimeOnly {
70 #[darling(word)]
71 #[darling(skip)]
72 Default,
73 Sandbox(syn::Path),
74}
75
76#[cfg(any(test, feature = "sandbox"))]
77impl From<RuntimeOnly> for syn::Path {
78 fn from(value: RuntimeOnly) -> Self {
79 match value {
80 RuntimeOnly::Default => syn::parse_quote! { ::ink_e2e::DefaultSandbox },
81 RuntimeOnly::Sandbox(path) => path,
82 }
83 }
84}
85
86#[derive(Debug, Default, PartialEq, Eq, darling::FromMeta)]
88pub struct E2EConfig {
89 #[darling(default)]
96 environment: Option<syn::Path>,
97 #[darling(default)]
99 backend: Backend,
100 #[darling(default)]
103 features: Vec<syn::LitStr>,
104 #[darling(default)]
110 replace_test_attr: Option<String>,
111}
112
113impl E2EConfig {
114 pub fn environment(&self) -> Option<syn::Path> {
116 self.environment.clone()
117 }
118
119 pub fn features(&self) -> Vec<String> {
121 self.features.iter().map(|ls| ls.value()).collect()
122 }
123
124 pub fn backend(&self) -> Backend {
126 self.backend.clone()
127 }
128
129 pub fn replace_test_attr(&self) -> Option<String> {
132 self.replace_test_attr.clone()
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use darling::{
140 FromMeta,
141 ast::NestedMeta,
142 };
143 use quote::quote;
144
145 #[test]
146 fn config_works_backend_runtime_only() {
147 let input = quote! {
148 environment = crate::CustomEnvironment,
149 backend(runtime_only),
150 };
151 let config =
152 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
153
154 assert_eq!(
155 config.environment(),
156 Some(syn::parse_quote! { crate::CustomEnvironment })
157 );
158
159 assert_eq!(config.backend(), Backend::RuntimeOnly(RuntimeOnly::Default));
160 }
161
162 #[test]
163 #[should_panic(expected = "ErrorUnknownField")]
164 fn config_backend_runtime_only_default_not_allowed() {
165 let input = quote! {
166 backend(runtime_only(default)),
167 };
168 let config =
169 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
170
171 assert_eq!(config.backend(), Backend::RuntimeOnly(RuntimeOnly::Default));
172 }
173
174 #[test]
175 fn config_works_runtime_only_with_custom_backend() {
176 let input = quote! {
177 backend(runtime_only(sandbox = ::ink_e2e::DefaultSandbox)),
178 };
179 let config =
180 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
181
182 assert_eq!(
183 config.backend(),
184 Backend::RuntimeOnly(RuntimeOnly::Sandbox(
185 syn::parse_quote! { ::ink_e2e::DefaultSandbox }
186 ))
187 );
188 }
189
190 #[test]
191 fn config_works_backend_node() {
192 let input = quote! {
193 backend(node),
194 };
195 let config =
196 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
197
198 assert_eq!(config.backend(), Backend::Node(Node::Auto));
199
200 match config.backend() {
201 Backend::Node(node_config) => {
202 assert_eq!(node_config, Node::Auto);
203
204 temp_env::with_vars([("CONTRACTS_NODE_URL", None::<&str>)], || {
205 assert_eq!(node_config.url(), None);
206 });
207
208 temp_env::with_vars(
209 [("CONTRACTS_NODE_URL", Some("ws://127.0.0.1:9000"))],
210 || {
211 assert_eq!(
212 node_config.url(),
213 Some(String::from("ws://127.0.0.1:9000"))
214 );
215 },
216 );
217 }
218 _ => panic!("Expected Backend::Node"),
219 }
220 }
221
222 #[test]
223 #[should_panic(expected = "ErrorUnknownField")]
224 fn config_backend_node_auto_not_allowed() {
225 let input = quote! {
226 backend(node(auto)),
227 };
228 let config =
229 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
230
231 assert_eq!(config.backend(), Backend::Node(Node::Auto));
232 }
233
234 #[test]
235 fn config_works_backend_node_url() {
236 let input = quote! {
237 backend(node(url = "ws://0.0.0.0:9999")),
238 };
239 let config =
240 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
241
242 match config.backend() {
243 Backend::Node(node_config) => {
244 assert_eq!(node_config, Node::Url("ws://0.0.0.0:9999".to_owned()));
245
246 temp_env::with_vars([("CONTRACTS_NODE_URL", None::<&str>)], || {
247 assert_eq!(node_config.url(), Some("ws://0.0.0.0:9999".to_owned()));
248 });
249
250 temp_env::with_vars(
251 [("CONTRACTS_NODE_URL", Some("ws://127.0.0.1:9000"))],
252 || {
253 assert_eq!(
254 node_config.url(),
255 Some(String::from("ws://127.0.0.1:9000"))
256 );
257 },
258 );
259 }
260 _ => panic!("Expected Backend::Node"),
261 }
262 }
263
264 #[test]
265 fn config_works_test_attr_replacement() {
266 let input = quote! {
267 replace_test_attr = "#[quickcheck]"
268 };
269 let config =
270 E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
271
272 assert_eq!(config.replace_test_attr(), Some("#[quickcheck]".to_owned()));
273 }
274}