Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,46 @@ echo
mise --env tcp run postgres:setup
mise --env tls run postgres:setup

echo
echo '###############################################'
echo '# Test: Prometheus'
echo '###############################################'
echo

mise --env tcp run proxy:up proxy --extra-args "--detach --wait"
mise --env tcp run test:wait_for_postgres_to_quack --port 6432 --max-retries 20
mise --env tcp run test:integration:prometheus
mise --env tcp run proxy:down

echo
echo '###############################################'
echo '# Test: non-TLS'
echo '###############################################'
echo

mise --env tcp run proxy:up proxy --extra-args "--detach --wait"
mise --env tcp run test:wait_for_postgres_to_quack --port 6432 --max-retries 20
mise --env tcp run test:integration:psql-tcp
mise --env tcp run proxy:down

echo
echo '###############################################'
echo '# Test: TLS'
echo '###############################################'
echo

mise --env tls run proxy:up proxy-tls --extra-args "--detach --wait"
mise --env tls run test:wait_for_postgres_to_quack --port 6432 --max-retries 20 --tls
mise --env tls run test:integration:psql-tls
mise --env tls run proxy:down


echo
echo '###############################################'
echo '# Test: Integration'
echo '###############################################'
echo

mise --env tls e
mise --env tls run proxy:up proxy-tls --extra-args "--detach --wait"
mise --env tls run test:wait_for_postgres_to_quack --port 6432 --max-retries 20 --tls
cargo nextest run --no-fail-fast --nocapture -E 'package(cipherstash-proxy-integration)'
Expand Down
16 changes: 16 additions & 0 deletions packages/cipherstash-proxy-integration/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ pub fn random_id() -> i64 {
rng.random_range(1..=i64::MAX)
}

// Limited by valid data range
pub fn random_limited() -> i32 {
use rand::Rng;
let mut rng = rand::rng();
rng.random_range(1..=31)
}

pub fn random_string() -> String {
rand::rng()
.sample_iter(&Alphanumeric)
Expand Down Expand Up @@ -126,6 +133,15 @@ pub async fn query<T: for<'a> tokio_postgres::types::FromSql<'a> + Send + Sync>(
rows.iter().map(|row| row.get(0)).collect::<Vec<T>>()
}

pub async fn query_by<T>(sql: &str, param: &(dyn ToSql + Sync)) -> Vec<T>
where
T: for<'a> tokio_postgres::types::FromSql<'a> + Send + Sync,
{
let client = connect_with_tls(PROXY).await;
let rows = client.query(sql, &[param]).await.unwrap();
rows.iter().map(|row| row.get(0)).collect::<Vec<T>>()
}

pub async fn simple_query<T: std::str::FromStr>(sql: &str) -> Vec<T>
where
<T as std::str::FromStr>::Err: std::fmt::Debug,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#[cfg(test)]
mod tests {
use crate::common::{clear, insert, query_by, random_id, random_limited, trace};
use chrono::NaiveDate;
use serde_json::Value;

macro_rules! test_insert_with_literal {
($name: ident, $type: ident, $pg_type: ident) => {
#[tokio::test]
pub async fn $name() {
trace();

clear().await;

let id = random_id();

let encrypted_col = format!("encrypted_{}", stringify!($pg_type));
let encrypted_val = crate::value_for_type!($type, random_limited());

let sql = format!("INSERT INTO encrypted (id, {encrypted_col}) VALUES ($1, '{encrypted_val}')");
insert(&sql, &[&id]).await;

let expected = vec![encrypted_val];

let sql = format!("SELECT {encrypted_col} FROM encrypted WHERE id = $1");

let actual = query_by::<$type>(&sql, &id).await;

assert_eq!(expected, actual);
}
};
}

test_insert_with_literal!(insert_with_literal_int2, i16, int2);
test_insert_with_literal!(insert_with_literal_int4, i32, int4);
test_insert_with_literal!(insert_with_literal_int8, i64, int8);
test_insert_with_literal!(insert_with_literal_float8, f64, float8);
test_insert_with_literal!(insert_with_literal_bool, bool, bool);
test_insert_with_literal!(insert_with_literal_text, String, text);
test_insert_with_literal!(insert_with_literal_date, NaiveDate, date);
test_insert_with_literal!(insert_with_literal_jsonb, Value, jsonb);

// -----------------------------------------------------------------

/// Sanity check insert of unencrypted literal value
#[tokio::test]
pub async fn insert_with_literal_plaintext() {
trace();

clear().await;

let id = random_id();

let encrypted_val = crate::value_for_type!(String, random_limited());

let sql = format!("INSERT INTO encrypted (id, plaintext) VALUES ($1, '{encrypted_val}')");
insert(&sql, &[&id]).await;

let expected = vec![encrypted_val];

let sql = "SELECT plaintext FROM encrypted WHERE id = $1";

let actual = query_by::<String>(sql, &id).await;

assert_eq!(expected, actual);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[cfg(test)]
mod tests {
use crate::common::{clear, insert, query_by, random_id, trace};
use chrono::NaiveDate;
use serde_json::Value;

macro_rules! test_insert_with_null_literal {
($name: ident, $type: ident, $pg_type: ident) => {
#[tokio::test]
pub async fn $name() {
trace();

clear().await;

let id = random_id();

let encrypted_col = format!("encrypted_{}", stringify!($pg_type));
let encrypted_val: Option<$type> = None;

let sql = format!("INSERT INTO encrypted (id, {encrypted_col}) VALUES ($1, NULL)");
insert(&sql, &[&id]).await;

let expected = vec![encrypted_val];

let sql = format!("SELECT {encrypted_col} FROM encrypted WHERE id = $1");

let actual = query_by::<Option<$type>>(&sql, &id).await;

assert_eq!(expected, actual);
}
};
}

test_insert_with_null_literal!(insert_with_null_literal_int2, i16, int2);
test_insert_with_null_literal!(insert_with_null_literal_int4, i32, int4);
test_insert_with_null_literal!(insert_with_null_literal_int8, i64, int8);
test_insert_with_null_literal!(insert_with_null_literal_float8, f64, float8);
test_insert_with_null_literal!(insert_with_null_literal_bool, bool, bool);
test_insert_with_null_literal!(insert_with_null_literal_text, String, text);
test_insert_with_null_literal!(insert_with_null_literal_date, NaiveDate, date);
test_insert_with_null_literal!(insert_with_null_literal_jsonb, Value, jsonb);

// -----------------------------------------------------------------

/// Sanity check insert of unencrypted literal value
#[tokio::test]
pub async fn insert_with_null_literal_plaintext() {
trace();

clear().await;

let id = random_id();

let expected: Vec<Option<String>> = vec![None];

let sql = "INSERT INTO encrypted (id, plaintext) VALUES ($1, NULL)";
insert(sql, &[&id]).await;

let sql = "SELECT plaintext FROM encrypted WHERE id = $1";

let actual = query_by::<Option<String>>(sql, &id).await;

assert_eq!(expected, actual);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#[cfg(test)]
mod tests {
use crate::common::{clear, insert, query_by, random_id, trace};
use chrono::NaiveDate;
use serde_json::Value;

macro_rules! test_insert_with_null_param {
($name: ident, $type: ident, $pg_type: ident) => {
#[tokio::test]
pub async fn $name() {
trace();

clear().await;

let id = random_id();

let encrypted_col = format!("encrypted_{}", stringify!($pg_type));
let encrypted_val: Option<$type> = None;

let sql = format!("INSERT INTO encrypted (id, {encrypted_col}) VALUES ($1, $2)");
insert(&sql, &[&id, &encrypted_val]).await;

let expected = vec![encrypted_val];

let sql = format!("SELECT {encrypted_col} FROM encrypted WHERE id = $1");
let result = query_by::<Option<$type>>(&sql, &id).await;

assert_eq!(expected, result);
}
};
}

test_insert_with_null_param!(insert_with_null_param_int2, i16, int2);
test_insert_with_null_param!(insert_with_null_param_int4, i32, int4);
test_insert_with_null_param!(insert_with_null_param_int8, i64, int8);
test_insert_with_null_param!(insert_with_null_param_float8, f64, float8);
test_insert_with_null_param!(insert_with_null_param_bool, bool, bool);
test_insert_with_null_param!(insert_with_null_param_text_only, String, text);
test_insert_with_null_param!(insert_with_null_param_date, NaiveDate, date);
test_insert_with_null_param!(insert_with_null_param_jsonb, Value, jsonb);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#[cfg(test)]
mod tests {
use crate::common::{clear, insert, query_by, random_id, random_limited, trace};
use chrono::NaiveDate;
use serde_json::Value;

macro_rules! test_insert_with_param {
($name: ident, $type: ident, $pg_type: ident) => {
#[tokio::test]
pub async fn $name() {
trace();

clear().await;

let id = random_id();

let encrypted_col = format!("encrypted_{}", stringify!($pg_type));
let encrypted_val = crate::value_for_type!($type, random_limited());

let sql = format!("INSERT INTO encrypted (id, {encrypted_col}) VALUES ($1, $2)");
insert(&sql, &[&id, &encrypted_val]).await;

let expected = vec![encrypted_val];

let sql = format!("SELECT {encrypted_col} FROM encrypted WHERE id = $1");

let actual = query_by::<$type>(&sql, &id).await;

assert_eq!(expected, actual);
}
};
}

test_insert_with_param!(insert_with_param_int2, i16, int2);
test_insert_with_param!(insert_with_param_int4, i32, int4);
test_insert_with_param!(insert_with_param_int8, i64, int8);
test_insert_with_param!(insert_with_param_float8, f64, float8);
test_insert_with_param!(insert_with_param_bool, bool, bool);
test_insert_with_param!(insert_with_param_text, String, text);
test_insert_with_param!(insert_with_param_date, NaiveDate, date);
test_insert_with_param!(insert_with_param_jsonb, Value, jsonb);

// -----------------------------------------------------------------

/// Sanity check insert of unencrypted plaintext value
#[tokio::test]
pub async fn insert_with_param_plaintext() {
trace();

clear().await;

let id = random_id();

let encrypted_val = crate::value_for_type!(String, random_limited());

let sql = "INSERT INTO encrypted (id, plaintext) VALUES ($1, $2)";
insert(sql, &[&id, &encrypted_val]).await;

let expected = vec![encrypted_val];

let sql = "SELECT plaintext FROM encrypted WHERE id = $1";

let actual = query_by::<String>(sql, &id).await;

assert_eq!(expected, actual);
}
}
Loading
Loading