Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,194 @@
|
||||
use crate::{
|
||||
adapter::Document,
|
||||
database::{Binding, Database, Error, Executor, Options, Parameter, Registry},
|
||||
job::{self, Claim, Execution, Policy, Submission},
|
||||
task::{Runtime, Scope},
|
||||
wire::json::Json,
|
||||
};
|
||||
use futures_util::FutureExt;
|
||||
use std::{
|
||||
panic::AssertUnwindSafe,
|
||||
path::PathBuf,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
mod heartbeat;
|
||||
mod lease;
|
||||
mod queue;
|
||||
mod schedule;
|
||||
mod workers;
|
||||
|
||||
const NOW: i64 = 1_700_000_000_000;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Case {
|
||||
database: Database,
|
||||
schema: String,
|
||||
directory: PathBuf,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
async fn registry(&self, scope: &Scope<'_>) -> Arc<Registry> {
|
||||
Registry::load(
|
||||
&self.directory.join("setting.json"),
|
||||
&[Binding {
|
||||
storage: crate::database::Storage::Global,
|
||||
component: "demo",
|
||||
database: Some("default"),
|
||||
table: job::schema::TABLE,
|
||||
schema: job::schema::SQL,
|
||||
plan: None,
|
||||
references: &[],
|
||||
}],
|
||||
scope,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn executor(&self, scope: &Scope<'_>) -> Executor {
|
||||
let mut executor = self.database.executor().await.unwrap();
|
||||
executor
|
||||
.query(
|
||||
scope,
|
||||
"SELECT set_config('search_path',$1,false)",
|
||||
vec![Parameter::Text(self.schema.clone())],
|
||||
1,
|
||||
|row| row.get::<_, String>(0),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
executor
|
||||
}
|
||||
|
||||
async fn count(&self, scope: &Scope<'_>, sql: &'static str) -> i64 {
|
||||
self.executor(scope)
|
||||
.await
|
||||
.query(scope, sql, vec![], 1, |row| row.get::<_, i64>(0))
|
||||
.await
|
||||
.unwrap()[0]
|
||||
}
|
||||
}
|
||||
|
||||
fn fixture(check: impl AsyncFnOnce(&Case, &Scope<'_>)) {
|
||||
let path =
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../config/setting.json");
|
||||
let document = Document::load(&path).expect("cannot read PostgreSQL test settings");
|
||||
let root: serde_json::Value = document.decode().expect("invalid PostgreSQL test settings");
|
||||
let value = root
|
||||
.get("database")
|
||||
.and_then(|database| database.get("postgres_test"))
|
||||
.expect("database.postgres_test must select an isolated test database");
|
||||
let mut options: Options = serde_json::from_value(value.clone())
|
||||
.unwrap_or_else(|_| panic!("invalid PostgreSQL test options"));
|
||||
assert_eq!(options.driver, "postgres");
|
||||
options.max_connections = 8;
|
||||
options.wait_timeout_ms = 1000;
|
||||
options.io_timeout_ms = 5000;
|
||||
Runtime::new(Default::default())
|
||||
.unwrap()
|
||||
.run(async |scope| {
|
||||
let database = Database::open(std::path::Path::new("."), options, scope)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = format!("dever_job_test_{}", job::identifier().unwrap());
|
||||
let directory = std::env::temp_dir().join(&schema);
|
||||
std::fs::create_dir(&directory).unwrap();
|
||||
let case = Case {
|
||||
database,
|
||||
schema,
|
||||
directory,
|
||||
};
|
||||
let mut owner = case.database.executor().await.unwrap();
|
||||
owner
|
||||
.postgres_operation(async |client| {
|
||||
client
|
||||
.batch_execute(&format!("CREATE SCHEMA {}", case.schema))
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let outcome = AssertUnwindSafe(async {
|
||||
use std::{
|
||||
io::Write,
|
||||
os::unix::fs::{OpenOptionsExt, PermissionsExt},
|
||||
};
|
||||
std::fs::set_permissions(&case.directory, std::fs::Permissions::from_mode(0o700))
|
||||
.unwrap();
|
||||
let mut connection = value.clone();
|
||||
connection["max_connections"] = 8.into();
|
||||
let url = connection["url"].as_str().unwrap();
|
||||
assert!(
|
||||
url.parse::<tokio_postgres::Config>()
|
||||
.unwrap()
|
||||
.get_options()
|
||||
.is_none()
|
||||
);
|
||||
let separator = if url.contains('?') { '&' } else { '?' };
|
||||
connection["url"] =
|
||||
format!("{url}{separator}options=-csearch_path%3D{}", case.schema).into();
|
||||
let mut settings = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.mode(0o600)
|
||||
.open(case.directory.join("setting.json"))
|
||||
.unwrap();
|
||||
settings
|
||||
.write_all(
|
||||
&serde_json::to_vec(
|
||||
&serde_json::json!({"database":{"default":connection}}),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut executor = case.executor(scope).await;
|
||||
executor
|
||||
.schema(scope, job::schema::TABLE, job::schema::SQL)
|
||||
.await
|
||||
.unwrap();
|
||||
executor
|
||||
.schema(scope, job::schema::TABLE, job::schema::SQL)
|
||||
.await
|
||||
.unwrap();
|
||||
drop(executor);
|
||||
tokio::time::timeout(Duration::from_secs(30), check(&case, scope))
|
||||
.await
|
||||
.expect("PostgreSQL Job test deadline exceeded");
|
||||
})
|
||||
.catch_unwind()
|
||||
.await;
|
||||
let cleanup = owner
|
||||
.postgres_operation(async |client| {
|
||||
client
|
||||
.batch_execute(&format!("DROP SCHEMA {} CASCADE", case.schema))
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
})
|
||||
.await;
|
||||
case.database.close();
|
||||
std::fs::remove_dir_all(&case.directory).unwrap();
|
||||
cleanup.expect("PostgreSQL Job test schema cleanup failed");
|
||||
if let Err(panic) = outcome {
|
||||
std::panic::resume_unwind(panic);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn submission(key: &str) -> Submission<'_> {
|
||||
static PAYLOAD: LazyLock<Json> = LazyLock::new(|| Json::parse(r#"{"number":1}"#).unwrap());
|
||||
Submission {
|
||||
target: "demo.account.publish",
|
||||
payload: Some(&PAYLOAD),
|
||||
schema: "schema-1",
|
||||
execution: &Execution::System { tenant: None },
|
||||
key,
|
||||
policy: Policy {
|
||||
retries: 2,
|
||||
timeout_ms: 50,
|
||||
},
|
||||
at: NOW,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user