mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-22 16:25:58 +00:00
test(bootstrap): cover CP1252 stderr decode and UTF-8 BOM cache writes
Locks the #67193 invariants: localized PowerShell error bytes survive decode_console_bytes / read_decoded_line (including CP1252-only 0x91/0x92 punctuation), cached .ps1 files get a single UTF-8 BOM for Windows PowerShell 5.1 -File, .sh stays BOM-less, and mutable branch caches plan a refresh with stale-cache fallback.
This commit is contained in:
parent
4ce1994159
commit
acee4f25c7
2 changed files with 106 additions and 0 deletions
|
|
@ -349,4 +349,58 @@ mod tests {
|
|||
assert_eq!(sanitize_ref("main"), "main");
|
||||
assert_eq!(sanitize_ref("release/1.2.3"), "release_1.2.3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_cached_ps1_prefixes_utf8_bom() {
|
||||
let out = prepare_cached_script_bytes(ScriptKind::Ps1, b"Write-Host hi\n");
|
||||
assert!(out.starts_with(UTF8_BOM), "cached .ps1 must start with UTF-8 BOM");
|
||||
assert_eq!(&out[UTF8_BOM.len()..], b"Write-Host hi\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_cached_ps1_does_not_double_bom() {
|
||||
let mut already = UTF8_BOM.to_vec();
|
||||
already.extend_from_slice(b"x");
|
||||
let out = prepare_cached_script_bytes(ScriptKind::Ps1, &already);
|
||||
assert_eq!(out, already);
|
||||
assert_eq!(out.windows(3).filter(|w| *w == UTF8_BOM).count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_cached_sh_stays_bomless() {
|
||||
let out = prepare_cached_script_bytes(ScriptKind::Sh, b"#!/bin/bash\n");
|
||||
assert!(!out.starts_with(UTF8_BOM));
|
||||
assert_eq!(out, b"#!/bin/bash\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_pins_are_immutable_branch_pins_are_not() {
|
||||
// Mirrors the resolve() immutable decision: SHA pins may reuse cache
|
||||
// forever; branch pins must refresh so Retry cannot keep a bad script.
|
||||
assert!(is_valid_commit("02d26981d3d4ad50e142399b8476f59ad5953ff0"));
|
||||
assert!(!is_valid_commit("main"));
|
||||
assert!(!is_valid_commit("release/1.2.3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn existing_branch_cache_plans_refresh_with_stale_fallback() {
|
||||
// Resolver-level: a prior install-main.ps1 must not short-circuit
|
||||
// Retry — mutable pins refresh, and only fall back if download fails.
|
||||
assert_eq!(
|
||||
cache_plan(/*immutable=*/ false, /*cached_exists=*/ true),
|
||||
CachePlan::Fetch { stale_ok: true }
|
||||
);
|
||||
assert_eq!(
|
||||
cache_plan(/*immutable=*/ true, /*cached_exists=*/ true),
|
||||
CachePlan::Reuse
|
||||
);
|
||||
assert_eq!(
|
||||
cache_plan(/*immutable=*/ false, /*cached_exists=*/ false),
|
||||
CachePlan::Fetch { stale_ok: false }
|
||||
);
|
||||
assert_eq!(
|
||||
cache_plan(/*immutable=*/ true, /*cached_exists=*/ false),
|
||||
CachePlan::Fetch { stale_ok: false }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,4 +447,56 @@ info line
|
|||
"unexpected powershell path: {normalized}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_console_bytes_keeps_valid_utf8() {
|
||||
assert_eq!(decode_console_bytes("café — ok".as_bytes()), "café — ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_console_bytes_preserves_cp1252_portuguese_error() {
|
||||
// "Não foi fornecido o terminador..." as Windows PowerShell 5.1 emits
|
||||
// under CP1252 (0xE3 = ã). BufReader::lines() previously failed here
|
||||
// with "stream did not contain valid UTF-8" and the UI only showed "No".
|
||||
let bytes: &[u8] = b"N\xE3o foi fornecido o terminador";
|
||||
assert_eq!(decode_console_bytes(bytes), "Não foi fornecido o terminador");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_console_bytes_maps_cp1252_only_punctuation() {
|
||||
// 0x91/0x92 are curly quotes in Windows-1252, but C1 controls under
|
||||
// Latin-1 (`b as char`). This locks the real CP1252 fallback.
|
||||
let bytes: &[u8] = b"say \x91hi\x92";
|
||||
assert_eq!(decode_console_bytes(bytes), "say \u{2018}hi\u{2019}");
|
||||
assert_ne!(
|
||||
decode_console_bytes(bytes),
|
||||
bytes.iter().map(|&b| b as char).collect::<String>(),
|
||||
"Latin-1 byte mapping must not be used for the 0x80..=0x9F range"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_decoded_line_survives_non_utf8_and_crlf() {
|
||||
let data: &[u8] = b"N\xE3o erro\r\nnext\n";
|
||||
let mut reader = BufReader::new(data);
|
||||
let mut buf = Vec::new();
|
||||
assert_eq!(
|
||||
read_decoded_line(&mut reader, &mut buf)
|
||||
.await
|
||||
.unwrap()
|
||||
.as_deref(),
|
||||
Some("Não erro")
|
||||
);
|
||||
assert_eq!(
|
||||
read_decoded_line(&mut reader, &mut buf)
|
||||
.await
|
||||
.unwrap()
|
||||
.as_deref(),
|
||||
Some("next")
|
||||
);
|
||||
assert!(read_decoded_line(&mut reader, &mut buf)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue