|
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