mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-16 18:08:21 +00:00
144 lines
6.3 KiB
Rust
144 lines
6.3 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! JetStream stream validation integration tests for the NATS target.
|
|
//!
|
|
//! These tests are ignored because they require a running NATS server with JetStream enabled. To
|
|
//! run locally:
|
|
//!
|
|
//! docker run -d --name rustfs-nats-test -p 4222:4222 nats:2 -js
|
|
//! cargo test -p rustfs-targets --test nats_jetstream_validation_integration -- --ignored
|
|
//!
|
|
//! Override the server URL with RUSTFS_TEST_NATS_URL. Each test provisions and removes its own
|
|
//! stream. RustFS never provisions the stream, the test harness does, mirroring the operator role.
|
|
|
|
use async_nats::jetstream::stream::Config;
|
|
use rustfs_targets::check_nats_server_available;
|
|
use rustfs_targets::target::TargetType;
|
|
use rustfs_targets::target::nats::NATSArgs;
|
|
use std::time::Duration;
|
|
use uuid::Uuid;
|
|
|
|
fn server_url() -> String {
|
|
std::env::var("RUSTFS_TEST_NATS_URL").unwrap_or_else(|_| "nats://127.0.0.1:4222".to_string())
|
|
}
|
|
|
|
fn jetstream_args(subject: &str, stream_name: &str) -> NATSArgs {
|
|
NATSArgs {
|
|
enable: true,
|
|
address: server_url(),
|
|
subject: subject.to_string(),
|
|
username: String::new(),
|
|
password: String::new(),
|
|
token: String::new(),
|
|
credentials_file: String::new(),
|
|
tls_ca: String::new(),
|
|
tls_client_cert: String::new(),
|
|
tls_client_key: String::new(),
|
|
tls_required: false,
|
|
queue_dir: "/tmp/rustfs-nats-jetstream-test".to_string(),
|
|
queue_limit: 0,
|
|
jetstream_enable: Some(true),
|
|
jetstream_stream_name: Some(stream_name.to_string()),
|
|
jetstream_ack_timeout_secs: Some(30),
|
|
target_type: TargetType::NotifyEvent,
|
|
}
|
|
}
|
|
|
|
/// A pre-provisioned writable stream: it captures the subject, returns acks, accepts writes, and its
|
|
/// duplicate window exceeds the retry lifetime. The test harness creates it, never RustFS.
|
|
async fn provision_writable_stream(stream_name: &str, subject: &str) {
|
|
let client = async_nats::connect(server_url()).await.expect("connect to NATS");
|
|
let context = async_nats::jetstream::new(client);
|
|
// Clear any leftover from a prior aborted run. Absent is fine, so the result is dropped.
|
|
let _ = context.delete_stream(stream_name).await;
|
|
context
|
|
.create_stream(Config {
|
|
name: stream_name.to_string(),
|
|
subjects: vec![subject.to_string()],
|
|
duplicate_window: Duration::from_secs(300),
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.expect("provision the stream");
|
|
}
|
|
|
|
async fn remove_stream(stream_name: &str) {
|
|
let client = async_nats::connect(server_url()).await.expect("connect to NATS");
|
|
let context = async_nats::jetstream::new(client);
|
|
// Best-effort teardown. A failure to delete does not fail the test, so the result is dropped.
|
|
let _ = context.delete_stream(stream_name).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires a live NATS JetStream server (see module docs; RUSTFS_TEST_NATS_URL overrides)"]
|
|
async fn missing_stream_fails_the_health_check() {
|
|
let stream_name = format!("RUSTFS_TEST_{}", Uuid::new_v4().simple());
|
|
let args = jetstream_args("rustfs.events", &stream_name);
|
|
// No stream is provisioned, so the lookup must fail.
|
|
let err = check_nats_server_available(&args)
|
|
.await
|
|
.expect_err("a missing stream fails the health check");
|
|
assert!(!err.to_string().is_empty(), "the failure carries a generic message");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires a live NATS JetStream server (see module docs; RUSTFS_TEST_NATS_URL overrides)"]
|
|
async fn valid_stream_passes_the_health_check() {
|
|
let stream_name = format!("RUSTFS_TEST_{}", Uuid::new_v4().simple());
|
|
let subject = "rustfs.events";
|
|
provision_writable_stream(&stream_name, subject).await;
|
|
let args = jetstream_args(subject, &stream_name);
|
|
let result = check_nats_server_available(&args).await;
|
|
remove_stream(&stream_name).await;
|
|
result.expect("a pre-provisioned writable stream passes the health check");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires a live NATS JetStream server (see module docs; RUSTFS_TEST_NATS_URL overrides)"]
|
|
async fn stream_not_capturing_the_subject_fails_the_health_check() {
|
|
let stream_name = format!("RUSTFS_TEST_{}", Uuid::new_v4().simple());
|
|
// The stream binds a different subject than the target publishes to.
|
|
provision_writable_stream(&stream_name, "some.other.subject").await;
|
|
let args = jetstream_args("rustfs.events", &stream_name);
|
|
let result = check_nats_server_available(&args).await;
|
|
remove_stream(&stream_name).await;
|
|
result.expect_err("a stream that does not capture the subject fails the health check");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires a live NATS JetStream server (see module docs; RUSTFS_TEST_NATS_URL overrides)"]
|
|
async fn too_small_duplicate_window_fails_the_health_check() {
|
|
let stream_name = format!("RUSTFS_TEST_{}", Uuid::new_v4().simple());
|
|
let subject = "rustfs.events";
|
|
let client = async_nats::connect(server_url()).await.expect("connect to NATS");
|
|
let context = async_nats::jetstream::new(client);
|
|
// Clear any leftover from a prior aborted run. Absent is fine, so the result is dropped.
|
|
let _ = context.delete_stream(&stream_name).await;
|
|
context
|
|
.create_stream(Config {
|
|
name: stream_name.clone(),
|
|
subjects: vec![subject.to_string()],
|
|
// Below the worst-case retry lifetime at the default ack timeout.
|
|
duplicate_window: Duration::from_secs(60),
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.expect("provision the stream");
|
|
let args = jetstream_args(subject, &stream_name);
|
|
let result = check_nats_server_available(&args).await;
|
|
remove_stream(&stream_name).await;
|
|
result.expect_err("a too-small duplicate window fails the health check");
|
|
}
|