mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-16 11:49:43 +02:00
git_status, git_diff et git_clone (rust/loki-rs/src/tools/git.rs) quand la façade tient la conversation : dépôt deviné comme le Go (sous-dossier demandé, racine, unique sous-dossier dépôt), sorties mêlées et bornées, jamais d'invite, clone limité à https:// et git@hôte: et rangé dans le dossier de la discussion. Toujours sur cette machine, cible distante ou non, comme le Go. Parité : TestPariteOutilsGit (deux dépôts, l'un modifié, l'autre sans commit ; aucun appel réseau) range tests/parity_tools/git.json ; tests/tools_git.rs le retrouve à l'identique. Le rejeu des journaux des outils est partagé (tests/common/toolsfx.rs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
70 lines
2.3 KiB
Rust
70 lines
2.3 KiB
Rust
//! Parité des outils fichiers et du shell avec le Go (lot 7).
|
|
//!
|
|
//! `tests/parity_tools/outils.json` : journal produit par `execTool`
|
|
//! (`internal/loki/parity_tools_test.go`, régénéré par
|
|
//! `LOKI_PARITY_UPDATE=1 go test ./internal/loki -run TestPariteOutils`). La
|
|
//! même arborescence est recréée ici et les mêmes appels passent, dans le même
|
|
//! ordre, par `tools::exec_local` (`common::toolsfx`) : libellés, résultats,
|
|
//! diffs, questions et arborescence finale doivent être ceux du Go.
|
|
|
|
mod common;
|
|
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
use std::sync::Mutex;
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use common::toolsfx::{FakeState, replay};
|
|
use loki_rs::chat::stream::tool_call_label;
|
|
use loki_rs::tools::{ToolCtx, exec_local};
|
|
|
|
#[tokio::test]
|
|
async fn outils_comme_le_go() {
|
|
let diffs = replay("outils").await;
|
|
assert!(
|
|
diffs.is_empty(),
|
|
"{} écart(s) avec le Go :\n\n{}",
|
|
diffs.len(),
|
|
diffs.join("\n\n")
|
|
);
|
|
}
|
|
|
|
/// Outils non portés, ou cible distante : c'est au Go de les exécuter.
|
|
#[tokio::test]
|
|
async fn le_reste_va_au_go() {
|
|
let st = FakeState {
|
|
ctx: ToolCtx {
|
|
cwd: PathBuf::from("/nulle-part"),
|
|
memory_dir: PathBuf::from("/nulle-part/memory"),
|
|
target: "poste".into(),
|
|
code: false,
|
|
},
|
|
reads: Mutex::new(HashMap::new()),
|
|
};
|
|
let a = |v: Value| v.as_object().unwrap().clone();
|
|
for (name, args) in [
|
|
("mem_read", json!({"file":"x"})),
|
|
("web_search", json!({"query":"x"})),
|
|
("write", json!({"file":"a","content":"x"})),
|
|
("edit", json!({"file":"a","old":"x","new":"y"})),
|
|
("bash", json!({"command":"ls"})),
|
|
] {
|
|
let args = a(args);
|
|
let label = tool_call_label(name, &args);
|
|
assert!(
|
|
exec_local(&st, false, name, &args, &label).await.is_none(),
|
|
"{name}"
|
|
);
|
|
}
|
|
// Une commande catastrophique est refusée ici, cible distante comprise.
|
|
let args = a(json!({"command":"rm -rf /"}));
|
|
let out = exec_local(&st, false, "bash", &args, "rm -rf /")
|
|
.await
|
|
.unwrap();
|
|
assert!(out.result.starts_with("[refusé] Commande bloquée"));
|
|
// read/grep/glob lisent toujours cette machine.
|
|
let args = a(json!({"pattern":"*"}));
|
|
assert!(exec_local(&st, false, "glob", &args, "*").await.is_some());
|
|
}
|