tawasuyu

tawasuyu/brahman

Rust 492TOML 194Markdown 53JSON 176939 Definiciones

⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd crates/modules/semantic_dht/minga-cli/tests/watcher.rs Rust · 129 líneas · 4.3 KB Historia Quién tocó qué crudo

chore: monorepo inicial con arje + minga + yahweh absorbidos 53dbdf0f1 · Sergio ·

1//! Tests del file watcher: el "puente humano" que convierte Minga en
2//! un VCS de fondo — el usuario edita archivos y Minga los versiona
3//! sin acción explícita.
4
5use std::fs;
6use std::time::Duration;
7
8use minga_cli::{cmd_init, cmd_status, cmd_watch};
9use tempfile::TempDir;
10
11/// Espera hasta que el `cmd_status` reporte `expected` claves en MST,
12/// o hasta `timeout`. Devuelve `true` si se alcanzó la cuenta.
13async fn wait_until_mst_size(
14    repo: &std::path::Path,
15    pass: &str,
16    expected: usize,
17    timeout: Duration,
18) -> bool {
19    let deadline = std::time::Instant::now() + timeout;
20    loop {
21        if let Ok(s) = cmd_status(repo, pass) {
22            if s.mst_len >= expected {
23                return true;
24            }
25        }
26        if std::time::Instant::now() >= deadline {
27            return false;
28        }
29        tokio::time::sleep(Duration::from_millis(80)).await;
30    }
31}
32
33#[tokio::test]
34async fn watcher_initial_scan_picks_up_existing_files() {
35    let dir = TempDir::new().unwrap();
36    let repo = dir.path().join("repo");
37    let watch = dir.path().join("src");
38    fs::create_dir(&watch).unwrap();
39    cmd_init(&repo, "p").unwrap();
40
41    // Escribimos archivos ANTES de arrancar el watcher.
42    fs::write(watch.join("a.rs"), "fn a() -> i32 { 1 }").unwrap();
43    fs::write(watch.join("b.rs"), "fn b() -> i32 { 2 }").unwrap();
44
45    // Arrancamos el watcher en una task. La pasada inicial debería
46    // ingerir ambos.
47    let repo_clone = repo.clone();
48    let handle = tokio::spawn(async move {
49        let _ = cmd_watch(&repo_clone, "p", &watch).await;
50    });
51
52    // Damos margen para la pasada inicial. cmd_watch tiene el repo
53    // abierto, pero cmd_status no puede mientras tanto (sled lock).
54    // Solución: cancelamos el watcher antes de medir.
55    tokio::time::sleep(Duration::from_millis(500)).await;
56    handle.abort();
57    let _ = handle.await;
58    tokio::time::sleep(Duration::from_millis(150)).await;
59
60    let s = cmd_status(&repo, "p").unwrap();
61    assert_eq!(s.mst_len, 2, "esperaba 2 funciones del initial scan");
62    assert_eq!(s.attestations_len, 2);
63}
64
65#[tokio::test]
66async fn watcher_ingests_new_file_after_creation() {
67    let dir = TempDir::new().unwrap();
68    let repo = dir.path().join("repo");
69    let watch = dir.path().join("src");
70    fs::create_dir(&watch).unwrap();
71    cmd_init(&repo, "p").unwrap();
72
73    // Watcher arranca con directorio vacío.
74    let repo_clone = repo.clone();
75    let watch_clone = watch.clone();
76    let handle = tokio::spawn(async move {
77        let _ = cmd_watch(&repo_clone, "p", &watch_clone).await;
78    });
79
80    // Margen para que el watcher se inicialice y registre con notify.
81    tokio::time::sleep(Duration::from_millis(300)).await;
82
83    // Creamos un archivo. notify debería emitir un evento y el
84    // watcher debería ingerirlo.
85    fs::write(watch.join("new.rs"), "fn new() -> i32 { 42 }").unwrap();
86
87    // Esperamos a que el evento se procese.
88    tokio::time::sleep(Duration::from_millis(800)).await;
89
90    // Detenemos el watcher para liberar el lock de sled antes de
91    // hacer cmd_status.
92    handle.abort();
93    let _ = handle.await;
94    tokio::time::sleep(Duration::from_millis(150)).await;
95
96    // Polling con timeout — algunos sistemas de archivos tienen
97    // latencia de eventos.
98    assert!(
99        wait_until_mst_size(&repo, "p", 1, Duration::from_secs(3)).await,
100        "el watcher no ingirió el archivo creado",
101    );
102}
103
104#[tokio::test]
105async fn watcher_ignores_non_rs_files() {
106    let dir = TempDir::new().unwrap();
107    let repo = dir.path().join("repo");
108    let watch = dir.path().join("src");
109    fs::create_dir(&watch).unwrap();
110    cmd_init(&repo, "p").unwrap();
111
112    // Pre-poblamos con un .rs y varios archivos no-Rust.
113    fs::write(watch.join("real.rs"), "fn real() -> i32 { 0 }").unwrap();
114    fs::write(watch.join("readme.md"), "# proyecto").unwrap();
115    fs::write(watch.join("data.json"), "{}").unwrap();
116
117    let repo_clone = repo.clone();
118    let handle = tokio::spawn(async move {
119        let _ = cmd_watch(&repo_clone, "p", &watch).await;
120    });
121
122    tokio::time::sleep(Duration::from_millis(500)).await;
123    handle.abort();
124    let _ = handle.await;
125    tokio::time::sleep(Duration::from_millis(150)).await;
126
127    let s = cmd_status(&repo, "p").unwrap();
128    assert_eq!(s.mst_len, 1, "solo el .rs debe haberse ingerido");
129}