返回 CodeWhale
tests.rs
根目录 / crates / tui / src / tools / fetch_url / tests.rs
1 use super::*;
2 use crate::tools::spec::ToolContext;
3 use std::path::PathBuf;
4
5 struct ArtifactRootRestore(Option<PathBuf>);
6
7 impl Drop for ArtifactRootRestore {
8 fn drop(&mut self) {
9 crate::artifacts::set_test_artifact_sessions_root(self.0.take());
10 }
11 }
12
13 fn runtime() -> tokio::runtime::Runtime {
14 tokio::runtime::Builder::new_current_thread()
15 .enable_all()
16 .build()
17 .expect("test runtime")
18 }
19
20 #[test]
21 fn raw_pdf_production_path_preserves_exact_bytes_without_extractor() {
22 let _lock = crate::artifacts::TEST_ARTIFACT_SESSIONS_GUARD
23 .lock()
24 .unwrap_or_else(|error| error.into_inner());
25 let temporary = tempfile::tempdir().expect("artifact root");
26 let prior =
27 crate::artifacts::set_test_artifact_sessions_root(Some(temporary.path().join("sessions")));
28 let _restore = ArtifactRootRestore(prior);
29 let bytes = b"%PDF-1.7\nraw fixture that is intentionally not parseable\n%%EOF";
30 let missing = temporary.path().join("definitely-not-pdftotext");
31 let document = runtime()
32 .block_on(extract_fetched_document(
33 Format::Raw,
34 "https://example.com/raw.pdf",
35 "application/pdf",
36 bytes,
37 true,
38 None,
39 PdfTextCommand::test(missing.as_os_str(), Duration::from_millis(50), None),
40 ))
41 .expect("signed raw PDF must bypass the missing extractor");
42 let (content, artifact) = render_extracted(
43 "https://example.com/raw.pdf",
44 "application/pdf",
45 Format::Raw,
46 document,
47 bytes,
48 &ToolContext::new("."),
49 )
50 .expect("raw PDF preservation must not require pdftotext");
51 let artifact = artifact.expect("raw PDF artifact");
52 assert!(content.contains("PDF response saved"), "{content}");
53 assert_eq!(std::fs::read(artifact.absolute_path).unwrap(), bytes);
54 }
55
56 #[test]
57 fn raw_pdf_spoofs_and_contradictory_media_mime_create_no_artifact() {
58 let _lock = crate::artifacts::TEST_ARTIFACT_SESSIONS_GUARD
59 .lock()
60 .unwrap_or_else(|error| error.into_inner());
61 let temporary = tempfile::tempdir().expect("artifact root");
62 let sessions = temporary.path().join("sessions");
63 let prior = crate::artifacts::set_test_artifact_sessions_root(Some(sessions.clone()));
64 let _restore = ArtifactRootRestore(prior);
65 let missing = temporary.path().join("definitely-not-pdftotext");
66 let request = PdfTextCommand::test(missing.as_os_str(), Duration::from_millis(50), None);
67 let runtime = runtime();
68
69 for (url, content_type, bytes, expected) in [
70 (
71 "https://example.com/download",
72 "application/pdf",
73 b"plain text pretending to be a PDF".as_slice(),
74 "PDF signature",
75 ),
76 (
77 "https://example.com/spoof.pdf",
78 "text/plain",
79 b"plain text pretending to be a PDF".as_slice(),
80 "PDF signature",
81 ),
82 (
83 "https://example.com/signed",
84 "image/png",
85 b"%PDF-1.7\n%%EOF".as_slice(),
86 "did not match its PDF bytes",
87 ),
88 ] {
89 let error = runtime
90 .block_on(extract_fetched_document(
91 Format::Raw,
92 url,
93 content_type,
94 bytes,
95 true,
96 None,
97 request,
98 ))
99 .expect_err("invalid PDF response must fail before raw preservation");
100 assert!(error.to_string().contains(expected), "{error}");
101 }
102 assert!(
103 !sessions.exists(),
104 "rejected bytes must not create artifacts"
105 );
106 }
107
108 #[tokio::test]
109 async fn fetched_pdf_missing_helper_is_a_failed_typed_outcome() {
110 let temporary = tempfile::tempdir().expect("tempdir");
111 let missing = temporary.path().join("definitely-not-pdftotext");
112 let error = extract_fetched_document(
113 Format::Text,
114 "https://example.com/document.pdf",
115 "application/pdf",
116 b"%PDF-1.7\n%%EOF",
117 true,
118 None,
119 PdfTextCommand::test(missing.as_os_str(), Duration::from_secs(1), None),
120 )
121 .await
122 .expect_err("missing helper must fail the fetched PDF call");
123 let payload = match &error {
124 ToolError::NotAvailable { message } => {
125 serde_json::from_str::<Value>(message).expect("structured unavailable payload")
126 }
127 other => panic!("unexpected error: {other:?}"),
128 };
129 assert_eq!(payload["type"], "binary_unavailable");
130 assert_eq!(
131 crate::tools::spec::ToolExecutionOutcome::from_legacy(Err(error)).status,
132 crate::tools::spec::ToolTerminalStatus::Failed
133 );
134 }
135
135 lines RUST