mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 14:38:12 +02:00
Lot 45 : outils d'image (see_image, web_screenshot) tenus par la façade
La façade exécute les deux outils qui montrent une image au modèle (src/imagetools.rs, port de chat_vision_tool.go, chat_screenshot.go et prepareImageForModel) : préparation de l'image comme les pièces jointes (orientation EXIF, grand côté sous 1568 px, mêmes pixels), sonde du moteur (/props authentifié, gardée 10 s ; preset externe : sa case), see_image aux textes du Go, web_screenshot par Playwright (adresses locales quand internet est coupé, mêmes arguments, erreur résumée, capture rangée dans le dossier de la discussion, ménage à 20 captures et 40 Mo, relayée au modèle s'il voit les images). Le Go garde les siens pour ses sous-agents et tâches IA. Parité : journal tiré du Go avec ses fichiers, un faux moteur et un faux Playwright (tests/parity_images/images.json), rejoué par tests/imagetools.rs ; un tour de la façade sans passer par le Go (tests/lot6.rs) ; tests unitaires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
This commit is contained in:
11 files changed
+2090
No files matched your search
@@ -0,0 +1,150 @@
|
||||
//! Tests unitaires des outils d'image (lot 45) ; la parité avec le Go est
|
||||
//! dans `tests/imagetools.rs`.
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn types_d_image() {
|
||||
assert_eq!(image_mime("a.PNG"), "image/png");
|
||||
assert_eq!(image_mime("/x/b.jpeg"), "image/jpeg");
|
||||
assert_eq!(image_mime("c.Jpg"), "image/jpeg");
|
||||
assert_eq!(image_mime("d.gif"), "image/gif");
|
||||
assert_eq!(image_mime("e.webp"), "image/webp");
|
||||
assert_eq!(image_mime("f.bmp"), "image/bmp");
|
||||
assert_eq!(image_mime("g.svg"), "");
|
||||
assert_eq!(image_mime("png"), "");
|
||||
assert_eq!(image_mime(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noms_de_capture() {
|
||||
assert_eq!(safe_slug("localhost:3000"), "localhost-3000");
|
||||
assert_eq!(safe_slug("www.exemple.test"), "www.exemple.test");
|
||||
assert_eq!(safe_slug("..--a b--.."), "a-b");
|
||||
assert_eq!(safe_slug("été"), "t");
|
||||
assert_eq!(safe_slug("::"), "page");
|
||||
assert_eq!(safe_slug(""), "page");
|
||||
assert_eq!(safe_slug(&"x".repeat(60)).len(), 40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adresses_locales() {
|
||||
for u in [
|
||||
"http://localhost:3000/app",
|
||||
"http://127.0.0.1/",
|
||||
"http://[::1]:8080/",
|
||||
"http://0.0.0.0:5173",
|
||||
"https://app.localhost/",
|
||||
"http://moi:secret@localhost/",
|
||||
] {
|
||||
assert!(is_local_url(u), "{u}");
|
||||
}
|
||||
for u in [
|
||||
"https://exemple.test/",
|
||||
"http://localhost.exemple.test/",
|
||||
"http://LOCALHOST/",
|
||||
"http://127.0.0.2/",
|
||||
"pas une url",
|
||||
"http://localhost:abc/",
|
||||
] {
|
||||
assert!(!is_local_url(u), "{u}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dernieres_lignes() {
|
||||
assert_eq!(last_lines("a\n\nb\n \nc\nd\n", 3), "b · c · d");
|
||||
assert_eq!(last_lines("seule", 6), "seule");
|
||||
assert_eq!(last_lines("", 6), "");
|
||||
assert_eq!(last_lines("\n \n", 6), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_preparee() {
|
||||
// Petite PNG : rien à faire, les octets d'origine.
|
||||
let mut png = Vec::new();
|
||||
image::RgbaImage::from_pixel(10, 5, image::Rgba([1, 2, 3, 255]))
|
||||
.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
prepare_image_for_model(&png, "image/png"),
|
||||
(png.clone(), "image/png".into())
|
||||
);
|
||||
// Indécodable : telle quelle.
|
||||
assert_eq!(
|
||||
prepare_image_for_model(b"pas une image", "image/png"),
|
||||
(b"pas une image".to_vec(), "image/png".into())
|
||||
);
|
||||
// Trop grande : réduite sous 1568 px, en PNG.
|
||||
let mut big = Vec::new();
|
||||
image::RgbaImage::from_pixel(3136, 100, image::Rgba([9, 9, 9, 255]))
|
||||
.write_to(&mut std::io::Cursor::new(&mut big), image::ImageFormat::Png)
|
||||
.unwrap();
|
||||
let (b, mime) = prepare_image_for_model(&big, "image/png");
|
||||
assert_eq!(mime, "image/png");
|
||||
let img = image::load_from_memory(&b).unwrap();
|
||||
assert_eq!((img.width(), img.height()), (1568, 50));
|
||||
let part = image_url_part(b"ab", "image/gif");
|
||||
assert_eq!(part["image_url"]["url"], "data:image/gif;base64,YWI=");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn menage_des_captures() {
|
||||
let d = tempfile::tempdir().unwrap();
|
||||
let base = SystemTime::now() - Duration::from_secs(1000);
|
||||
for i in 0..25u64 {
|
||||
let p = d.path().join(format!("c{i:02}.jpg"));
|
||||
std::fs::write(&p, "x").unwrap();
|
||||
std::fs::File::options()
|
||||
.write(true)
|
||||
.open(&p)
|
||||
.unwrap()
|
||||
.set_modified(base + Duration::from_secs(i))
|
||||
.unwrap();
|
||||
}
|
||||
// La plus ancienne est celle qu'on vient de prendre : elle reste.
|
||||
let keep = d.path().join("c00.jpg");
|
||||
prune_captures(d.path(), &keep);
|
||||
let mut left: Vec<String> = std::fs::read_dir(d.path())
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.map(|e| e.file_name().to_string_lossy().into_owned())
|
||||
.collect();
|
||||
left.sort();
|
||||
assert_eq!(left.len(), 20);
|
||||
assert_eq!(left[0], "c00.jpg");
|
||||
assert_eq!(left[1], "c06.jpg");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn capture_refusee_avant_playwright() {
|
||||
let d = tempfile::tempdir().unwrap();
|
||||
let args = |v: Value| serde_json::from_value::<Map<String, Value>>(v).unwrap();
|
||||
let s = web_screenshot(&args(json!({"url": "http://x/"})), "", d.path(), true, "t").await;
|
||||
assert!(
|
||||
s.result.contains("Playwright n'est pas installé"),
|
||||
"{}",
|
||||
s.result
|
||||
);
|
||||
let s = web_screenshot(&args(json!({"url": 3})), "/bin/false", d.path(), true, "t").await;
|
||||
assert_eq!(s.result, "[erreur] url manquante");
|
||||
assert!(s.rel.is_empty());
|
||||
assert!(!d.path().join("captures").exists());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn voir_sans_poste_distant() {
|
||||
let d = tempfile::tempdir().unwrap();
|
||||
let (r, img) = see_image("a.png", d.path(), "nœud", true, async { true }).await;
|
||||
assert!(r.contains("poste distant"), "{r}");
|
||||
assert!(img.is_none());
|
||||
let (r, _) = see_image("", d.path(), "", true, async { true }).await;
|
||||
assert_eq!(r, "[erreur] chemin de fichier manquant");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horodatage_local() {
|
||||
let s = local_stamp();
|
||||
assert_eq!(s.len(), 15);
|
||||
assert_eq!(&s[8..9], "-");
|
||||
}
|
||||
Reference in new issue
Block a user