|
1 | | -use std::{collections::HashSet, sync::Arc}; |
| 1 | +use std::{ |
| 2 | + collections::{HashMap, HashSet}, |
| 3 | + sync::Arc, |
| 4 | +}; |
2 | 5 |
|
3 | | -use spin_factor_sqlite::SqliteFactor; |
| 6 | +use spin_factor_sqlite::{RuntimeConfig, SqliteFactor}; |
4 | 7 | use spin_factors::{ |
5 | | - anyhow::{self, bail, Context}, |
6 | | - runtime_config::toml::TomlKeyTracker, |
7 | | - Factor, FactorRuntimeConfigSource, RuntimeConfigSourceFinalizer, RuntimeFactors, |
| 8 | + anyhow::{self, bail, Context as _}, |
| 9 | + RuntimeFactors, |
8 | 10 | }; |
9 | 11 | use spin_factors_test::{toml, TestEnvironment}; |
10 | | -use spin_sqlite::RuntimeConfigResolver; |
11 | | -use spin_world::async_trait; |
| 12 | +use spin_world::{async_trait, v2::sqlite as v2}; |
| 13 | +use v2::HostConnection as _; |
12 | 14 |
|
13 | 15 | #[derive(RuntimeFactors)] |
14 | 16 | struct TestFactors { |
15 | 17 | sqlite: SqliteFactor, |
16 | 18 | } |
17 | 19 |
|
18 | 20 | #[tokio::test] |
19 | | -async fn sqlite_works() -> anyhow::Result<()> { |
| 21 | +async fn errors_when_non_configured_database_used() -> anyhow::Result<()> { |
20 | 22 | let factors = TestFactors { |
21 | 23 | sqlite: SqliteFactor::new(), |
22 | 24 | }; |
23 | 25 | let env = TestEnvironment::new(factors).extend_manifest(toml! { |
24 | 26 | [component.test-component] |
25 | 27 | source = "does-not-exist.wasm" |
26 | | - sqlite_databases = ["default"] |
| 28 | + sqlite_databases = ["foo"] |
27 | 29 | }); |
28 | | - let state = env.build_instance_state().await?; |
| 30 | + let Err(err) = env.build_instance_state().await else { |
| 31 | + bail!("Expected build_instance_state to error but it did not"); |
| 32 | + }; |
29 | 33 |
|
30 | | - assert_eq!( |
31 | | - state.sqlite.allowed_databases(), |
32 | | - &["default".into()].into_iter().collect::<HashSet<_>>() |
33 | | - ); |
| 34 | + assert!(err |
| 35 | + .to_string() |
| 36 | + .contains("One or more components use SQLite databases which are not defined.")); |
34 | 37 |
|
35 | 38 | Ok(()) |
36 | 39 | } |
37 | 40 |
|
38 | 41 | #[tokio::test] |
39 | | -async fn errors_when_non_configured_database_used() -> anyhow::Result<()> { |
| 42 | +async fn errors_when_database_not_allowed() -> anyhow::Result<()> { |
40 | 43 | let factors = TestFactors { |
41 | 44 | sqlite: SqliteFactor::new(), |
42 | 45 | }; |
43 | 46 | let env = TestEnvironment::new(factors).extend_manifest(toml! { |
44 | 47 | [component.test-component] |
45 | 48 | source = "does-not-exist.wasm" |
46 | | - sqlite_databases = ["foo"] |
| 49 | + sqlite_databases = [] |
47 | 50 | }); |
48 | | - let Err(err) = env.build_instance_state().await else { |
49 | | - bail!("Expected build_instance_state to error but it did not"); |
50 | | - }; |
| 51 | + let mut state = env |
| 52 | + .build_instance_state() |
| 53 | + .await |
| 54 | + .context("build_instance_state failed")?; |
51 | 55 |
|
52 | | - assert!(err |
53 | | - .to_string() |
54 | | - .contains("One or more components use SQLite databases which are not defined.")); |
| 56 | + assert!(matches!( |
| 57 | + state.sqlite.open("foo".into()).await, |
| 58 | + Err(spin_world::v2::sqlite::Error::AccessDenied) |
| 59 | + )); |
55 | 60 |
|
56 | 61 | Ok(()) |
57 | 62 | } |
58 | 63 |
|
59 | 64 | #[tokio::test] |
60 | | -async fn no_error_when_database_is_configured() -> anyhow::Result<()> { |
| 65 | +async fn it_works_when_database_is_configured() -> anyhow::Result<()> { |
61 | 66 | let factors = TestFactors { |
62 | 67 | sqlite: SqliteFactor::new(), |
63 | 68 | }; |
64 | | - let runtime_config = toml! { |
65 | | - [sqlite_database.foo] |
66 | | - type = "spin" |
| 69 | + let mut connection_creators = HashMap::new(); |
| 70 | + connection_creators.insert("foo".to_owned(), Arc::new(MockConnectionCreator) as _); |
| 71 | + let runtime_config = TestFactorsRuntimeConfig { |
| 72 | + sqlite: Some(RuntimeConfig { |
| 73 | + connection_creators, |
| 74 | + }), |
67 | 75 | }; |
68 | | - let sqlite_config = RuntimeConfigResolver::new(None, "/".into()); |
69 | 76 | let env = TestEnvironment::new(factors) |
70 | 77 | .extend_manifest(toml! { |
71 | 78 | [component.test-component] |
72 | 79 | source = "does-not-exist.wasm" |
73 | 80 | sqlite_databases = ["foo"] |
74 | 81 | }) |
75 | | - .runtime_config(TomlRuntimeSource::new(&runtime_config, sqlite_config))?; |
76 | | - env.build_instance_state() |
| 82 | + .runtime_config(runtime_config)?; |
| 83 | + |
| 84 | + let mut state = env |
| 85 | + .build_instance_state() |
77 | 86 | .await |
78 | 87 | .context("build_instance_state failed")?; |
79 | | - Ok(()) |
80 | | -} |
81 | | - |
82 | | -struct TomlRuntimeSource<'a> { |
83 | | - table: TomlKeyTracker<'a>, |
84 | | - runtime_config_resolver: RuntimeConfigResolver, |
85 | | -} |
86 | | - |
87 | | -impl<'a> TomlRuntimeSource<'a> { |
88 | | - fn new(table: &'a toml::Table, runtime_config_resolver: RuntimeConfigResolver) -> Self { |
89 | | - Self { |
90 | | - table: TomlKeyTracker::new(table), |
91 | | - runtime_config_resolver, |
92 | | - } |
93 | | - } |
94 | | -} |
95 | 88 |
|
96 | | -impl FactorRuntimeConfigSource<SqliteFactor> for TomlRuntimeSource<'_> { |
97 | | - fn get_runtime_config( |
98 | | - &mut self, |
99 | | - ) -> anyhow::Result<Option<<SqliteFactor as Factor>::RuntimeConfig>> { |
100 | | - self.runtime_config_resolver.resolve_from_toml(&self.table) |
101 | | - } |
102 | | -} |
| 89 | + assert_eq!( |
| 90 | + state.sqlite.allowed_databases(), |
| 91 | + &["foo".into()].into_iter().collect::<HashSet<_>>() |
| 92 | + ); |
103 | 93 |
|
104 | | -impl RuntimeConfigSourceFinalizer for TomlRuntimeSource<'_> { |
105 | | - fn finalize(&mut self) -> anyhow::Result<()> { |
106 | | - self.table.validate_all_keys_used()?; |
107 | | - Ok(()) |
108 | | - } |
| 94 | + assert!(state.sqlite.open("foo".into()).await.is_ok()); |
| 95 | + Ok(()) |
109 | 96 | } |
110 | 97 |
|
111 | | -impl TryFrom<TomlRuntimeSource<'_>> for TestFactorsRuntimeConfig { |
112 | | - type Error = anyhow::Error; |
| 98 | +/// A connection creator that returns a mock connection. |
| 99 | +struct MockConnectionCreator; |
113 | 100 |
|
114 | | - fn try_from(value: TomlRuntimeSource<'_>) -> Result<Self, Self::Error> { |
115 | | - Self::from_source(value) |
| 101 | +#[async_trait] |
| 102 | +impl spin_factor_sqlite::ConnectionCreator for MockConnectionCreator { |
| 103 | + async fn create_connection( |
| 104 | + &self, |
| 105 | + label: &str, |
| 106 | + ) -> Result<Box<dyn spin_factor_sqlite::Connection + 'static>, v2::Error> { |
| 107 | + let _ = label; |
| 108 | + Ok(Box::new(MockConnection)) |
116 | 109 | } |
117 | 110 | } |
118 | 111 |
|
119 | | -/// A connection creator that always returns an error. |
120 | | -struct InvalidConnectionCreator; |
| 112 | +/// A mock connection that always errors. |
| 113 | +struct MockConnection; |
121 | 114 |
|
122 | 115 | #[async_trait] |
123 | | -impl spin_factor_sqlite::ConnectionCreator for InvalidConnectionCreator { |
124 | | - async fn create_connection( |
| 116 | +impl spin_factor_sqlite::Connection for MockConnection { |
| 117 | + async fn query( |
125 | 118 | &self, |
126 | | - label: &str, |
127 | | - ) -> Result<Box<dyn spin_factor_sqlite::Connection + 'static>, spin_world::v2::sqlite::Error> |
128 | | - { |
129 | | - let _ = label; |
130 | | - Err(spin_world::v2::sqlite::Error::InvalidConnection) |
| 119 | + query: &str, |
| 120 | + parameters: Vec<v2::Value>, |
| 121 | + ) -> Result<v2::QueryResult, v2::Error> { |
| 122 | + let _ = (query, parameters); |
| 123 | + Err(v2::Error::Io("Mock connection".into())) |
| 124 | + } |
| 125 | + |
| 126 | + async fn execute_batch(&self, statements: &str) -> anyhow::Result<()> { |
| 127 | + let _ = statements; |
| 128 | + bail!("Mock connection") |
131 | 129 | } |
132 | 130 | } |
0 commit comments