1- use anyhow:: Context ;
1+ use anyhow:: Context as _ ;
22use spin_factor_key_value:: {
3- runtime_config:: spin:: { MakeKeyValueStore , RuntimeConfigResolver , StoreConfig } ,
3+ runtime_config:: spin:: { MakeKeyValueStore , RuntimeConfigResolver } ,
44 KeyValueFactor , RuntimeConfig ,
55} ;
66use spin_factor_key_value_redis:: RedisKeyValueStore ;
7- use spin_factor_key_value_spin:: { SpinKeyValueRuntimeConfig , SpinKeyValueStore } ;
7+ use spin_factor_key_value_spin:: SpinKeyValueStore ;
88use spin_factors:: { FactorRuntimeConfigSource , RuntimeConfigSourceFinalizer , RuntimeFactors } ;
99use spin_factors_test:: { toml, TestEnvironment } ;
10+ use spin_world:: v2:: key_value:: HostStore ;
1011use std:: { collections:: HashSet , sync:: Arc } ;
1112
1213#[ derive( RuntimeFactors ) ]
1314struct TestFactors {
1415 key_value : KeyValueFactor ,
1516}
1617
17- fn default_key_value_resolver ( ) -> anyhow:: Result < ( RuntimeConfigResolver , tempdir:: TempDir ) > {
18- let mut test_resolver = RuntimeConfigResolver :: new ( ) ;
19- test_resolver. register_store_type ( SpinKeyValueStore :: new (
20- std:: env:: current_dir ( ) . context ( "failed to get current directory" ) ?,
21- ) ) ?;
22- let tmp_dir = tempdir:: TempDir :: new ( "example" ) ?;
23- let path = tmp_dir. path ( ) . to_path_buf ( ) ;
24- let default_config = SpinKeyValueRuntimeConfig :: default ( Some ( path) ) ;
25- let store_config = StoreConfig :: new (
26- SpinKeyValueStore :: RUNTIME_CONFIG_TYPE . to_string ( ) ,
27- default_config,
28- ) ?;
29- test_resolver. add_default_store ( "default" , store_config) ;
30- Ok ( ( test_resolver, tmp_dir) )
31- }
32-
3318#[ tokio:: test]
3419async fn default_key_value_works ( ) -> anyhow:: Result < ( ) > {
35- let ( test_resolver, dir) = default_key_value_resolver ( ) ?;
20+ let mut test_resolver = RuntimeConfigResolver :: new ( ) ;
21+ test_resolver. register_store_type ( SpinKeyValueStore :: new ( None ) ) ?;
22+ test_resolver. add_default_store :: < SpinKeyValueStore > ( "default" , Default :: default ( ) ) ?;
3623 let factors = TestFactors {
3724 key_value : KeyValueFactor :: new ( test_resolver) ,
3825 } ;
@@ -47,16 +34,14 @@ async fn default_key_value_works() -> anyhow::Result<()> {
4734 state. key_value. allowed_stores( ) ,
4835 & [ "default" . into( ) ] . into_iter( ) . collect:: <HashSet <_>>( )
4936 ) ;
50- // Ensure the database directory is created
51- assert ! ( dir. path( ) . exists( ) ) ;
5237 Ok ( ( ) )
5338}
5439
5540async fn run_test_with_config_and_stores_for_label (
5641 runtime_config : Option < toml:: Table > ,
5742 store_types : Vec < impl MakeKeyValueStore > ,
5843 labels : Vec < & str > ,
59- ) -> anyhow:: Result < ( ) > {
44+ ) -> anyhow:: Result < TestFactorsInstanceState > {
6045 let mut test_resolver = RuntimeConfigResolver :: new ( ) ;
6146 for store_type in store_types {
6247 test_resolver. register_store_type ( store_type) ?;
@@ -79,7 +64,7 @@ async fn run_test_with_config_and_stores_for_label(
7964 state. key_value. allowed_stores( ) . iter( ) . collect:: <Vec <_>>( )
8065 ) ;
8166
82- Ok ( ( ) )
67+ Ok ( state )
8368}
8469
8570#[ tokio:: test]
@@ -94,7 +79,8 @@ async fn overridden_default_key_value_works() -> anyhow::Result<()> {
9479 vec ! [ RedisKeyValueStore :: new( ) ] ,
9580 vec ! [ "default" ] ,
9681 )
97- . await
82+ . await ?;
83+ Ok ( ( ) )
9884}
9985
10086#[ tokio:: test]
@@ -105,52 +91,69 @@ async fn custom_spin_key_value_works() -> anyhow::Result<()> {
10591 } ;
10692 run_test_with_config_and_stores_for_label (
10793 Some ( runtime_config) ,
108- vec ! [ SpinKeyValueStore :: new(
109- std:: env:: current_dir( ) . context( "failed to get current directory" ) ?,
110- ) ] ,
94+ vec ! [ SpinKeyValueStore :: new( None ) ] ,
11195 vec ! [ "custom" ] ,
11296 )
113- . await
97+ . await ?;
98+ Ok ( ( ) )
11499}
115100
116- #[ tokio:: test]
101+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
117102async fn custom_spin_key_value_works_with_absolute_path ( ) -> anyhow:: Result < ( ) > {
118103 let tmp_dir = tempdir:: TempDir :: new ( "example" ) ?;
119- let path = tmp_dir. path ( ) . join ( "custom.db" ) ;
120- let path_str = path. to_str ( ) . unwrap ( ) ;
104+ let db_path = tmp_dir. path ( ) . join ( "foo/custom.db" ) ;
105+ // Check that the db does not exist yet - it will exist by the end of the test
106+ assert ! ( !db_path. exists( ) ) ;
107+
108+ let path_str = db_path. to_str ( ) . unwrap ( ) ;
121109 let runtime_config = toml:: toml! {
122110 [ key_value_store. custom]
123111 type = "spin"
124112 path = path_str
125113 } ;
126- run_test_with_config_and_stores_for_label (
114+ let mut state = run_test_with_config_and_stores_for_label (
127115 Some ( runtime_config) ,
128- vec ! [ SpinKeyValueStore :: new(
116+ vec ! [ SpinKeyValueStore :: new( Some (
129117 std:: env:: current_dir( ) . context( "failed to get current directory" ) ?,
130- ) ] ,
118+ ) ) ] ,
131119 vec ! [ "custom" ] ,
132120 )
133121 . await ?;
134- assert ! ( tmp_dir. path( ) . exists( ) ) ;
122+
123+ // Actually et a key since store creation is lazy
124+ let store = state. key_value . open ( "custom" . to_owned ( ) ) . await ??;
125+ let _ = state. key_value . get ( store, "foo" . to_owned ( ) ) . await ??;
126+
127+ // Check that the parent has been created
128+ assert ! ( db_path. exists( ) ) ;
135129 Ok ( ( ) )
136130}
137131
138- #[ tokio:: test]
132+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
139133async fn custom_spin_key_value_works_with_relative_path ( ) -> anyhow:: Result < ( ) > {
140134 let tmp_dir = tempdir:: TempDir :: new ( "example" ) ?;
141- let path = tmp_dir. path ( ) . to_owned ( ) ;
135+ let db_path = tmp_dir. path ( ) . join ( "custom.db" ) ;
136+ // Check that the db does not exist yet - it will exist by the end of the test
137+ assert ! ( !db_path. exists( ) ) ;
138+
142139 let runtime_config = toml:: toml! {
143140 [ key_value_store. custom]
144141 type = "spin"
145142 path = "custom.db"
146143 } ;
147- run_test_with_config_and_stores_for_label (
144+ let mut state = run_test_with_config_and_stores_for_label (
148145 Some ( runtime_config) ,
149- vec ! [ SpinKeyValueStore :: new( path) ] ,
146+ vec ! [ SpinKeyValueStore :: new( Some ( tmp_dir . path( ) . to_owned ( ) ) ) ] ,
150147 vec ! [ "custom" ] ,
151148 )
152149 . await ?;
153- assert ! ( tmp_dir. path( ) . exists( ) ) ;
150+
151+ // Actually et a key since store creation is lazy
152+ let store = state. key_value . open ( "custom" . to_owned ( ) ) . await ??;
153+ let _ = state. key_value . get ( store, "foo" . to_owned ( ) ) . await ??;
154+
155+ // Check that the correct store in the config was chosen by verifying the existence of the DB
156+ assert ! ( db_path. exists( ) ) ;
154157 Ok ( ( ) )
155158}
156159
@@ -166,64 +169,75 @@ async fn custom_redis_key_value_works() -> anyhow::Result<()> {
166169 vec ! [ RedisKeyValueStore :: new( ) ] ,
167170 vec ! [ "custom" ] ,
168171 )
169- . await
172+ . await ?;
173+ Ok ( ( ) )
170174}
171175
172176#[ tokio:: test]
173177async fn misconfigured_spin_key_value_fails ( ) -> anyhow:: Result < ( ) > {
178+ let tmp_dir = tempdir:: TempDir :: new ( "example" ) ?;
174179 let runtime_config = toml:: toml! {
175180 [ key_value_store. custom]
176181 type = "spin"
177182 path = "/$$&/bad/path/foo.db"
178183 } ;
179- assert ! ( run_test_with_config_and_stores_for_label(
184+ let result = run_test_with_config_and_stores_for_label (
180185 Some ( runtime_config) ,
181- vec![ SpinKeyValueStore :: new(
182- std:: env:: current_dir( ) . context( "failed to get current directory" ) ?
183- ) ] ,
184- vec![ "custom" ]
186+ vec ! [ SpinKeyValueStore :: new( Some ( tmp_dir. path( ) . to_owned( ) ) ) ] ,
187+ vec ! [ "custom" ] ,
185188 )
186- . await
187- . is_err( ) ) ;
189+ . await ;
190+ // TODO(rylev): This only fails on my machine due to a read-only file system error.
191+ // We should consider adding a check for the error message.
192+ assert ! ( result. is_err( ) ) ;
188193 Ok ( ( ) )
189194}
190195
191- #[ tokio:: test]
192- async fn multiple_custom_key_value_uses_first_store ( ) -> anyhow:: Result < ( ) > {
196+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
197+ // TODO(rylev): consider removing this test as it is really only a consequence of
198+ // toml deserialization and not a feature of the key-value store.
199+ async fn multiple_custom_key_value_uses_second_store ( ) -> anyhow:: Result < ( ) > {
193200 let tmp_dir = tempdir:: TempDir :: new ( "example" ) ?;
201+ let db_path = tmp_dir. path ( ) . join ( "custom.db" ) ;
202+ // Check that the db does not exist yet - it will exist by the end of the test
203+ assert ! ( !db_path. exists( ) ) ;
204+
194205 let mut test_resolver = RuntimeConfigResolver :: new ( ) ;
195206 test_resolver. register_store_type ( RedisKeyValueStore :: new ( ) ) ?;
196- test_resolver. register_store_type ( SpinKeyValueStore :: new ( tmp_dir. path ( ) . to_owned ( ) ) ) ?;
207+ test_resolver. register_store_type ( SpinKeyValueStore :: new ( Some ( tmp_dir. path ( ) . to_owned ( ) ) ) ) ?;
197208 let test_resolver = Arc :: new ( test_resolver) ;
198209 let factors = TestFactors {
199210 key_value : KeyValueFactor :: new ( test_resolver. clone ( ) ) ,
200211 } ;
212+ let runtime_config = toml:: toml! {
213+ [ key_value_store. custom]
214+ type = "redis"
215+ url = "redis://localhost:6379"
216+
217+ [ key_value_store. custom]
218+ type = "spin"
219+ path = "custom.db"
220+
221+ } ;
201222 let env = TestEnvironment :: new ( factors)
202223 . extend_manifest ( toml ! {
203224 [ component. test-component]
204225 source = "does-not-exist.wasm"
205226 key_value_stores = [ "custom" ]
206227 } )
207- . runtime_config ( TomlConfig :: new (
208- test_resolver,
209- Some ( toml:: toml! {
210- [ key_value_store. custom]
211- type = "spin"
212- path = "custom.db"
228+ . runtime_config ( TomlConfig :: new ( test_resolver, Some ( runtime_config) ) ) ?;
229+ let mut state = env. build_instance_state ( ) . await ?;
213230
214- [ key_value_store. custom]
215- type = "redis"
216- url = "redis://localhost:6379"
217- } ) ,
218- ) ) ?;
219- let state = env. build_instance_state ( ) . await ?;
231+ // Actually et a key since store creation is lazy
232+ let store = state. key_value . open ( "custom" . to_owned ( ) ) . await ??;
233+ let _ = state. key_value . get ( store, "foo" . to_owned ( ) ) . await ??;
220234
221235 assert_eq ! (
222236 state. key_value. allowed_stores( ) ,
223237 & [ "custom" . into( ) ] . into_iter( ) . collect:: <HashSet <_>>( )
224238 ) ;
225- // Check that the first store in the config was chosen by verifying the existence of the DB directory
226- assert ! ( tmp_dir . path ( ) . exists( ) ) ;
239+ // Check that the correct store in the config was chosen by verifying the existence of the DB
240+ assert ! ( db_path . exists( ) ) ;
227241 Ok ( ( ) )
228242}
229243
0 commit comments