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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
env:
CARGO_TERM_COLOR: always
BUILD_PROFILE: debug
WHITAKER_INSTALLER_VERSION: '0.2.5'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
- name: Setup Rust
Expand All @@ -26,6 +27,24 @@ jobs:
**/*.md
!**/target/**
!**/dist/**
- name: Cache whitaker-installer
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/bin/whitaker-installer
~/.cache/cargo-binstall
key: whitaker-installer-${{ runner.os }}-${{ runner.arch }}-${{ env.WHITAKER_INSTALLER_VERSION }}
- name: Install the Whitaker Dylint suite
run: |
if ! command -v whitaker-installer >/dev/null 2>&1; then
if cargo binstall --version >/dev/null 2>&1; then
cargo binstall --no-confirm --locked "whitaker-installer@${WHITAKER_INSTALLER_VERSION}"
else
echo "cargo-binstall unavailable; building whitaker-installer from crates.io"
cargo install --locked whitaker-installer --version "${WHITAKER_INSTALLER_VERSION}"
fi
fi
whitaker-installer
- name: Lint
run: make lint
- name: Test pg_worker preparation
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ CLIPPY_FLAGS ?= $(CARGO_FLAGS) -- $(RUST_FLAGS)
TEST_FLAGS ?= $(CARGO_FLAGS)
MDLINT ?= markdownlint-cli2
NIXIE ?= nixie
WHITAKER ?= whitaker
PG_WORKER_PATH ?= $(CURDIR)/target/pg_worker
PG_WORKER_PROFILE ?= dev
PG_WORKER_DEBUG_PROFILES := dev test
Expand Down Expand Up @@ -56,9 +57,10 @@ test-prepare-pg-worker: ## Test pg_worker profile mapping and fail-fast setup
target/%/$(TARGET): ## Build binary in debug or release mode
$(CARGO) build $(BUILD_JOBS) $(if $(findstring release,$(@)),--release)

lint: ## Run Clippy with warnings denied
lint: ## Run Clippy and the Whitaker Dylint suite with warnings denied
RUSTDOCFLAGS="$(RUSTDOC_FLAGS)" $(CARGO) doc --no-deps
$(CARGO) clippy $(CLIPPY_FLAGS)
RUSTFLAGS="$(RUST_FLAGS)" $(WHITAKER) --all -- $(CARGO_FLAGS)

fmt: ## Format Rust and Markdown sources
$(CARGO) fmt --all
Expand Down
2 changes: 2 additions & 0 deletions src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ where

#[cfg(test)]
mod tests {
//! Unit tests for the CTE builder functions.

use super::*;
use crate::test_support::normalise_debug_sql;
use diesel::{
Expand Down
2 changes: 2 additions & 0 deletions src/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ macro_rules! table_columns {

#[cfg(test)]
mod tests {
//! Unit tests for CTE column list handling.

use super::*;

diesel::table! {
Expand Down
2 changes: 2 additions & 0 deletions src/connection_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ impl<B> RecursiveCTEExt for SyncConnectionWrapper<diesel::sqlite::SqliteConnecti

#[cfg(test)]
mod tests {
//! Unit tests for the connection extension traits.

use super::*;
use crate::{SearchStyle, builders::RecursiveParts, test_support::normalise_debug_sql};
use diesel::{
Expand Down
122 changes: 2 additions & 120 deletions src/cte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,123 +285,5 @@ impl_cte_traits!(WithCte<Cte, Body>, Body);
impl_cte_traits!(WithRecursive<Seed, Step, Body>, Body);

#[cfg(test)]
mod tests {
use super::*;
use crate::{
builders::{self, RecursiveParts},
test_support::normalise_debug_sql,
};
use diesel::{
debug_query, dsl::sql, expression::SqlLiteral, sql_types::Integer, sqlite::Sqlite,
};
use rstest::{fixture, rstest};

enum Builder {
All,
Distinct,
}

#[fixture]
fn sample_parts()
-> RecursiveParts<SqlLiteral<Integer>, SqlLiteral<Integer>, SqlLiteral<Integer>> {
RecursiveParts::new(
sql::<Integer>("SELECT 1"),
sql::<Integer>("SELECT n + 1 FROM nums WHERE n < 2"),
sql::<Integer>("SELECT n FROM nums"),
)
}

#[test]
fn duplicate_column_names_are_rejected() {
let names = &["id", "id"];
match ensure_unique_columns(names) {
Err(err) => {
assert!(matches!(err, Error::QueryBuilderError(_)));
assert!(err.to_string().contains("duplicate column name"));
}
Ok(()) => panic!("expected duplicate column error"),
}
}

#[rstest]
#[case::all(Builder::All, "UNION ALL")]
#[case::distinct(Builder::Distinct, "UNION")]
fn with_recursive_renders_expected_sql(
sample_parts: RecursiveParts<SqlLiteral<Integer>, SqlLiteral<Integer>, SqlLiteral<Integer>>,
#[case] builder: Builder,
#[case] union_op: &str,
) {
let query = match builder {
Builder::All => {
builders::with_recursive::<Sqlite, _, _, _, _, _>("nums", &["n"], sample_parts)
}
Builder::Distinct => builders::with_recursive_not_all::<Sqlite, _, _, _, _, _>(
"nums",
&["n"],
sample_parts,
),
};

let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
format!(
"WITH RECURSIVE \"nums\" (\"n\") AS (SELECT 1 {union_op} SELECT n + 1 FROM nums WHERE n < 2) SELECT n FROM nums"
)
);
}

#[test]
fn with_cte_renders_expected_sql() {
let query = builders::with_cte::<Sqlite, _, _, _, _>(
"seed",
&["value"],
builders::CteParts::new(
sql::<Integer>("SELECT 42"),
sql::<Integer>("SELECT value FROM seed"),
),
);
let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
"WITH \"seed\" (\"value\") AS (SELECT 42) SELECT value FROM seed"
);
}

#[test]
fn with_recursive_skips_identifier_list_when_empty() {
let query = builders::with_recursive::<Sqlite, _, _, _, _, _>(
"nums",
&[] as &[&str],
RecursiveParts::new(
sql::<Integer>("SELECT 1"),
sql::<Integer>("SELECT n + 1 FROM nums WHERE n < 2"),
sql::<Integer>("SELECT n FROM nums"),
),
);
let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
"WITH RECURSIVE \"nums\" AS (SELECT 1 UNION ALL SELECT n + 1 FROM nums WHERE n < 2) SELECT n FROM nums"
);
}

#[test]
fn query_id_reflects_runtime_union_choice() {
type RecursiveQuery = WithRecursive<
Sqlite,
(),
SqlLiteral<Integer>,
SqlLiteral<Integer>,
SqlLiteral<Integer>,
>;
type CteQuery = WithCte<Sqlite, (), SqlLiteral<Integer>, SqlLiteral<Integer>>;

let recursive_has_static =
std::hint::black_box(<RecursiveQuery as QueryId>::HAS_STATIC_QUERY_ID);
let cte_has_static = std::hint::black_box(<CteQuery as QueryId>::HAS_STATIC_QUERY_ID);

assert!(!recursive_has_static);
assert!(cte_has_static);
}
}
#[path = "cte_tests.rs"]
mod tests;
110 changes: 110 additions & 0 deletions src/cte_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Unit tests for the CTE query types in [`crate::cte`].

use super::*;
use crate::{
builders::{self, RecursiveParts},
test_support::normalise_debug_sql,
};
use diesel::{debug_query, dsl::sql, expression::SqlLiteral, sql_types::Integer, sqlite::Sqlite};
use rstest::{fixture, rstest};

enum Builder {
All,
Distinct,
}

#[fixture]
fn sample_parts() -> RecursiveParts<SqlLiteral<Integer>, SqlLiteral<Integer>, SqlLiteral<Integer>> {
RecursiveParts::new(
sql::<Integer>("SELECT 1"),
sql::<Integer>("SELECT n + 1 FROM nums WHERE n < 2"),
sql::<Integer>("SELECT n FROM nums"),
)
}

#[test]
fn duplicate_column_names_are_rejected() {
let names = &["id", "id"];
match ensure_unique_columns(names) {
Err(err) => {
assert!(matches!(err, Error::QueryBuilderError(_)));
assert!(err.to_string().contains("duplicate column name"));
}
Ok(()) => panic!("expected duplicate column error"),
}
}

#[rstest]
#[case::all(Builder::All, "UNION ALL")]
#[case::distinct(Builder::Distinct, "UNION")]
fn with_recursive_renders_expected_sql(
sample_parts: RecursiveParts<SqlLiteral<Integer>, SqlLiteral<Integer>, SqlLiteral<Integer>>,
#[case] builder: Builder,
#[case] union_op: &str,
) {
let query = match builder {
Builder::All => {
builders::with_recursive::<Sqlite, _, _, _, _, _>("nums", &["n"], sample_parts)
}
Builder::Distinct => {
builders::with_recursive_not_all::<Sqlite, _, _, _, _, _>("nums", &["n"], sample_parts)
}
};

let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
format!(
"WITH RECURSIVE \"nums\" (\"n\") AS (SELECT 1 {union_op} SELECT n + 1 FROM nums WHERE n < 2) SELECT n FROM nums"
)
);
}

#[test]
fn with_cte_renders_expected_sql() {
let query = builders::with_cte::<Sqlite, _, _, _, _>(
"seed",
&["value"],
builders::CteParts::new(
sql::<Integer>("SELECT 42"),
sql::<Integer>("SELECT value FROM seed"),
),
);
let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
"WITH \"seed\" (\"value\") AS (SELECT 42) SELECT value FROM seed"
);
}

#[test]
fn with_recursive_skips_identifier_list_when_empty() {
let query = builders::with_recursive::<Sqlite, _, _, _, _, _>(
"nums",
&[] as &[&str],
RecursiveParts::new(
sql::<Integer>("SELECT 1"),
sql::<Integer>("SELECT n + 1 FROM nums WHERE n < 2"),
sql::<Integer>("SELECT n FROM nums"),
),
);
let sql = normalise_debug_sql(&debug_query::<Sqlite, _>(&query).to_string());
assert_eq!(
sql,
"WITH RECURSIVE \"nums\" AS (SELECT 1 UNION ALL SELECT n + 1 FROM nums WHERE n < 2) SELECT n FROM nums"
);
}

#[test]
fn query_id_reflects_runtime_union_choice() {
type RecursiveQuery =
WithRecursive<Sqlite, (), SqlLiteral<Integer>, SqlLiteral<Integer>, SqlLiteral<Integer>>;
type CteQuery = WithCte<Sqlite, (), SqlLiteral<Integer>, SqlLiteral<Integer>>;

let recursive_has_static =
std::hint::black_box(<RecursiveQuery as QueryId>::HAS_STATIC_QUERY_ID);
let cte_has_static = std::hint::black_box(<CteQuery as QueryId>::HAS_STATIC_QUERY_ID);

assert!(!recursive_has_static);
assert!(cte_has_static);
}
2 changes: 2 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ macro_rules! step_query {

#[cfg(test)]
mod tests {
//! Unit tests for the query-part macros.

use super::QueryPart;
use crate::test_support::normalise_debug_sql;
use diesel::{debug_query, dsl::sql, sql_types::Integer, sqlite::Sqlite};
Expand Down
Loading