obeli_sk_wasm_workers/
lib.rs

1use concepts::StrVariant;
2use std::{error::Error, fmt::Debug};
3use utils::wasm_tools::{self};
4
5pub mod activity;
6mod component_logger;
7pub mod engines;
8pub mod epoch_ticker;
9mod host_exports;
10pub mod std_output_stream;
11pub mod webhook;
12pub mod workflow;
13
14#[derive(thiserror::Error, Debug)]
15pub enum WasmFileError {
16    #[error("cannot read WASM file: {0}")]
17    CannotReadComponent(wasmtime::Error),
18    #[error("cannot decode: {0}")]
19    DecodeError(#[from] wasm_tools::DecodeError),
20    #[error("linking error - {context}, details: {err}")]
21    LinkingError {
22        context: StrVariant,
23        err: Box<dyn Error + Send + Sync>,
24    },
25}
26
27pub mod envvar {
28    #[derive(Clone, derive_more::Debug)]
29    pub struct EnvVar {
30        pub key: String,
31        #[debug(skip)]
32        pub val: String,
33    }
34}
35
36#[cfg(test)]
37pub(crate) mod tests {
38    use std::{sync::Arc, time::Duration};
39
40    use async_trait::async_trait;
41    use concepts::{
42        ComponentId, ComponentRetryConfig, FnName, FunctionFqn, FunctionMetadata, FunctionRegistry,
43        IfcFqnName, PackageIfcFns, ParameterTypes,
44    };
45    use indexmap::IndexMap;
46    use utils::wasm_tools::WasmComponent;
47
48    pub(crate) struct TestingFnRegistry {
49        ffqn_to_fn_details:
50            hashbrown::HashMap<FunctionFqn, (FunctionMetadata, ComponentId, ComponentRetryConfig)>,
51        export_hierarchy: Vec<PackageIfcFns>,
52    }
53
54    impl TestingFnRegistry {
55        pub(crate) fn new_from_components(
56            wasm_components: Vec<(WasmComponent, ComponentId)>,
57        ) -> Arc<dyn FunctionRegistry> {
58            let mut ffqn_to_fn_details = hashbrown::HashMap::new();
59            let mut export_hierarchy: hashbrown::HashMap<
60                IfcFqnName,
61                IndexMap<FnName, FunctionMetadata>,
62            > = hashbrown::HashMap::new();
63            for (wasm_component, component_id) in wasm_components {
64                for exported_function in wasm_component.exim.get_exports(true) {
65                    let ffqn = exported_function.ffqn.clone();
66                    ffqn_to_fn_details.insert(
67                        ffqn.clone(),
68                        (
69                            exported_function.clone(),
70                            component_id.clone(),
71                            ComponentRetryConfig {
72                                max_retries: 0,
73                                retry_exp_backoff: Duration::ZERO,
74                            },
75                        ),
76                    );
77
78                    let index_map = export_hierarchy.entry(ffqn.ifc_fqn.clone()).or_default();
79                    index_map.insert(ffqn.function_name.clone(), exported_function.clone());
80                }
81            }
82            let export_hierarchy = export_hierarchy
83                .into_iter()
84                .map(|(ifc_fqn, fns)| PackageIfcFns {
85                    extension: ifc_fqn.is_extension(),
86                    ifc_fqn,
87                    fns,
88                })
89                .collect();
90            Arc::from(TestingFnRegistry {
91                ffqn_to_fn_details,
92                export_hierarchy,
93            })
94        }
95    }
96
97    #[async_trait]
98    impl FunctionRegistry for TestingFnRegistry {
99        async fn get_by_exported_function(
100            &self,
101            ffqn: &FunctionFqn,
102        ) -> Option<(FunctionMetadata, ComponentId, ComponentRetryConfig)> {
103            self.ffqn_to_fn_details.get(ffqn).cloned()
104        }
105
106        fn all_exports(&self) -> &[PackageIfcFns] {
107            &self.export_hierarchy
108        }
109    }
110
111    pub(crate) fn fn_registry_dummy(ffqns: &[FunctionFqn]) -> Arc<dyn FunctionRegistry> {
112        let component_id = ComponentId::dummy_activity();
113        let mut ffqn_to_fn_details = hashbrown::HashMap::new();
114        let mut export_hierarchy: hashbrown::HashMap<
115            IfcFqnName,
116            IndexMap<FnName, FunctionMetadata>,
117        > = hashbrown::HashMap::new();
118        for ffqn in ffqns {
119            let fn_metadata = FunctionMetadata {
120                ffqn: ffqn.clone(),
121                parameter_types: ParameterTypes::default(),
122                return_type: None,
123                extension: None,
124                submittable: true,
125            };
126            ffqn_to_fn_details.insert(
127                ffqn.clone(),
128                (
129                    fn_metadata.clone(),
130                    component_id.clone(),
131                    ComponentRetryConfig {
132                        max_retries: 0,
133                        retry_exp_backoff: Duration::ZERO,
134                    },
135                ),
136            );
137            let index_map = export_hierarchy.entry(ffqn.ifc_fqn.clone()).or_default();
138            index_map.insert(ffqn.function_name.clone(), fn_metadata);
139        }
140        let export_hierarchy = export_hierarchy
141            .into_iter()
142            .map(|(ifc_fqn, fns)| PackageIfcFns {
143                extension: ifc_fqn.is_extension(),
144                ifc_fqn,
145                fns,
146            })
147            .collect();
148        Arc::new(TestingFnRegistry {
149            ffqn_to_fn_details,
150            export_hierarchy,
151        })
152    }
153
154    mod populate_codegen_cache {
155        use crate::{
156            activity::activity_worker::tests::compile_activity,
157            workflow::workflow_worker::tests::compile_workflow,
158        };
159
160        #[rstest::rstest(wasm_path => [
161            test_programs_fibo_activity_builder::TEST_PROGRAMS_FIBO_ACTIVITY,
162            test_programs_http_get_activity_builder::TEST_PROGRAMS_HTTP_GET_ACTIVITY,
163            test_programs_sleep_activity_builder::TEST_PROGRAMS_SLEEP_ACTIVITY,
164            ])]
165        #[tokio::test]
166        async fn fibo(wasm_path: &str) {
167            compile_activity(wasm_path).await;
168        }
169
170        #[rstest::rstest(wasm_path => [
171            test_programs_fibo_workflow_builder::TEST_PROGRAMS_FIBO_WORKFLOW,
172            test_programs_http_get_workflow_builder::TEST_PROGRAMS_HTTP_GET_WORKFLOW,
173            test_programs_sleep_workflow_builder::TEST_PROGRAMS_SLEEP_WORKFLOW,
174            ])]
175        #[tokio::test]
176        async fn workflow(wasm_path: &str) {
177            compile_workflow(wasm_path).await;
178        }
179
180        #[cfg(not(madsim))]
181        #[rstest::rstest(wasm_path => [
182            test_programs_fibo_webhook_builder::TEST_PROGRAMS_FIBO_WEBHOOK
183            ])]
184        #[tokio::test]
185        async fn webhook(wasm_path: &str) {
186            crate::webhook::webhook_trigger::tests::nosim::compile_webhook(wasm_path).await;
187        }
188    }
189}