diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ecd5f75..1ad040e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,6 +111,10 @@ jobs: - mysql,tracing - postgres,streaming - postgres,webhooks + - postgres,full + - mysql,full + - full + - test-queue steps: - uses: actions/checkout@v5 - name: Install system packages @@ -141,7 +145,7 @@ jobs: run: cargo test --workspace --all-features --lib --bins --no-run - name: Run unit tests (no databases) run: cargo test --workspace --all-features --lib --bins --no-fail-fast - - name: Doctests with default features + - name: Doctests with the default (empty) feature set run: cargo test -p hammerwork --doc # Database jobs: every test file under tests/ contains both a PostgreSQL and a MySQL module, diff --git a/CHANGELOG.md b/CHANGELOG.md index e692b52..5c952f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Breaking changes + +- **No features are enabled by default** (#92). `default` used to be `["metrics", "alerting", "webhooks"]`, which pulled `warp`, `reqwest`, `lettre` and `prometheus` into every build. It is now empty. Enable what you use, or the new `full` feature (`metrics`, `alerting`, `webhooks`, `streaming`, `tracing`, `encryption`; no database backend, KMS key source, event stream sink or `test-queue`). Code that uses `hammerwork::metrics`, `hammerwork::alerting` or `hammerwork::webhooks` must now enable those features. `cargo-hammerwork` and `hammerwork-web` enable the features they need themselves. +- **`azure-kv` is renamed `azure-kms`**, to match `aws-kms`, `gcp-kms` and `vault-kms` (#92). There is no alias. The same rename applies to the `cargo-hammerwork` feature. +- **`test` is renamed `test-queue`**, after the `TestQueue` it enables (#92). There is no alias. + +### Fixed + +- Building with `encryption` but no database backend (and so with `full`) no longer fails clippy with dead-code and unused-import errors. + +### Changed + +- CI checks the feature combinations `full`, `postgres,full`, `mysql,full` and `test-queue`. + ## [2.5.0] - 2026-10-11 A bug-fix release from a third bug hunt (#111 to #114, #118). It is a minor version diff --git a/Cargo.toml b/Cargo.toml index a6e1bd2..5f13c45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,7 +139,11 @@ lettre = { version = "0.11", default-features = false, features = ["builder", "s google-cloud-gax = { version = "1.4", package = "gcloud-gax", optional = true } [features] -default = ["metrics", "alerting", "webhooks"] +# No features by default: just the queue, worker and job types. Pick a database backend +# (`postgres` and/or `mysql`) and opt in to subsystems; `full` turns on all of the pure-Rust +# ones (not the database backends, the KMS key sources, the event-stream sinks or `test-queue`). +default = [] +full = ["metrics", "alerting", "webhooks", "streaming", "tracing", "encryption"] postgres = ["sqlx/postgres"] mysql = ["sqlx/mysql"] metrics = ["prometheus", "warp"] @@ -149,13 +153,13 @@ encryption = ["aes-gcm", "chacha20poly1305", "argon2", "hmac", "sha2", "hex", "z aws-kms = ["aws-sdk-kms", "aws-config"] gcp-kms = ["google-cloud-kms", "google-cloud-auth", "rustls"] vault-kms = ["vaultrs", "url"] -azure-kv = ["azure_security_keyvault_keys", "azure_identity", "azure_core"] +azure-kms = ["azure_security_keyvault_keys", "azure_identity", "azure_core"] kafka = ["rdkafka"] google-pubsub = ["google-cloud-pubsub", "google-cloud-auth", "google-cloud-googleapis", "google-cloud-gax", "rustls"] kinesis = ["aws-sdk-kinesis", "aws-config"] tracing = ["opentelemetry", "opentelemetry_sdk", "opentelemetry-otlp", "tracing-opentelemetry"] streaming = ["rmp-serde", "apache-avro", "prost"] -test = [] +test-queue = [] [dev-dependencies] tokio-test = { workspace = true } @@ -207,7 +211,7 @@ required-features = ["postgres"] [[example]] name = "test_queue_example" -required-features = ["test"] +required-features = ["test-queue"] [[example]] name = "encryption_example" diff --git a/README.md b/README.md index 9bc1613..7a2e8de 100644 --- a/README.md +++ b/README.md @@ -32,36 +32,46 @@ A high-performance, database-driven job queue for Rust with comprehensive featur ### Core Library +No features are enabled by default: Hammerwork 3.0 gives you the queue, worker, job, retry, cron, batch, workflow and priority APIs, without the HTTP, SMTP, Prometheus, OpenTelemetry or encryption dependencies. You always pick a database backend, and opt in to the subsystems you use. + ```toml [dependencies] -# Default features include metrics and alerting +# A queue on PostgreSQL (or MySQL): the lightest build hammerwork = { version = "2.5", features = ["postgres"] } -# or hammerwork = { version = "2.5", features = ["mysql"] } -# With encryption for PII protection -hammerwork = { version = "2.5", features = ["postgres", "encryption"] } - -# With AWS KMS integration for enterprise key management -hammerwork = { version = "2.5", features = ["postgres", "encryption", "aws-kms"] } - -# With Google Cloud KMS integration for enterprise key management -hammerwork = { version = "2.5", features = ["postgres", "encryption", "gcp-kms"] } - -# With HashiCorp Vault KMS integration for enterprise key management -hammerwork = { version = "2.5", features = ["postgres", "encryption", "vault-kms"] } +# Everything that is not a backend, a key source or an event sink: +# metrics, alerting, webhooks, streaming, tracing and encryption +hammerwork = { version = "2.5", features = ["postgres", "full"] } -# With distributed tracing -hammerwork = { version = "2.5", features = ["postgres", "tracing"] } +# Or pick subsystems one by one +hammerwork = { version = "2.5", features = ["postgres", "metrics", "alerting", "webhooks"] } -# Full feature set -hammerwork = { version = "2.5", features = ["postgres", "encryption", "aws-kms", "gcp-kms", "vault-kms", "tracing"] } +# With encryption for PII protection, and a KMS key source +hammerwork = { version = "2.5", features = ["postgres", "encryption", "aws-kms"] } # or "gcp-kms", "vault-kms", "azure-kms" -# Minimal installation -hammerwork = { version = "2.5", features = ["postgres"], default-features = false } +# In-memory TestQueue for your tests +[dev-dependencies] +hammerwork = { version = "2.5", features = ["test-queue"] } ``` -**Feature Flags**: `postgres`, `mysql`, `metrics` (default), `alerting` (default), `encryption` (optional), `aws-kms` (optional), `gcp-kms` (optional), `vault-kms` (optional), `tracing` (optional), `test` (for TestQueue) +**Feature flags** + +| Feature | Enables | +|---|---| +| `postgres`, `mysql` | database backends (pick at least one to use a real queue) | +| `metrics` | Prometheus metrics and the metrics HTTP endpoint | +| `alerting` | alert delivery by webhook and email | +| `webhooks` | signed webhook delivery of job events | +| `streaming` | event streaming (MessagePack, Avro, Protobuf) | +| `tracing` | OpenTelemetry tracing | +| `encryption` | payload encryption and key management | +| `aws-kms`, `gcp-kms`, `vault-kms`, `azure-kms` | KMS key sources for `encryption` | +| `kafka`, `google-pubsub`, `kinesis` | event stream sinks for `streaming` | +| `test-queue` | the in-memory `TestQueue` | +| `full` | `metrics`, `alerting`, `webhooks`, `streaming`, `tracing` and `encryption` | + +`full` does not include a database backend, a KMS key source, an event stream sink (these pull in cloud SDKs or native libraries) or `test-queue`. Upgrading from 2.x: see [Upgrading to 3.0](docs/upgrading-to-3.0.md). ### Web Dashboard (Optional) diff --git a/cargo-hammerwork/Cargo.toml b/cargo-hammerwork/Cargo.toml index d0e9be2..d794111 100644 --- a/cargo-hammerwork/Cargo.toml +++ b/cargo-hammerwork/Cargo.toml @@ -17,7 +17,7 @@ name = "cargo-hammerwork" path = "src/main.rs" [dependencies] -hammerwork = { workspace = true, features = ["postgres", "mysql", "encryption"] } +hammerwork = { workspace = true, features = ["postgres", "mysql", "encryption", "webhooks"] } clap = { workspace = true, features = ["env"] } tokio = { workspace = true } anyhow = { workspace = true } @@ -42,7 +42,7 @@ base64 = { workspace = true } aws-kms = ["hammerwork/aws-kms"] gcp-kms = ["hammerwork/gcp-kms"] vault-kms = ["hammerwork/vault-kms"] -azure-kv = ["hammerwork/azure-kv"] +azure-kms = ["hammerwork/azure-kms"] [dev-dependencies] tempfile = "3.8" diff --git a/cargo-hammerwork/README.md b/cargo-hammerwork/README.md index d3fd668..d2c8a31 100644 --- a/cargo-hammerwork/README.md +++ b/cargo-hammerwork/README.md @@ -154,7 +154,7 @@ or `--pii-field ` (repeatable) encrypt a job on any queue. The CLI never plaintext by mistake: with encryption enabled but the key unavailable, these commands fail, and without any settings they still refuse to write a plaintext job to a queue that already holds encrypted jobs. KMS key sources need the matching cargo feature of `cargo-hammerwork` -(`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kv`). +(`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kms`). ### Job Management Commands diff --git a/docs/encryption.md b/docs/encryption.md index 2a4dcc3..0330c99 100644 --- a/docs/encryption.md +++ b/docs/encryption.md @@ -258,7 +258,7 @@ let config = EncryptionConfig::new(EncryptionAlgorithm::AES256GCM) # } ``` -Credentials come from the environment (`AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET`, workload identity, managed identity, or the Azure CLI). Requires the `azure-kv` feature. The key material must be exactly the algorithm's key size (32 bytes, e.g. a 256-bit `oct` key); shorter or longer material is rejected with `EncryptionError::InvalidConfiguration` instead of being padded or truncated, for engine keys and `KeyManager` master keys alike. +Credentials come from the environment (`AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET`, workload identity, managed identity, or the Azure CLI). Requires the `azure-kms` feature. The key material must be exactly the algorithm's key size (32 bytes, e.g. a 256-bit `oct` key); shorter or longer material is rejected with `EncryptionError::InvalidConfiguration` instead of being padded or truncated, for engine keys and `KeyManager` master keys alike. #### Key loading fails closed @@ -267,7 +267,7 @@ Loading a key from an external source never falls back to another key. `Encrypti - the KMS or vault is unreachable, or rejects the credentials - the key, secret or `key` field does not exist - the source is malformed (for example a Vault path without a mount, or a GCP resource that is not `projects/.../locations/...`) -- the cargo feature for the source (`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kv`) is not enabled +- the cargo feature for the source (`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kms`) is not enabled Previously, these cases logged an error and used a key derived from the source string (key ID, region, Vault path, vault URL). Anyone who knew the configuration could derive that key. For development without a KMS, use `KeySource::Static` with a base64-encoded key, or `KeySource::Generated`. @@ -413,7 +413,7 @@ let pool = WorkerPool::from_hammerwork_config(worker, &config)?; ### Testing with `TestQueue` -The in-memory `TestQueue` (feature `test`) applies the same semantics when given an engine, so unit tests exercise the real redaction and decryption paths: +The in-memory `TestQueue` (feature `test-queue`) applies the same semantics when given an engine, so unit tests exercise the real redaction and decryption paths: ```rust,no_run # #[allow(unused_imports)] use hammerwork::{*, encryption::*, queue::DatabaseQueue, worker::JobHandler}; diff --git a/docs/job-types.md b/docs/job-types.md index effb09a..0ac04d6 100644 --- a/docs/job-types.md +++ b/docs/job-types.md @@ -280,7 +280,7 @@ In-memory results are faster to store than database results, but: `ResultConfig::with_max_size` is recorded with the job but not enforced, for database and in-memory results alike. -The `TestQueue` (feature `test`) keeps all results in memory regardless of +The `TestQueue` (feature `test-queue`) keeps all results in memory regardless of `ResultStorage`, expiring them by its mock clock. ## Error Handling diff --git a/docs/monitoring.md b/docs/monitoring.md index 96d1be7..4cb761b 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -219,17 +219,20 @@ metrics_collector.observe_custom_histogram("custom_histogram_1", "queue_name", 1 # } ``` -## Disabling Monitoring +## Enabling Monitoring -If you want to disable monitoring features: +Monitoring is opt-in: no feature is enabled by default. ```toml -# Disable all monitoring -hammerwork = { version = "0.6", features = ["postgres"], default-features = false } +# The queue only, no monitoring +hammerwork = { version = "2.5", features = ["postgres"] } -# Enable only metrics -hammerwork = { version = "0.6", features = ["postgres", "metrics"], default-features = false } +# Only metrics +hammerwork = { version = "2.5", features = ["postgres", "metrics"] } -# Enable only alerting -hammerwork = { version = "0.6", features = ["postgres", "alerting"], default-features = false } -``` \ No newline at end of file +# Only alerting +hammerwork = { version = "2.5", features = ["postgres", "alerting"] } + +# Metrics, alerting and the other optional subsystems +hammerwork = { version = "2.5", features = ["postgres", "full"] } +``` diff --git a/docs/quick-start.md b/docs/quick-start.md index 04956d2..a607772 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -20,7 +20,7 @@ Add Hammerwork to your `Cargo.toml`: ```toml [dependencies] -# PostgreSQL support with default features (metrics, alerting) +# PostgreSQL support (no features are on by default; the queue only) hammerwork = { version = "2.5", features = ["postgres"] } # MySQL support @@ -29,8 +29,8 @@ hammerwork = { version = "2.5", features = ["mysql"] } # With distributed tracing hammerwork = { version = "2.5", features = ["postgres", "tracing"] } -# Minimal installation without default features -hammerwork = { version = "2.5", features = ["postgres"], default-features = false } +# Metrics, alerting, webhooks, streaming, tracing and encryption together +hammerwork = { version = "2.5", features = ["postgres", "full"] } # Additional dependencies for examples serde_json = "1.0" diff --git a/docs/testing.md b/docs/testing.md index 4f444d6..75d5469 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -23,7 +23,7 @@ Add TestQueue to your test dependencies: ```toml [dependencies] -hammerwork = { version = "2.5", features = ["test"] } +hammerwork = { version = "2.5", features = ["test-queue"] } [dev-dependencies] tokio-test = "0.4" diff --git a/docs/upgrading-to-3.0.md b/docs/upgrading-to-3.0.md new file mode 100644 index 0000000..14ae72c --- /dev/null +++ b/docs/upgrading-to-3.0.md @@ -0,0 +1,48 @@ +# Upgrading from 2.x to 3.0 + +This page lists what to change in your project when you move from Hammerwork 2.x to 3.0. + +## Feature flags + +Three changes to the Cargo features of `hammerwork` (#92): + +1. **No default features.** In 2.x, `default = ["metrics", "alerting", "webhooks"]`. In 3.0, `default = []`, so a plain `hammerwork` dependency builds only the queue, worker and job APIs, without `warp`, `reqwest`, `lettre` or `prometheus`. +2. **`azure-kv` is now `azure-kms`**, like `aws-kms`, `gcp-kms` and `vault-kms`. There is no alias. +3. **`test` is now `test-queue`.** There is no alias. + +New: a `full` feature that enables `metrics`, `alerting`, `webhooks`, `streaming`, `tracing` and `encryption`. It does not include a database backend (`postgres`, `mysql`), a KMS key source, an event stream sink (`kafka`, `google-pubsub`, `kinesis`) or `test-queue`. + +### What to change + +If your 2.x `Cargo.toml` relied on the defaults, name the features you use: + +```toml +# 2.x (implicitly metrics + alerting + webhooks) +hammerwork = { version = "2", features = ["postgres"] } + +# 3.0, same behaviour +hammerwork = { version = "3", features = ["postgres", "metrics", "alerting", "webhooks"] } + +# 3.0, or everything that is not a backend, KMS source or event sink +hammerwork = { version = "3", features = ["postgres", "full"] } +``` + +If you used `default-features = false`, you can drop it. + +Rename the other features: + +```toml +# 2.x +hammerwork = { version = "2", features = ["postgres", "encryption", "azure-kv"] } +[dev-dependencies] +hammerwork = { version = "2", features = ["test"] } + +# 3.0 +hammerwork = { version = "3", features = ["postgres", "encryption", "azure-kms"] } +[dev-dependencies] +hammerwork = { version = "3", features = ["test-queue"] } +``` + +A compile error such as `unresolved import hammerwork::metrics`, `hammerwork::alerting` or `hammerwork::webhooks`, or a missing `TestQueue`, means a feature is not enabled. `cargo-hammerwork` and `hammerwork-web` already enable the features they need. + +If you gate your own code with `cfg(feature = "...")` on features that you forward to hammerwork, rename them in the same way. diff --git a/docs/webhooks.md b/docs/webhooks.md index 50e0f58..ccd0038 100644 --- a/docs/webhooks.md +++ b/docs/webhooks.md @@ -1,6 +1,6 @@ # Webhooks -`WebhookManager` (module `hammerwork::webhooks`, feature `webhooks`, on by default) +`WebhookManager` (module `hammerwork::webhooks`, feature `webhooks`, included in `full`) delivers job lifecycle events to HTTP endpoints, with retries, authentication and HMAC signatures. (Alert notifications are a separate system; see [Monitoring & Alerting](monitoring.md).) diff --git a/hammerwork-web/Cargo.toml b/hammerwork-web/Cargo.toml index 511c1cd..ede5247 100644 --- a/hammerwork-web/Cargo.toml +++ b/hammerwork-web/Cargo.toml @@ -68,7 +68,7 @@ auth = ["bcrypt"] [dev-dependencies] # `TestQueue` (in-memory queue) for handler tests -hammerwork = { workspace = true, features = ["test"] } +hammerwork = { workspace = true, features = ["test-queue"] } tempfile = { workspace = true } tokio-test = { workspace = true } warp = { workspace = true, features = ["test"] } diff --git a/src/archive.rs b/src/archive.rs index 09c1c46..fdcad6e 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1528,7 +1528,7 @@ mod tests { /// A scheduled pass whose policy ages are too large to subtract from now (say /// `Duration::MAX` for "never purge") archives and purges nothing instead of /// panicking. - #[cfg(all(feature = "test", feature = "postgres"))] + #[cfg(all(feature = "test-queue", feature = "postgres"))] #[tokio::test] async fn scheduled_pass_with_huge_ages_does_not_panic() { let queue = crate::queue::test::TestQueue::new(); diff --git a/src/config.rs b/src/config.rs index f2e9726..ec42210 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1095,7 +1095,7 @@ impl std::str::FromStr for PayloadEncryptionAlgorithm { /// - `aws://...`, `gcp://...`: a data key generated by AWS or GCP KMS and stored /// KMS-encrypted in the database (`aws-kms` / `gcp-kms` features) /// - `vault://...`: a HashiCorp Vault secret (`vault-kms` feature) -/// - `azure://...`: an Azure Key Vault secret (`azure-kv` feature) +/// - `azure://...`: an Azure Key Vault secret (`azure-kms` feature) /// /// Static keys cannot be configured: a key written in a configuration file would be /// printed and saved with it. Anything else is rejected when the configuration is diff --git a/src/encryption.rs b/src/encryption.rs index bde66a6..93b0488 100644 --- a/src/encryption.rs +++ b/src/encryption.rs @@ -54,7 +54,7 @@ //! # } //! ``` -#[cfg(feature = "azure-kv")] +#[cfg(feature = "azure-kms")] mod azure; pub mod engine; mod envelope; @@ -991,7 +991,7 @@ impl EncryptionStats { /// /// # Arguments /// -/// * `service_prefix` - Service-specific prefix (e.g., "aws-kms-data-key", "azure-kv-master-key") +/// * `service_prefix` - Service-specific prefix (e.g., "aws-kms-data-key", "azure-kms-master-key") /// * `inputs` - Variable number of string inputs that will be hashed together /// /// # Returns @@ -1010,7 +1010,7 @@ impl EncryptionStats { /// assert_eq!(key.len(), 32); // SHA-256 produces 32 bytes /// /// // Different inputs give a different key -/// let key = generate_deterministic_key("azure-kv-master-key", &["vault-url", "key-name"]); +/// let key = generate_deterministic_key("azure-kms-master-key", &["vault-url", "key-name"]); /// assert_eq!(key.len(), 32); /// # } /// ``` diff --git a/src/encryption/engine.rs b/src/encryption/engine.rs index 3e96150..cdddcfd 100644 --- a/src/encryption/engine.rs +++ b/src/encryption/engine.rs @@ -1417,7 +1417,7 @@ async fn load_external_key( } else if service_config.starts_with("vault://") { load_from_vault(service_config, expected_size).await } else if service_config.starts_with("azure://") { - load_from_azure_kv(service_config, expected_size).await + load_from_azure_kms(service_config, expected_size).await } else { Err(EncryptionError::KeyManagement(format!( "Unknown external key management service: {}", @@ -1461,7 +1461,7 @@ async fn load_from_vault( /// Load a key from Azure Key Vault (`azure:///keys/`, key name /// defaults to `encryption-key`). The key material must be exactly `expected_size` /// bytes; anything else is an [`EncryptionError::InvalidConfiguration`]. -async fn load_from_azure_kv( +async fn load_from_azure_kms( service_config: &str, expected_size: usize, ) -> Result { @@ -1472,13 +1472,13 @@ async fn load_from_azure_kv( vault_url, key_name ); - #[cfg(not(feature = "azure-kv"))] + #[cfg(not(feature = "azure-kms"))] { let _ = (vault_url, key_name, expected_size); - Err(kms::kms_feature_disabled("azure://", "azure-kv")) + Err(kms::kms_feature_disabled("azure://", "azure-kms")) } - #[cfg(feature = "azure-kv")] + #[cfg(feature = "azure-kms")] { let decoded_key = super::azure::fetch_key_material(&vault_url, &key_name) .await @@ -1500,6 +1500,7 @@ async fn load_from_azure_kv( } #[cfg(test)] +#[cfg_attr(not(any(feature = "postgres", feature = "mysql")), allow(dead_code))] mod tests { use super::*; use serde_json::json; @@ -1826,8 +1827,8 @@ mod tests { "gcp://projects/p/locations/global/keyRings/r/cryptoKeys/k", "gcp-kms", )); - #[cfg(not(feature = "azure-kv"))] - cases.push(("azure://my-vault.vault.azure.net/keys/k", "azure-kv")); + #[cfg(not(feature = "azure-kms"))] + cases.push(("azure://my-vault.vault.azure.net/keys/k", "azure-kms")); for (source, feature) in cases { let err = load_external_key(source, 32).await.unwrap_err(); @@ -2613,16 +2614,16 @@ mod tests { } } - #[cfg(feature = "azure-kv")] + #[cfg(feature = "azure-kms")] #[tokio::test] async fn test_azure_key_with_invalid_vault_fails() { - let err = load_from_azure_kv("azure://", 32).await.unwrap_err(); + let err = load_from_azure_kms("azure://", 32).await.unwrap_err(); assert!( matches!(err, EncryptionError::InvalidConfiguration(_)), "{err}" ); // A vault host that is not a valid URL fails before any request is made. - let err = load_from_azure_kv("azure://bad host/keys/k", 32) + let err = load_from_azure_kms("azure://bad host/keys/k", 32) .await .unwrap_err(); assert!( diff --git a/src/encryption/key_manager.rs b/src/encryption/key_manager.rs index ca7e39d..c0282c9 100644 --- a/src/encryption/key_manager.rs +++ b/src/encryption/key_manager.rs @@ -107,7 +107,7 @@ //! ## Azure Key Vault Master Key Integration //! //! ```rust,no_run -//! # #[cfg(all(feature = "encryption", feature = "azure-kv", feature = "postgres"))] +//! # #[cfg(all(feature = "encryption", feature = "azure-kms", feature = "postgres"))] //! # { //! use hammerwork::encryption::{KeyManager, KeyManagerConfig, KeySource}; //! @@ -144,7 +144,9 @@ use base64::Engine; use chrono::{DateTime, Duration, Utc}; use rand::{RngCore, rngs::OsRng}; use serde::{Deserialize, Serialize}; -use sqlx::{Database, Pool, Row}; +#[cfg(any(test, feature = "postgres", feature = "mysql"))] +use sqlx::Row; +use sqlx::{Database, Pool}; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use tracing::{debug, error, info, warn}; @@ -972,7 +974,7 @@ impl KeyManager { /// /// Loads the master key from `config.master_key_source` and fails if it cannot be /// loaded. There is no fallback key: an unreachable KMS, missing credentials, or a KMS - /// source whose cargo feature (`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kv`) is not + /// source whose cargo feature (`aws-kms`, `gcp-kms`, `vault-kms`, `azure-kms`) is not /// enabled are all errors. /// /// If a key-encryption key was created earlier with [`KeyManager::generate_master_key`], @@ -2081,6 +2083,7 @@ impl KeyManager { /// ID of the active key-encryption key in the database, if any #[cfg(test)] + #[cfg_attr(not(any(feature = "postgres", feature = "mysql")), allow(dead_code))] async fn find_master_key_id_in_database(&self) -> Result, EncryptionError> { Ok(DB::load_active_master_key(&self.pool) .await? @@ -2089,6 +2092,7 @@ impl KeyManager { /// The active key-encryption key's ID, or an ID derived from `key_material` #[cfg(test)] + #[cfg_attr(not(any(feature = "postgres", feature = "mysql")), allow(dead_code))] async fn get_or_create_master_key_id( &self, key_material: &[u8], @@ -2197,6 +2201,7 @@ fn unwrap_key_material( .map_err(|e| EncryptionError::DecryptionFailed(format!("Key decryption failed: {}", e))) } +#[cfg(any(feature = "postgres", feature = "mysql"))] /// Label stored in the `key_source` column. Only the kind of source is stored, never its /// detail: for [`KeySource::Static`] the detail is the key itself. fn key_source_label(source: &KeySource) -> &'static str { @@ -2208,6 +2213,7 @@ fn key_source_label(source: &KeySource) -> &'static str { } } +#[cfg(any(feature = "postgres", feature = "mysql"))] fn db_error(context: &str) -> impl FnOnce(sqlx::Error) -> EncryptionError + '_ { move |e| EncryptionError::DatabaseError(format!("{}: {}", context, e)) } @@ -2237,6 +2243,7 @@ fn audit_context( .collect() } +#[cfg(any(feature = "postgres", feature = "mysql"))] fn column<'r, R, T>(row: &'r R, name: &str) -> Result where R: Row, @@ -2248,11 +2255,13 @@ where }) } +#[cfg(any(feature = "postgres", feature = "mysql"))] fn to_i32(value: u32, what: &str) -> Result { i32::try_from(value) .map_err(|_| EncryptionError::KeyManagement(format!("{} {} is out of range", what, value))) } +#[cfg(any(feature = "postgres", feature = "mysql"))] fn to_u32(value: i32, what: &str) -> Result { u32::try_from(value) .map_err(|_| EncryptionError::KeyManagement(format!("{} {} is out of range", what, value))) @@ -2359,13 +2368,13 @@ async fn load_master_key_from_azure(service_config: &str) -> Result( feature = "aws-kms", feature = "gcp-kms", feature = "vault-kms", - feature = "azure-kv" + feature = "azure-kms" ), allow(dead_code) )] @@ -114,7 +114,7 @@ pub(crate) fn exact_vault_key( /// Key material of exactly `expected_size` bytes, or a configuration error naming `what`. #[cfg_attr( - not(any(feature = "vault-kms", feature = "azure-kv")), + not(any(feature = "vault-kms", feature = "azure-kms")), allow(dead_code) )] pub(crate) fn exact_size( diff --git a/src/lib.rs b/src/lib.rs index 3fe5653..33be1a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -400,7 +400,7 @@ pub type Result = std::result::Result; doctest, feature = "postgres", feature = "mysql", - feature = "test", + feature = "test-queue", feature = "encryption", feature = "tracing", feature = "streaming", diff --git a/src/queue/mod.rs b/src/queue/mod.rs index f139dfb..190dbdb 100644 --- a/src/queue/mod.rs +++ b/src/queue/mod.rs @@ -32,7 +32,7 @@ pub mod postgres; #[cfg(feature = "mysql")] pub mod mysql; -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] pub mod test; mod payload_encryption; @@ -1250,7 +1250,7 @@ pub(crate) fn saturating_chrono_duration(duration: std::time::Duration) -> chron /// `now - duration`, saturating at the minimum representable timestamp. #[cfg_attr( - not(any(feature = "postgres", feature = "mysql", feature = "test")), + not(any(feature = "postgres", feature = "mysql", feature = "test-queue")), allow(dead_code) )] pub(crate) fn saturating_sub_from( @@ -1262,7 +1262,7 @@ pub(crate) fn saturating_sub_from( } /// `now + duration`, saturating at the maximum representable timestamp. -#[cfg_attr(not(feature = "test"), allow(dead_code))] +#[cfg_attr(not(feature = "test-queue"), allow(dead_code))] pub(crate) fn saturating_add_to( now: DateTime, duration: std::time::Duration, @@ -1958,7 +1958,7 @@ mod tests { // This test doesn't require an actual database connection // We'll use the test queue for this since it doesn't require a real database - #[cfg(feature = "test")] + #[cfg(feature = "test-queue")] { use crate::queue::test::TestQueue; let test_queue = TestQueue::new(); @@ -2050,7 +2050,7 @@ mod tests { // Test that JobQueue implements Clone trait correctly // This test verifies compilation and basic cloning functionality - #[cfg(feature = "test")] + #[cfg(feature = "test-queue")] { use crate::queue::test::TestQueue; diff --git a/src/spawn.rs b/src/spawn.rs index 3062c42..1e65620 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -819,12 +819,12 @@ mod tests { assert!(err.to_string().contains("_spawn_config")); } - #[cfg(all(feature = "test", feature = "postgres"))] + #[cfg(all(feature = "test-queue", feature = "postgres"))] struct OwnSettingsSpawner { sibling: crate::JobId, } - #[cfg(all(feature = "test", feature = "postgres"))] + #[cfg(all(feature = "test-queue", feature = "postgres"))] #[async_trait] impl SpawnHandler for OwnSettingsSpawner { async fn spawn_jobs(&self, _context: SpawnContext) -> Result> { @@ -843,7 +843,7 @@ mod tests { /// A parent without a retry strategy, timeout or trace id no longer wipes the /// child's own (a parent that has one still overrides it), and the child keeps its /// own dependencies in addition to the parent. - #[cfg(all(feature = "test", feature = "postgres"))] + #[cfg(all(feature = "test-queue", feature = "postgres"))] #[tokio::test] async fn test_prepare_spawn_keeps_the_childs_own_settings() { use crate::queue::test::TestQueue; diff --git a/tests/archive_workflow_regression_tests.rs b/tests/archive_workflow_regression_tests.rs index 436458e..206b404 100644 --- a/tests/archive_workflow_regression_tests.rs +++ b/tests/archive_workflow_regression_tests.rs @@ -62,7 +62,7 @@ impl StoredWorkflow for hammerwork::JobQueue { } } -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] impl StoredWorkflow for hammerwork::queue::test::TestQueue { async fn stored_workflow(&self, id: WorkflowId) -> (String, i32, i32, i32) { // TestQueue computes the workflow when it is read. @@ -599,7 +599,7 @@ where /// #111 (5), with the default policy enabled: a queue without a policy of its own gets /// the default one, a queue with its own policy keeps it. Only run on `TestQueue`: on /// a shared database an archive-now default policy would archive other tests' jobs. -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] async fn scheduled_pass_applies_default_policy_to_other_queues(queue: Arc) where Q: DatabaseQueue + Send + Sync + 'static, @@ -674,7 +674,7 @@ crate::test_utils::backend_tests!( ] ); -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] crate::test_utils::backend_tests!( test_queue_tests, test_utils::test_queue, diff --git a/tests/batch_dequeue_tests.rs b/tests/batch_dequeue_tests.rs index 820e99a..78c4b84 100644 --- a/tests/batch_dequeue_tests.rs +++ b/tests/batch_dequeue_tests.rs @@ -7,7 +7,7 @@ //! attempt count, pause/dependency/recurring checks, priority order and exactly-once //! claiming under concurrency. -#![cfg(any(feature = "postgres", feature = "mysql", feature = "test"))] +#![cfg(any(feature = "postgres", feature = "mysql", feature = "test-queue"))] mod test_utils; @@ -433,7 +433,7 @@ crate::test_utils::backend_tests!( ] ); -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] crate::test_utils::backend_tests!( #[tokio::test(flavor = "multi_thread", worker_threads = 4)] test_queue_tests, diff --git a/tests/queue_api_tests.rs b/tests/queue_api_tests.rs index 59ced33..4a2a27f 100644 --- a/tests/queue_api_tests.rs +++ b/tests/queue_api_tests.rs @@ -4,7 +4,7 @@ //! information, workflow dependency helpers and the in-place rescheduling of recurring //! jobs. -#![cfg(any(feature = "postgres", feature = "mysql", feature = "test"))] +#![cfg(any(feature = "postgres", feature = "mysql", feature = "test-queue"))] mod test_utils; @@ -653,7 +653,7 @@ crate::test_utils::backend_tests!( ] ); -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] crate::test_utils::backend_tests!( test_queue_tests, test_utils::test_queue, diff --git a/tests/queue_regression_tests.rs b/tests/queue_regression_tests.rs index 5c994ae..f063496 100644 --- a/tests/queue_regression_tests.rs +++ b/tests/queue_regression_tests.rs @@ -524,7 +524,7 @@ mod mysql_tests { } /// The in-memory `TestQueue` re-evaluates dependencies like the database backends. -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] mod test_queue_tests { use super::*; use hammerwork::queue::test::TestQueue; diff --git a/tests/test_queue_parity_tests.rs b/tests/test_queue_parity_tests.rs index ab58d27..12bcebe 100644 --- a/tests/test_queue_parity_tests.rs +++ b/tests/test_queue_parity_tests.rs @@ -404,7 +404,7 @@ crate::test_utils::backend_tests!( ] ); -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] crate::test_utils::backend_tests!( test_queue_tests, test_utils::test_queue, diff --git a/tests/test_queue_tests.rs b/tests/test_queue_tests.rs index 932746d..6d8e171 100644 --- a/tests/test_queue_tests.rs +++ b/tests/test_queue_tests.rs @@ -1,6 +1,6 @@ //! Integration tests for the TestQueue implementation -#![cfg(feature = "test")] +#![cfg(feature = "test-queue")] use chrono::Duration; use hammerwork::{ diff --git a/tests/test_utils.rs b/tests/test_utils.rs index 221b553..cd0a078 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -183,7 +183,7 @@ pub async fn serial() -> tokio::sync::MutexGuard<'static, ()> { } /// A fresh in-memory [`TestQueue`](hammerwork::queue::test::TestQueue). -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] pub async fn test_queue() -> std::sync::Arc { std::sync::Arc::new(hammerwork::queue::test::TestQueue::new()) } diff --git a/tests/workflow_failure_policy_tests.rs b/tests/workflow_failure_policy_tests.rs index 5aedb30..693d2a1 100644 --- a/tests/workflow_failure_policy_tests.rs +++ b/tests/workflow_failure_policy_tests.rs @@ -3,7 +3,7 @@ //! the other jobs (`fail_job`, a worker's `finish_job_run` and the explicit //! `fail_job_dependencies` helper all apply it the same way). -#![cfg(any(feature = "postgres", feature = "mysql", feature = "test"))] +#![cfg(any(feature = "postgres", feature = "mysql", feature = "test-queue"))] mod test_utils; @@ -404,7 +404,7 @@ crate::test_utils::backend_tests!( ] ); -#[cfg(feature = "test")] +#[cfg(feature = "test-queue")] crate::test_utils::backend_tests!( test_queue_tests, test_utils::test_queue,