返回 CodeWhale
tests.rs
根目录 / crates / tui / src / tools / pdf / tests.rs
1 use super::*;
2 use crate::tools::spec::{ToolExecutionOutcome, ToolTerminalStatus};
3
4 #[tokio::test]
5 async fn missing_binary_maps_to_bounded_failed_machine_contract() {
6 let temporary = tempfile::tempdir().expect("tempdir");
7 let missing = temporary.path().join("definitely-not-pdftotext");
8 let input = temporary.path().join("input.pdf");
9 std::fs::write(&input, b"%PDF-1.7\n%%EOF").expect("fixture");
10 let error = extract_path(
11 &input,
12 None,
13 PdfTextCommand::test(missing.as_os_str(), Duration::from_secs(1), None),
14 )
15 .await
16 .expect_err("missing binary");
17 assert_eq!(error, PdfTextError::BinaryUnavailable);
18
19 let error = into_tool_error(error);
20 let payload = match &error {
21 ToolError::NotAvailable { message } => {
22 assert!(message.len() < 512, "{message}");
23 serde_json::from_str::<serde_json::Value>(message).expect("JSON failure payload")
24 }
25 other => panic!("unexpected mapped error: {other:?}"),
26 };
27 assert_eq!(payload["type"], "binary_unavailable");
28 assert_eq!(payload["binary"], "pdftotext");
29 assert_eq!(
30 ToolExecutionOutcome::from_legacy(Err(error)).status,
31 ToolTerminalStatus::Failed
32 );
33 }
34
35 #[cfg(unix)]
36 fn executable_script(contents: &str) -> (tempfile::TempDir, std::path::PathBuf) {
37 use std::os::unix::fs::PermissionsExt;
38
39 let temporary = tempfile::tempdir().expect("tempdir");
40 let binary = temporary.path().join("fake-pdftotext");
41 std::fs::write(&binary, contents).expect("fake binary");
42 let mut permissions = std::fs::metadata(&binary).expect("metadata").permissions();
43 permissions.set_mode(0o700);
44 std::fs::set_permissions(&binary, permissions).expect("executable");
45 (temporary, binary)
46 }
47
48 #[cfg(unix)]
49 #[tokio::test]
50 async fn shared_adapter_forwards_page_window_and_returns_stdout() {
51 let (temporary, binary) = executable_script(
52 "#!/bin/sh\nprintf 'args:%s\\n' \"$*\"\nprintf 'page one\\fpage two\\n'\n",
53 );
54 let request = PdfTextCommand::test(binary.as_os_str(), Duration::from_secs(1), None);
55 let input = temporary.path().join("input with spaces.pdf");
56 std::fs::write(&input, b"fixture bytes").expect("fixture");
57 let text = extract_path(&input, Some((2, 4)), request)
58 .await
59 .expect("fake extraction");
60 assert!(text.contains("-layout -f 2 -l 4"), "{text}");
61 assert!(text.contains(input.to_string_lossy().as_ref()), "{text}");
62 assert!(text.ends_with("page one\u{c}page two\n"), "{text:?}");
63
64 let staged = extract_bytes(b"fetched fixture bytes", request)
65 .await
66 .expect("fake fetched extraction");
67 assert!(staged.contains("-layout"), "{staged}");
68 assert!(staged.ends_with("page one\u{c}page two\n"), "{staged:?}");
69 }
70
71 #[cfg(unix)]
72 #[tokio::test]
73 async fn child_execution_is_timeout_and_cancellation_bounded() {
74 let (temporary, binary) = executable_script("#!/bin/sh\nexec sleep 10\n");
75 let input = temporary.path().join("input.pdf");
76 std::fs::write(&input, b"fixture bytes").expect("fixture");
77 let started = std::time::Instant::now();
78 let error = extract_path(
79 &input,
80 None,
81 PdfTextCommand::test(binary.as_os_str(), Duration::from_millis(50), None),
82 )
83 .await
84 .expect_err("timeout");
85 assert_eq!(error, PdfTextError::TimedOut);
86 assert!(started.elapsed() < Duration::from_secs(2));
87
88 let cancel = CancellationToken::new();
89 let cancel_after_spawn = cancel.clone();
90 tokio::spawn(async move {
91 tokio::time::sleep(Duration::from_millis(50)).await;
92 cancel_after_spawn.cancel();
93 });
94 let started = std::time::Instant::now();
95 let error = extract_path(
96 &input,
97 None,
98 PdfTextCommand::test(binary.as_os_str(), Duration::from_secs(10), Some(&cancel)),
99 )
100 .await
101 .expect_err("cancelled");
102 assert_eq!(error, PdfTextError::Cancelled);
103 assert!(started.elapsed() < Duration::from_secs(2));
104 }
105
106 #[tokio::test]
107 async fn bounded_reader_drains_but_retains_only_the_prefix() {
108 let output = read_bounded(&b"0123456789"[..], 4).await.expect("read");
109 assert_eq!(output.bytes, b"0123");
110 assert!(output.truncated);
111 }
112
113 #[test]
114 fn stderr_sanitizer_removes_terminal_control_bytes() {
115 assert_eq!(sanitized_text(b"bad\x1b[31m\0\nnext"), "bad�[31m�\nnext");
116 }
117
117 lines RUST