mirror of
https://github.com/hanzo-ds/rs.git
synced 2026-10-30 20:49:56 +00:00
Cut from the upstream v0.13.3 release tag (not HEAD) so this is a pure
rename with no behaviour change. v0.13.3 is what our consumers already
resolve to: insights/rust's Cargo.lock pins clickhouse 0.13.3 and its
manifest already declares `datastore` at 0.13.x.
The load-bearing change is the HTTP auth header rename. The Datastore
server reads only X-Datastore-User / X-Datastore-Key; the upstream names
return `401 Code: 194 Authentication failed`. Every un-renamed HTTP
client is therefore unable to authenticate at all — this is a
correctness fix, not branding.
- crate `clickhouse` -> `datastore`, `clickhouse-derive` -> `datastore-derive`;
versions preserved exactly (0.13.3 / 0.2.0).
- derive macro emits `datastore::Row` in lockstep, so downstream
`#[derive(Row)]` keeps resolving.
- User-Agent reports `datastore-rs/{version}`.
- test-harness env vars CLICKHOUSE_* -> DATASTORE_*; compose uses
ghcr.io/hanzoai/datastore with /etc/datastore-server config paths.
- doc links point at docs.hanzo.ai/datastore.
- `publish = false`: the `datastore` name on crates.io belongs to an
unrelated crate, so consume this by git tag, never by version range.
CHANGELOG is truncated to a fork notice rather than find-replaced —
rewriting upstream release history would manufacture changes Datastore
never shipped. References to upstream issues keep their numbers.
172 lines
5.1 KiB
Rust
172 lines
5.1 KiB
Rust
//! ## Integration tests
|
|
//!
|
|
//! - The `wait_end_of_query` setting that is used for all DDLs forces HTTP response buffering.
|
|
//! We will get the response only when the DDL is executed on every cluster node.
|
|
//! See also: https://docs.hanzo.ai/datastore/interfaces/http/#response-buffering
|
|
//!
|
|
//! - When tests are executed against Hanzo Datastore Cloud, `ENGINE = MergeTree`
|
|
//! is automatically replaced with `ENGINE = SharedMergeTree` by the server.
|
|
//! In this case, no modifications to the DDLs are required, unlike on-premise clusters.
|
|
//!
|
|
//! - `DATASTORE_TEST_ENVIRONMENT` env variable determines whether we are going to use
|
|
//! a local Datastore instance in Docker (`local`, default value) or Hanzo Datastore Cloud (`cloud`).
|
|
//! NB: `cloud` will require one of the TLS features to be enabled.
|
|
//!
|
|
//! - Datastore server credentials are set via `DATASTORE_CLOUD_HOST`, `DATASTORE_CLOUD_USER`,
|
|
//! and `DATASTORE_CLOUD_PASSWORD`. Specific Cloud-only tests might also require JWT access token,
|
|
//! which should be provided via `DATASTORE_CLOUD_JWT_ACCESS_TOKEN`.
|
|
//!
|
|
//! - Created database names should match the following template: `chrs__{...}__{unix_millis}`.
|
|
//! This allows to simply clean up the databases from the Cloud instance based on its creation time.
|
|
//! See [`_priv::make_db_name`].
|
|
|
|
use datastore::{sql::Identifier, Client, Row};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
macro_rules! prepare_database {
|
|
() => {
|
|
crate::_priv::prepare_database({
|
|
fn f() {}
|
|
fn type_name_of_val<T>(_: T) -> &'static str {
|
|
std::any::type_name::<T>()
|
|
}
|
|
type_name_of_val(f)
|
|
})
|
|
.await
|
|
};
|
|
}
|
|
|
|
macro_rules! check_cloud_test_env {
|
|
() => {
|
|
match std::env::var("DATASTORE_TEST_ENVIRONMENT") {
|
|
Ok(test_env) if test_env == "cloud" => (),
|
|
_ => {
|
|
eprintln!("Skipping test as it is Cloud only");
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub(crate) fn get_client() -> Client {
|
|
let client = Client::default();
|
|
match std::env::var("DATASTORE_TEST_ENVIRONMENT") {
|
|
Ok(test_env) if test_env == "cloud" => client
|
|
.with_url(get_cloud_url())
|
|
.with_user("default")
|
|
.with_password(require_env_var("DATASTORE_CLOUD_PASSWORD")),
|
|
_ => client.with_url("http://localhost:8123"),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn require_env_var(name: &str) -> String {
|
|
std::env::var(name).unwrap_or_else(|_| panic!("{name} environment variable is not set"))
|
|
}
|
|
|
|
pub(crate) fn get_cloud_url() -> String {
|
|
let hostname = require_env_var("DATASTORE_CLOUD_HOST");
|
|
format!("https://{hostname}:8443")
|
|
}
|
|
|
|
#[derive(Debug, Row, Serialize, Deserialize, PartialEq)]
|
|
struct SimpleRow {
|
|
id: u64,
|
|
data: String,
|
|
}
|
|
|
|
impl SimpleRow {
|
|
fn new(id: u64, data: impl ToString) -> Self {
|
|
Self {
|
|
id,
|
|
data: data.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn create_simple_table(client: &Client, table_name: &str) {
|
|
client
|
|
.query("CREATE TABLE ?(id UInt64, data String) ENGINE = MergeTree ORDER BY id")
|
|
.with_option("wait_end_of_query", "1")
|
|
.bind(Identifier(table_name))
|
|
.execute()
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
async fn fetch_rows<T>(client: &Client, table_name: &str) -> Vec<T>
|
|
where
|
|
T: Row + for<'b> Deserialize<'b>,
|
|
{
|
|
client
|
|
.query("SELECT ?fields FROM ?")
|
|
.bind(Identifier(table_name))
|
|
.fetch_all::<T>()
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
async fn flush_query_log(client: &Client) {
|
|
client.query("SYSTEM FLUSH LOGS").execute().await.unwrap();
|
|
}
|
|
|
|
mod chrono;
|
|
mod cloud_jwt;
|
|
mod compression;
|
|
mod cursor_error;
|
|
mod cursor_stats;
|
|
mod fetch_bytes;
|
|
mod insert;
|
|
mod inserter;
|
|
mod int128;
|
|
mod ip;
|
|
mod mock;
|
|
mod nested;
|
|
mod query;
|
|
mod time;
|
|
mod user_agent;
|
|
mod uuid;
|
|
mod variant;
|
|
mod watch;
|
|
|
|
mod _priv {
|
|
use super::*;
|
|
use std::time::SystemTime;
|
|
|
|
pub(crate) async fn prepare_database(fn_path: &str) -> Client {
|
|
let db_name = make_db_name(fn_path);
|
|
let client = get_client();
|
|
|
|
client
|
|
.query("DROP DATABASE IF EXISTS ?")
|
|
.with_option("wait_end_of_query", "1")
|
|
.bind(Identifier(&db_name))
|
|
.execute()
|
|
.await
|
|
.unwrap_or_else(|err| panic!("cannot drop db {db_name}, cause: {err}"));
|
|
|
|
client
|
|
.query("CREATE DATABASE ?")
|
|
.with_option("wait_end_of_query", "1")
|
|
.bind(Identifier(&db_name))
|
|
.execute()
|
|
.await
|
|
.unwrap_or_else(|err| panic!("cannot create db {db_name}, cause: {err}"));
|
|
|
|
println!("Created database {db_name}");
|
|
client.with_database(db_name)
|
|
}
|
|
|
|
// `it::compression::lz4::{{closure}}::f` -> `chrs__compression__lz4__{unix_millis}`
|
|
fn make_db_name(fn_path: &str) -> String {
|
|
assert!(fn_path.starts_with("it::"));
|
|
let mut iter = fn_path.split("::").skip(1);
|
|
let module = iter.next().unwrap();
|
|
let test = iter.next().unwrap();
|
|
let now_unix_millis = SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_millis();
|
|
format!("chrs__{module}__{test}__{now_unix_millis}")
|
|
}
|
|
}
|