Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration tests for /api/v2/text/detection/chat #320

Open
wants to merge 117 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
8b4df02
Refactor common integration test code
mdevino Jan 3, 2025
798b730
/detection/content base test case
mdevino Jan 6, 2025
c02c653
Replace json macros with strong types
mdevino Jan 8, 2025
2c80ddc
Update test detector name to mention whole_doc_chunker
mdevino Jan 9, 2025
2037533
Testing mock for chunker gRPC call
mdevino Jan 9, 2025
64ef86a
Update detection_content.rs::test_single_detection() to include detec…
mdevino Feb 5, 2025
a208f80
Update detection_content.rs::test_single_detection_whole_doc() mocks …
mdevino Feb 5, 2025
e30a2df
Make test_single_detection_whole_doc() more meaningful
mdevino Feb 5, 2025
7b4f0ac
Add test case: detection_content.rs::test_single_detection_sentence_c…
mdevino Feb 6, 2025
01aa131
Move specific code back to canary_test.rs
mdevino Feb 6, 2025
6af1ac4
refactor: extract constants
mdevino Feb 6, 2025
1cd39d0
refactor: move grpc server macros to tests common module
mdevino Feb 6, 2025
33839b1
Add copyright notice to common test module
mdevino Feb 6, 2025
6eaf9fe
refactor: create function for orchestrator config
mdevino Feb 6, 2025
17435b0
Add chunkers logic to create_orchestrator_shared_state()
mdevino Feb 6, 2025
b3706ac
Change mocktail source to IBM git repo
mdevino Feb 7, 2025
f6e5408
Refactor chunker unary endpoint as constant
mdevino Feb 7, 2025
14325e8
Update create_orchestrator_shared_state() to return Result
mdevino Feb 10, 2025
6ab815b
Remove unwrap() calls
mdevino Feb 10, 2025
b51eb12
Allow dead code on test common file
mdevino Feb 10, 2025
4cd10d6
Remove unneeded changes in canary_test.rs
mdevino Feb 10, 2025
61b1757
Add copyright text to canary_test.rs
mdevino Feb 10, 2025
4c21b76
Remove allow macro from tests/common/mod.rs
mdevino Feb 11, 2025
d6c9172
wip: test isolated NLP streaming call
mdevino Feb 11, 2025
769df32
Break common test module into multiple files
mdevino Feb 12, 2025
99e4544
Test NLP generation stremaing call
mdevino Feb 12, 2025
f55765b
test case: streaming no detectors
mdevino Feb 12, 2025
a8cafe5
Add MockOrchestratorServer to replace usage of TestServer, add EventS…
declark1 Feb 13, 2025
fc65700
Refactor test util.rs module into orchestrator.rs
mdevino Feb 13, 2025
2b3fed6
Remove old create_orchestrator_shared_state()
mdevino Feb 13, 2025
45ec273
Move orchestrator.rs functions into TestOrchestratorServer impl
mdevino Feb 13, 2025
ba43415
Refactor test constants
mdevino Feb 13, 2025
6cf3c17
Test case: streaming.rs::test_input_detector_whole_doc_no_detections()
mdevino Feb 13, 2025
b3b215f
Refactoring
mdevino Feb 13, 2025
60284c3
Replace tracing-test crate with test-log
mdevino Feb 18, 2025
14e32d4
test case: test_input_detector_sentence_chunker_no_detections
mdevino Feb 18, 2025
a702fbf
test case: test_input_detector_returns_404
mdevino Feb 18, 2025
eab68a1
test case: test_input_detector_returns_503
mdevino Feb 18, 2025
0a35b7c
test case: test_input_detector_returns_non_compliant_message
mdevino Feb 18, 2025
76fe749
refactor: move ensure_global_rustls_state() call into TestOrchestrato…
mdevino Feb 18, 2025
0ab0d01
test case: test_input_detector_whole_doc_with_detections()
mdevino Feb 18, 2025
14abbfc
test case: test_input_detector_sentence_chunker_no_detections()
mdevino Feb 19, 2025
00d5864
test case: test_input_detector_sentence_chunker_with_detections()
mdevino Feb 19, 2025
d383098
refactor: rename/move integration testing constants
mdevino Feb 19, 2025
2e1d927
test case: test_input_detector_returns_an_error()
mdevino Feb 19, 2025
a7ced7e
test case: test_generation_server_returns_an_error()
mdevino Feb 19, 2025
f3a7c94
test case: test_orchestrator_receives_a_non_compliant_request()
mdevino Feb 19, 2025
a3736e9
Add grpc_dns_probe_interval tests
mdevino Feb 24, 2025
7e499d2
Rename streaming tests file
mdevino Feb 24, 2025
8246116
Create test-specific header name constants
mdevino Feb 24, 2025
6f7a0d6
Undo src/server.rs changes
mdevino Feb 24, 2025
49c3fea
Fix test_input_detector_returns_503() detector mock response status
mdevino Feb 24, 2025
6c5d512
refactor: move input to variable in tests/chunker.rs
mdevino Feb 25, 2025
817945c
Document chunker::test_isolated_chunker_unary_call()
mdevino Feb 27, 2025
69d3363
Rename tests/test.config.yaml
mdevino Feb 27, 2025
85fd281
Document test cases on comments
mdevino Feb 27, 2025
e5390d2
Add assertions to make sure no detections are returned
mdevino Feb 27, 2025
6c46d6c
Update mocktail to 0.1.2-alpha
mdevino Feb 28, 2025
517e980
Fix comments
mdevino Mar 3, 2025
6adcac9
Refactor common integration test code
mdevino Jan 3, 2025
0c07875
/detection/content base test case
mdevino Jan 6, 2025
4762954
Rename tests/detection_content.rs
mdevino Feb 24, 2025
09b2510
test case: test_no_detection_whole_doc()
mdevino Feb 24, 2025
aba1e1c
test case: test_no_detection_sentence_chunker()
mdevino Feb 24, 2025
70be320
test case: test_detector_returns_503()
mdevino Feb 24, 2025
9f1100f
test case: test_detector_returns_404()
mdevino Feb 24, 2025
203bceb
test case: test_detector_returns_500()
mdevino Feb 24, 2025
4124714
test case: test_detector_returns_non_compliant_message()
mdevino Feb 24, 2025
f997f1d
test case: test_chunker_returns_an_error()
mdevino Feb 24, 2025
5aa899a
test case: test_request_with_extra_fields_returns_422()
mdevino Feb 24, 2025
cf3e8da
test case: test_request_missing_detectors_field_returns_422()
mdevino Feb 24, 2025
fb5b5da
test case: test_request_missing_content_field_returns_422()
mdevino Feb 24, 2025
d3215bc
test case: test_request_with_empty_detectors_field_returns_422()
mdevino Feb 24, 2025
adb7b50
Add missing import to canary_test.rs
mdevino Feb 25, 2025
2ce212c
Update tests to use mocktail 0.1.2-alpha
mdevino Feb 28, 2025
bd4e07a
Add comments to text_content tests
mdevino Feb 28, 2025
f7ece50
test case: detection_on_generation::test_detection_above_default_thre…
mdevino Feb 25, 2025
cdeaeea
test case: detection_on_generation::test_detection_below_default_thre…
mdevino Feb 25, 2025
9e3b67b
test case: detection_on_generation::test_detector_returns_503()
mdevino Feb 25, 2025
830f9a1
test case: detection_on_generation::test_detector_returns_404()
mdevino Feb 25, 2025
bce22ca
test case: detection_on_generation::test_detector_returns_500()
mdevino Feb 25, 2025
f329e02
test case: detection_on_generation::test_detector_returns_non_complia…
mdevino Feb 25, 2025
d894740
test case: detection_on_generation::test_request_with_extra_fields_re…
mdevino Feb 25, 2025
8c10221
Change response logs to pretty
mdevino Feb 25, 2025
55a12f0
test case: detection_on_generation::test_request_missing_prompt_retur…
mdevino Feb 25, 2025
68d680d
test case: detection_on_generation::test_request_missing_generated_te…
mdevino Feb 25, 2025
0151e99
test case: detection_on_generation::test_request_missing_detectors_re…
mdevino Feb 25, 2025
e1f4a8b
test case: detection_on_generation::test_request_with_empty_detectors…
mdevino Feb 25, 2025
42d7caa
Add comments to detection_on_generation tests
mdevino Mar 3, 2025
69777f4
Fix comments
mdevino Mar 3, 2025
89d6adc
test case: context_docs_detection::test_detection_below_default_thres…
mdevino Feb 26, 2025
10a03ef
test case: context_docs_detection::test_detection_above_default_thres…
mdevino Feb 26, 2025
4577327
test case: context_docs_detection::test_detectior_returns_503()
mdevino Feb 26, 2025
5d43895
test case: context_docs_detection::test_detectior_returns_404()
mdevino Feb 26, 2025
a0e22e8
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
07c9044
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
2fcd89f
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
b1c132e
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
e5e38e2
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
08e4e29
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
d807372
test case: context_docs_detection::test_orchestrator_receives_a_reque…
mdevino Feb 26, 2025
5bfc573
Update tests to use mocktail 0.1.2-alpha
mdevino Feb 28, 2025
cb817bc
Add comments to context_docs tests
mdevino Mar 3, 2025
a0da2c7
Fix comments
mdevino Mar 3, 2025
ad2053b
Rename tests/detection_content.rs
mdevino Feb 24, 2025
9ebbe92
test case: chat_detection::test_detection_below_default_threshold_is_…
mdevino Feb 26, 2025
cdf0509
test case: chat_detection::test_detection_above_default_threshold_is_…
mdevino Feb 26, 2025
d189011
test case: chat_detection::test_detector_returns_503()
mdevino Feb 26, 2025
8e325d7
test case: chat_detection::test_detector_returns_404()
mdevino Feb 26, 2025
c92c23d
test case: chat_detection::test_detector_returns_500()
mdevino Feb 26, 2025
d0d71d5
Change detector messages
mdevino Feb 26, 2025
08aa872
test case: chat_detection::test_request_contains_extra_fields()
mdevino Feb 26, 2025
8c5e318
test case: chat_detection::test_request_missing_messages()
mdevino Feb 26, 2025
8af0a67
test case: chat_detection::test_request_missing_detector()
mdevino Feb 26, 2025
0978f87
test case: chat_detection::test_request_with_invalid_detector()
mdevino Feb 26, 2025
d328a6d
Update mocktail to 0.1.2-alpha
mdevino Feb 27, 2025
20d2afc
Add comments to chat_detection tests
mdevino Mar 3, 2025
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
845 changes: 495 additions & 350 deletions Cargo.lock

Large diffs are not rendered by default.

46 changes: 37 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ anyhow = "1.0.95"
async-trait = "0.1.85"
axum = { version = "0.8.1", features = ["json"] }
axum-extra = "0.10.0"
bytes = "1.10.0"
clap = { version = "4.5.26", features = ["derive", "env"] }
eventsource-stream = "0.2.3"
futures = "0.3.31"
Expand All @@ -28,27 +29,53 @@ http-body = "1.0"
http-body-util = "0.1.2"
http-serde = "2.1.1"
hyper = { version = "1.5.2", features = ["http1", "http2", "server"] }
hyper-rustls = { version = "0.27.5", features = ["ring"]}
hyper-rustls = { version = "0.27.5", features = ["ring"] }
hyper-timeout = "0.5.2"
hyper-util = { version = "0.1.10", features = ["server-auto", "server-graceful", "tokio"] }
hyper-util = { version = "0.1.10", features = [
"server-auto",
"server-graceful",
"tokio",
] }
opentelemetry = { version = "0.27.1", features = ["metrics", "trace"] }
opentelemetry-http = { version = "0.27.0", features = ["reqwest"] }
opentelemetry-otlp = { version = "0.27.0", features = ["grpc-tonic", "http-proto"] }
opentelemetry-otlp = { version = "0.27.0", features = [
"grpc-tonic",
"http-proto",
] }
opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio", "metrics"] }
pin-project-lite = "0.2.16"
prost = "0.13.4"
reqwest = { version = "0.12.12", features = ["blocking", "rustls-tls", "json"] }
rustls = {version = "0.23.21", default-features = false, features = ["ring", "std"]}
reqwest = { version = "0.12.12", features = [
"blocking",
"rustls-tls",
"json",
"stream",
] }
rustls = { version = "0.23.21", default-features = false, features = [
"ring",
"std",
] }
rustls-pemfile = "2.2.0"
rustls-webpki = "0.102.8"
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.135"
serde_yml = "0.0.12"
thiserror = "2.0.11"
tokio = { version = "1.43.0", features = ["rt", "rt-multi-thread", "parking_lot", "signal", "sync", "fs"] }
tokio-rustls = { version = "0.26.1", features = ["ring"]}
tokio = { version = "1.43.0", features = [
"rt",
"rt-multi-thread",
"parking_lot",
"signal",
"sync",
"fs",
] }
tokio-rustls = { version = "0.26.1", features = ["ring"] }
tokio-stream = { version = "0.1.17", features = ["sync"] }
tonic = { version = "0.12.3", features = ["tls", "tls-roots", "tls-webpki-roots"] }
tonic = { version = "0.12.3", features = [
"tls",
"tls-roots",
"tls-webpki-roots",
] }
tower = { version = "0.5.2", features = ["timeout"] }
tower-http = { version = "0.6.2", features = ["trace"] }
tracing = "0.1.41"
Expand All @@ -63,7 +90,8 @@ tonic-build = "0.12.3"
[dev-dependencies]
axum-test = "17.1.0"
faux = "0.1.12"
tracing-test = "0.2.5"
mocktail = { git = "https://github.com/IBM/mocktail", version = "0.1.2-alpha" }
test-log = "0.2.17"

[profile.release]
debug = false
Expand Down
106 changes: 53 additions & 53 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,59 +166,7 @@ pub async fn run(
}

// (2b) Add main guardrails server routes
let mut router = Router::new()
.route(
&format!("{}/classification-with-text-generation", API_PREFIX),
post(classification_with_gen),
)
.route(
&format!("{}/detection/stream-content", TEXT_API_PREFIX),
post(stream_content_detection),
)
.route(
&format!(
"{}/server-streaming-classification-with-text-generation",
API_PREFIX
),
post(stream_classification_with_gen),
)
.route(
&format!("{}/generation-detection", TEXT_API_PREFIX),
post(generation_with_detection),
)
.route(
&format!("{}/detection/content", TEXT_API_PREFIX),
post(detection_content),
)
.route(
&format!("{}/detection/chat", TEXT_API_PREFIX),
post(detect_chat),
)
.route(
&format!("{}/detection/context", TEXT_API_PREFIX),
post(detect_context_documents),
)
.route(
&format!("{}/detection/generated", TEXT_API_PREFIX),
post(detect_generated),
);

// If chat generation is configured, enable the chat completions detection endpoint.
if shared_state.orchestrator.config().chat_generation.is_some() {
info!("Enabling chat completions detection endpoint");
router = router.route(
"/api/v2/chat/completions-detection",
post(chat_completions_detection),
);
}

let app = router.with_state(shared_state).layer(
TraceLayer::new_for_http()
.make_span_with(utils::trace::incoming_request_span)
.on_request(utils::trace::on_incoming_request)
.on_response(utils::trace::on_outgoing_response)
.on_eos(utils::trace::on_outgoing_eos),
);
let app = get_app(shared_state);

// (2c) Generate main guardrails server handle based on whether TLS is needed
let listener: TcpListener = TcpListener::bind(&http_addr)
Expand Down Expand Up @@ -323,6 +271,58 @@ pub fn get_health_app(state: Arc<ServerState>) -> Router {
.with_state(state)
}

pub fn get_app(state: Arc<ServerState>) -> Router {
let mut router = Router::new()
.route(
&format!("{}/classification-with-text-generation", API_PREFIX),
post(classification_with_gen),
)
.route(
&format!(
"{}/server-streaming-classification-with-text-generation",
API_PREFIX
),
post(stream_classification_with_gen),
)
.route(
&format!("{}/generation-detection", TEXT_API_PREFIX),
post(generation_with_detection),
)
.route(
&format!("{}/detection/content", TEXT_API_PREFIX),
post(detection_content),
)
.route(
&format!("{}/detection/chat", TEXT_API_PREFIX),
post(detect_chat),
)
.route(
&format!("{}/detection/context", TEXT_API_PREFIX),
post(detect_context_documents),
)
.route(
&format!("{}/detection/generated", TEXT_API_PREFIX),
post(detect_generated),
);

// If chat generation is configured, enable the chat completions detection endpoint.
if state.orchestrator.config().chat_generation.is_some() {
info!("Enabling chat completions detection endpoint");
router = router.route(
"/api/v2/chat/completions-detection",
post(chat_completions_detection),
);
}

router.with_state(state).layer(
TraceLayer::new_for_http()
.make_span_with(utils::trace::incoming_request_span)
.on_request(utils::trace::on_incoming_request)
.on_response(utils::trace::on_outgoing_response)
.on_eos(utils::trace::on_outgoing_eos),
)
}

async fn health() -> Result<impl IntoResponse, ()> {
// NOTE: we are only adding the package information in the `health` endpoint to have this endpoint
// provide a non empty 200 response. If we need to add more information regarding dependencies version
Expand Down
36 changes: 27 additions & 9 deletions tests/canary_test.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@
/*
Copyright FMS Guardrails Orchestrator Authors

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.

*/

// This is needed because integration test files are compiled as separate crates.
// If any of the code in this file is not used by any of the test files, a warning about unused code is generated.
// For more: https://github.com/rust-lang/rust/issues/46379

use std::sync::Arc;
use test_log::test;

use axum_test::TestServer;
use common::orchestrator::ensure_global_rustls_state;
use fms_guardrails_orchestr8::{
config::OrchestratorConfig,
orchestrator::Orchestrator,
server::{get_health_app, ServerState},
};
use hyper::StatusCode;
use rustls::crypto::ring;
use serde_json::Value;
use tokio::sync::OnceCell;
use tracing::debug;
use tracing_test::traced_test;

pub mod common;

/// Async lazy initialization of shared state using tokio::sync::OnceCell
static ONCE: OnceCell<Arc<ServerState>> = OnceCell::const_new();

/// The actual async function that initializes the shared state if not already initialized
async fn shared_state() -> Arc<ServerState> {
let config = OrchestratorConfig::load("tests/test.config.yaml")
let config = OrchestratorConfig::load("tests/test_config.yaml")
.await
.unwrap();
let orchestrator = Orchestrator::new(config, false).await.unwrap();
Arc::new(ServerState::new(orchestrator))
}

fn ensure_global_rustls_state() {
let _ = ring::default_provider().install_default();
}

/// Checks if the health endpoint is working
/// NOTE: We do not currently mock client services yet, so this test is
/// superficially testing the client health endpoints on the orchestrator is accessible
/// and when the orchestrator is running (healthy) all the health endpoints return 200 OK.
/// This will happen even if the client services or their health endpoints are not found.
#[traced_test]
#[tokio::test]
#[test(tokio::test)]
async fn test_health() {
ensure_global_rustls_state();
let shared_state = ONCE.get_or_init(shared_state).await.clone();
Expand Down
Loading