Skip to content

Commit da77c32

Browse files
committed
feat:unit tests for candle refactoring
Signed-off-by: OneZero-Y <[email protected]> feat:unit tests for candle refactoring Signed-off-by: OneZero-Y <[email protected]> feat:unit tests for candle refactoring Signed-off-by: OneZero-Y <[email protected]>
1 parent cfa82ea commit da77c32

34 files changed

+4136
-97
lines changed

candle-binding/Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,12 @@ serde_json = "1.0.93"
2222
tracing = "0.1.37"
2323
libc = "0.2.147"
2424
lazy_static = "1.4.0"
25-
rand = "0.8.5"
25+
rand = "0.8.5"
26+
27+
[dev-dependencies]
28+
rstest = "0.18"
29+
tokio = { version = "1.0", features = ["full"] }
30+
tempfile = "3.8"
31+
serial_test = "3.0"
32+
criterion = "0.5"
33+
async-std = { version = "1.12", features = ["attributes"] }

candle-binding/src/classifiers/lora/intent_lora_test.rs

Lines changed: 501 additions & 0 deletions
Large diffs are not rendered by default.

candle-binding/src/classifiers/lora/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,13 @@ pub use intent_lora::*;
1414
pub use parallel_engine::*;
1515
pub use pii_lora::*;
1616
pub use security_lora::*;
17+
18+
// Test modules (only compiled in test builds)
19+
#[cfg(test)]
20+
pub mod intent_lora_test;
21+
#[cfg(test)]
22+
pub mod pii_lora_test;
23+
#[cfg(test)]
24+
pub mod security_lora_test;
25+
#[cfg(test)]
26+
pub mod token_lora_test;
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
//! Tests for LoRA PII detector implementation
2+
3+
use super::pii_lora::*;
4+
use crate::test_fixtures::{fixtures::*, test_utils::*};
5+
use rstest::*;
6+
use serial_test::serial;
7+
use std::sync::Arc;
8+
9+
/// Test PIILoRAClassifier creation with cached model (OPTIMIZED)
10+
#[rstest]
11+
#[serial]
12+
fn test_pii_lora_pii_lora_classifier_new(cached_pii_classifier: Option<Arc<PIILoRAClassifier>>) {
13+
if let Some(classifier) = cached_pii_classifier {
14+
println!("Testing PIILoRAClassifier with cached model - instant access!");
15+
16+
// Test actual PII detection with cached model
17+
{
18+
let test_text = "My name is John Doe and my email is [email protected]";
19+
match classifier.detect_pii(test_text) {
20+
Ok(result) => {
21+
println!("Real model PII detection result: has_pii={}, types={:?}, confidence={:.3}, time={}ms",
22+
result.has_pii, result.pii_types, result.confidence, result.processing_time_ms);
23+
24+
// Validate real model output
25+
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
26+
assert!(result.processing_time_ms > 0);
27+
assert!(result.processing_time_ms < 10000);
28+
29+
// Check PII detection logic
30+
if result.has_pii {
31+
assert!(!result.pii_types.is_empty());
32+
assert!(!result.occurrences.is_empty());
33+
} else {
34+
assert!(result.pii_types.is_empty());
35+
assert!(result.occurrences.is_empty());
36+
}
37+
}
38+
Err(e) => {
39+
println!("Real model PII detection failed: {}", e);
40+
}
41+
}
42+
}
43+
} else {
44+
println!("Cached PII classifier not available, skipping test");
45+
}
46+
}
47+
48+
/// Test cached model batch PII detection (OPTIMIZED)
49+
#[rstest]
50+
#[serial]
51+
fn test_pii_lora_pii_lora_classifier_batch_detect(
52+
cached_pii_classifier: Option<Arc<PIILoRAClassifier>>,
53+
) {
54+
if let Some(classifier) = cached_pii_classifier {
55+
println!("Testing batch PII detection with cached model!");
56+
{
57+
let test_texts = vec![
58+
"Hello, my name is Alice",
59+
"Contact me at [email protected]",
60+
"My phone number is 555-1234",
61+
"This is a normal message without PII",
62+
];
63+
64+
match classifier.batch_detect(&test_texts) {
65+
Ok(results) => {
66+
println!(
67+
"Real model batch PII detection succeeded with {} results",
68+
results.len()
69+
);
70+
assert_eq!(results.len(), test_texts.len());
71+
72+
for (i, result) in results.iter().enumerate() {
73+
println!("Batch PII result {}: has_pii={}, types={:?}, confidence={:.3}, time={}ms",
74+
i, result.has_pii, result.pii_types, result.confidence, result.processing_time_ms);
75+
76+
// Validate each result
77+
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
78+
assert!(result.processing_time_ms > 0);
79+
80+
// Check PII detection consistency
81+
assert_eq!(result.has_pii, !result.pii_types.is_empty());
82+
assert_eq!(result.has_pii, !result.occurrences.is_empty());
83+
}
84+
}
85+
Err(e) => {
86+
println!("Real model batch PII detection failed: {}", e);
87+
}
88+
}
89+
}
90+
} else {
91+
println!("Cached PII classifier not available, skipping batch test");
92+
}
93+
}
94+
95+
/// Test cached model parallel PII detection (OPTIMIZED)
96+
#[rstest]
97+
#[serial]
98+
fn test_pii_lora_pii_lora_classifier_parallel_detect(
99+
cached_pii_classifier: Option<Arc<PIILoRAClassifier>>,
100+
) {
101+
if let Some(classifier) = cached_pii_classifier {
102+
println!("Testing parallel PII detection with cached model!");
103+
{
104+
let test_texts = vec![
105+
"My SSN is 123-45-6789",
106+
"Call me at (555) 123-4567",
107+
108+
];
109+
110+
match classifier.parallel_detect(&test_texts) {
111+
Ok(results) => {
112+
println!(
113+
"Real model parallel PII detection succeeded with {} results",
114+
results.len()
115+
);
116+
assert_eq!(results.len(), test_texts.len());
117+
118+
for (i, result) in results.iter().enumerate() {
119+
println!("Parallel PII result {}: has_pii={}, types={:?}, confidence={:.3}, time={}ms",
120+
i, result.has_pii, result.pii_types, result.confidence, result.processing_time_ms);
121+
122+
// Validate each result
123+
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
124+
assert!(result.processing_time_ms > 0);
125+
126+
// Check PII detection consistency
127+
assert_eq!(result.has_pii, !result.pii_types.is_empty());
128+
assert_eq!(result.has_pii, !result.occurrences.is_empty());
129+
130+
// Validate occurrences if PII detected
131+
if result.has_pii {
132+
for occurrence in &result.occurrences {
133+
assert!(!occurrence.pii_type.is_empty());
134+
assert!(!occurrence.token.is_empty());
135+
assert!(
136+
occurrence.confidence >= 0.0 && occurrence.confidence <= 1.0
137+
);
138+
assert!(occurrence.start_pos <= occurrence.end_pos);
139+
}
140+
}
141+
}
142+
}
143+
Err(e) => {
144+
println!("Real model parallel PII detection failed: {}", e);
145+
}
146+
}
147+
}
148+
} else {
149+
println!("Cached PII classifier not available, skipping parallel test");
150+
}
151+
}
152+
153+
/// Test PIILoRAClassifier error handling with cached model (OPTIMIZED)
154+
#[rstest]
155+
#[serial]
156+
fn test_pii_lora_pii_lora_classifier_error_handling(
157+
cached_pii_classifier: Option<Arc<PIILoRAClassifier>>,
158+
) {
159+
if let Some(classifier) = cached_pii_classifier {
160+
println!("Testing error handling with cached model!");
161+
162+
// Test with cached model first (should work)
163+
let test_text = "Test error handling";
164+
match classifier.detect_pii(test_text) {
165+
Ok(_) => println!("Cached model error handling test passed"),
166+
Err(e) => println!("Cached model error: {}", e),
167+
}
168+
} else {
169+
println!("Cached PII classifier not available, skipping error handling test");
170+
}
171+
172+
// Test error scenarios with invalid paths
173+
let invalid_model_result = PIILoRAClassifier::new("", true);
174+
assert!(invalid_model_result.is_err());
175+
176+
let nonexistent_model_result = PIILoRAClassifier::new("/nonexistent/path/to/model", true);
177+
assert!(nonexistent_model_result.is_err());
178+
179+
println!("PIILoRAClassifier error handling test passed");
180+
}
181+
182+
/// Test PII detection output format with cached model (OPTIMIZED)
183+
#[rstest]
184+
#[serial]
185+
fn test_pii_lora_pii_detection_output_format(
186+
cached_pii_classifier: Option<Arc<PIILoRAClassifier>>,
187+
) {
188+
if let Some(classifier) = cached_pii_classifier {
189+
println!("Testing PII detection output format with cached model!");
190+
191+
let test_text = "My name is John Doe and my email is [email protected]";
192+
match classifier.detect_pii(test_text) {
193+
Ok(result) => {
194+
// Test output format
195+
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
196+
assert!(result.processing_time_ms > 0);
197+
198+
// Test PII types format (adapt to real model output)
199+
for pii_type in &result.pii_types {
200+
assert!(!pii_type.is_empty());
201+
assert!(pii_type
202+
.chars()
203+
.all(|c| c.is_ascii_alphabetic() || c == '_' || c == '-'));
204+
println!(" Detected PII type: '{}'", pii_type);
205+
}
206+
207+
println!("PII detection output format test passed with cached model");
208+
}
209+
Err(e) => {
210+
println!("PII detection failed: {}", e);
211+
}
212+
}
213+
} else {
214+
println!("Cached PII classifier not available, skipping output format test");
215+
}
216+
}
217+
218+
/// Test PII type classification with cached model (OPTIMIZED)
219+
#[rstest]
220+
#[serial]
221+
fn test_pii_lora_pii_type_classification(cached_pii_classifier: Option<Arc<PIILoRAClassifier>>) {
222+
if let Some(classifier) = cached_pii_classifier {
223+
println!("Testing PII type classification with cached model!");
224+
225+
let test_text = "My name is John Doe and my email is [email protected]";
226+
match classifier.detect_pii(test_text) {
227+
Ok(result) => {
228+
for pii_type in &result.pii_types {
229+
assert!(pii_type
230+
.chars()
231+
.all(|c| c.is_ascii_alphabetic() || c == '_' || c == '-'));
232+
println!(" Detected PII type: '{}'", pii_type);
233+
}
234+
println!("PII type classification test passed with cached model");
235+
}
236+
Err(e) => println!("PII type classification failed: {}", e),
237+
}
238+
} else {
239+
println!("Cached PII classifier not available, skipping type classification test");
240+
}
241+
}
242+
243+
/// Test token-level PII detection with cached model (OPTIMIZED)
244+
#[rstest]
245+
#[serial]
246+
fn test_pii_lora_token_level_pii_detection(cached_pii_classifier: Option<Arc<PIILoRAClassifier>>) {
247+
if let Some(classifier) = cached_pii_classifier {
248+
println!("Testing token-level PII detection with cached model!");
249+
250+
let test_text = "My name is John Doe and my email is [email protected]";
251+
match classifier.detect_pii(test_text) {
252+
Ok(result) => {
253+
// Test token-level detection
254+
for occurrence in &result.occurrences {
255+
assert!(occurrence.start_pos <= occurrence.end_pos);
256+
assert!(!occurrence.pii_type.is_empty());
257+
assert!(occurrence.confidence >= 0.0 && occurrence.confidence <= 1.0);
258+
println!(
259+
" Token PII: '{}' at {}:{}, type='{}', confidence={:.3}",
260+
occurrence.token,
261+
occurrence.start_pos,
262+
occurrence.end_pos,
263+
occurrence.pii_type,
264+
occurrence.confidence
265+
);
266+
}
267+
println!("Token-level PII detection test passed with cached model");
268+
}
269+
Err(e) => println!("Token-level PII detection failed: {}", e),
270+
}
271+
} else {
272+
println!("Cached PII classifier not available, skipping token-level test");
273+
}
274+
}
275+
276+
/// Performance test for PIILoRAClassifier cached model operations (OPTIMIZED)
277+
#[rstest]
278+
#[serial]
279+
fn test_pii_lora_pii_lora_classifier_performance(
280+
cached_pii_classifier: Option<Arc<PIILoRAClassifier>>,
281+
) {
282+
if let Some(classifier) = cached_pii_classifier {
283+
println!("Testing PIILoRAClassifier cached model performance");
284+
285+
let test_texts = vec![
286+
"My name is John Doe and my email is [email protected]",
287+
"Contact Alice at [email protected] or call 555-1234",
288+
"The weather is nice today",
289+
];
290+
291+
let (_, total_duration) = measure_execution_time(|| {
292+
for text in &test_texts {
293+
let (_, single_duration) =
294+
measure_execution_time(|| match classifier.detect_pii(text) {
295+
Ok(result) => {
296+
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
297+
assert!(result.processing_time_ms > 0);
298+
}
299+
Err(e) => println!("Performance test failed for '{}': {}", text, e),
300+
});
301+
assert!(
302+
single_duration.as_secs() < 15,
303+
"Single PII detection took too long: {:?}",
304+
single_duration
305+
);
306+
}
307+
});
308+
309+
assert!(
310+
total_duration.as_secs() < 60,
311+
"Batch PII processing took too long: {:?}",
312+
total_duration
313+
);
314+
println!(
315+
"PIILoRAClassifier cached model performance: {} texts in {:?}",
316+
test_texts.len(),
317+
total_duration
318+
);
319+
} else {
320+
println!("Cached PII classifier not available, skipping performance test");
321+
}
322+
}

0 commit comments

Comments
 (0)