use clap::CommandFactory;
use std::path::{Path, PathBuf};
const BANNED_CHANNELS: &[&str] = &["OPENROUTER_API_KEY", "SQLITE_GRAPHRAG_"];
const BANNED_PHRASES: &[&str] = &[
"env var",
"environment variable",
"variável de ambiente",
"variavel de ambiente",
];
fn help_texts(cmd: &mut clap::Command, path: String, out: &mut Vec<(String, String)>) {
out.push((path.clone(), cmd.render_long_help().to_string()));
let names: Vec<String> = cmd
.get_subcommands()
.map(|s| s.get_name().to_string())
.collect();
for name in names {
let child_path = format!("{path} {name}");
if let Some(child) = cmd.find_subcommand_mut(&name) {
help_texts(child, child_path, out);
}
}
}
const DENIAL_MARKERS: &[&str] = &[
"not read",
"never read",
"no environment variable",
"no product env",
"must not be used",
"não é lida",
"nao e lida",
"nenhuma variável de ambiente",
"nenhuma variavel de ambiente",
];
fn offences(text: &str) -> Vec<&'static str> {
let mut found = Vec::new();
for sentence in text.split(['.', '\n']) {
let lowered = sentence.to_lowercase();
if DENIAL_MARKERS.iter().any(|m| lowered.contains(m)) {
continue;
}
for needle in BANNED_CHANNELS {
if sentence.contains(needle) && !found.contains(needle) {
found.push(*needle);
}
}
for needle in BANNED_PHRASES {
if lowered.contains(&needle.to_lowercase()) && !found.contains(needle) {
found.push(*needle);
}
}
}
found
}
#[test]
fn the_guard_tells_an_offer_from_a_denial() {
assert!(
offences("Product environment variables are not read at runtime.").is_empty(),
"denying the channel must not count as offering it"
);
assert!(
offences("No environment variable supplies this value.").is_empty(),
"an explicit denial must pass"
);
assert_eq!(
offences("Falls back to OPENROUTER_API_KEY env var."),
vec!["OPENROUTER_API_KEY", "env var"],
"an offer must be reported, naming every token that made it one"
);
}
const SOURCE_EXEMPT: &[(&str, &str)] = &[(
"src/config/api_keys.rs",
"resolve_api_key_ignores_product_env proves OPENROUTER_API_KEY is IGNORED, \
and the only way to prove a variable is ignored is to set it and observe \
that nothing moved. Deleting the write would delete the proof.",
)];
const ENV_WRITERS: &[&str] = &["set_var(", "remove_var("];
struct Normalized {
text: String,
line_of_byte: Vec<usize>,
}
fn normalize(source: &str) -> Normalized {
let mut text = String::with_capacity(source.len());
let mut line_of_byte = Vec::with_capacity(source.len());
for (idx, raw) in source.lines().enumerate() {
let code = match raw.find("//") {
Some(pos) => &raw[..pos],
None => raw,
};
for ch in code.chars().filter(|c| !c.is_whitespace()) {
text.push(ch);
line_of_byte.resize(text.len(), idx + 1);
}
}
Normalized { text, line_of_byte }
}
fn offending_env_writes(source: &str) -> Vec<(usize, String)> {
let normalized = normalize(source);
let mut found = Vec::new();
for writer in ENV_WRITERS {
let mut from = 0;
while let Some(hit) = normalized.text[from..].find(writer) {
let at = from + hit;
let after = at + writer.len();
from = after;
let rest = &normalized.text[after..];
let Some(body) = rest.strip_prefix('"') else {
continue;
};
let Some(end) = body.find('"') else {
continue;
};
let name = &body[..end];
let banned = BANNED_CHANNELS.iter().any(|needle| {
if needle.ends_with('_') {
name.starts_with(needle)
} else {
name == *needle
}
});
if banned {
let line = normalized.line_of_byte.get(at).copied().unwrap_or(0);
found.push((line, name.to_string()));
}
}
}
found.sort();
found
}
fn rust_files(root: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(root) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
rust_files(&path, out);
} else if path.extension().is_some_and(|ext| ext == "rs") {
out.push(path);
}
}
}
fn relative(path: &Path, repo: &Path) -> String {
path.strip_prefix(repo)
.unwrap_or(path)
.to_string_lossy()
.replace('\\', "/")
}
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
#[test]
fn the_source_detector_tells_a_product_channel_from_a_system_one() {
let offence = "std::env::remove_var(\"SQLITE_GRAPHRAG_DISABLE_RETRY\");";
assert_eq!(
offending_env_writes(offence),
vec![(1, "SQLITE_GRAPHRAG_DISABLE_RETRY".to_string())],
"a write to a product variable must be reported, with its line"
);
assert_eq!(
offending_env_writes("std::env::set_var(\"OPENROUTER_API_KEY\", \"sk-x\");"),
vec![(1, "OPENROUTER_API_KEY".to_string())],
"the API key is one name, matched whole"
);
assert!(
offending_env_writes("std::env::set_var(\"XDG_RUNTIME_DIR\", dir);").is_empty(),
"a system channel must not be flagged: XDG_RUNTIME_DIR is a real contract"
);
assert!(
offending_env_writes(
"std::env::set_var(\"LC_ALL\", \"C\");\nstd::env::remove_var(\"NO_COLOR\");"
)
.is_empty(),
"POSIX locale and terminal channels are read by code the product does not own"
);
assert!(
offending_env_writes("// std::env::set_var(\"SQLITE_GRAPHRAG_LANG\", \"pt\");").is_empty(),
"prose documenting the banned call must not trip the gate"
);
assert_eq!(
offending_env_writes(
"std::env::set_var(\n \"SQLITE_GRAPHRAG_EMBEDDING_DIM\",\n \"384\",\n);"
)
.len(),
1,
"a rustfmt-split call must still be caught"
);
}
#[test]
fn no_source_file_manipulates_a_product_environment_variable() {
let repo = repo_root();
let mut files = Vec::new();
rust_files(&repo.join("src"), &mut files);
assert!(
files.len() > 100,
"the scan found {} files, which is too few to be the real tree — the \
walk is broken and this gate is passing on an empty set",
files.len()
);
let mut offences = Vec::new();
for path in &files {
let rel = relative(path, &repo);
if SOURCE_EXEMPT.iter().any(|(name, _)| *name == rel) {
continue;
}
let Ok(source) = std::fs::read_to_string(path) else {
continue;
};
for (line, name) in offending_env_writes(&source) {
offences.push(format!("{rel}:{line} writes {name}"));
}
}
offences.sort();
assert!(
offences.is_empty(),
"GAP-SG-232: source code writes an environment variable the product never \
reads. \
Configuration precedence is: CLI flag, then the XDG key via `config \
set`, then the compiled default — no product environment variable takes \
part at any layer, so setting or clearing one changes nothing and \
teaches the next reader that it does. Delete the call; if the test \
relied on it, the premise it needed comes from the XDG key or from the \
process-wide setter the code really uses.\n{}",
offences.join("\n")
);
}
#[test]
fn every_source_exemption_names_a_file_that_still_offends() {
let repo = repo_root();
for (name, reason) in SOURCE_EXEMPT {
assert!(
reason.len() > 40,
"the exemption for `{name}` has no real justification: {reason:?}"
);
let path = repo.join(name);
assert!(
path.is_file(),
"SOURCE_EXEMPT names `{name}`, which is gone"
);
let source = std::fs::read_to_string(&path).expect("exempt file must be readable");
assert!(
!offending_env_writes(&source).is_empty(),
"`{name}` is exempt but writes no product variable any more; delete the entry"
);
}
}
#[test]
fn no_help_text_anywhere_offers_the_environment_as_a_channel() {
let mut root = sqlite_graphrag::cli::Cli::command();
let mut rendered = Vec::new();
help_texts(&mut root, "sqlite-graphrag".to_string(), &mut rendered);
assert!(
rendered.len() > 30,
"the walk collected only {} help pages, which means it stopped short of \
the real subcommand tree and would pass by not looking",
rendered.len()
);
let mut failures = Vec::new();
for (path, text) in &rendered {
let found = offences(text);
if !found.is_empty() {
failures.push(format!("{path}: {found:?}"));
}
}
assert!(
failures.is_empty(),
"help text offers environment variables as a configuration channel, but \
the product never reads one. Point the operator at `config add-key \
--from-stdin` or the equivalent flag instead.\n{}",
failures.join("\n")
);
}
#[test]
fn no_error_message_or_suggestion_offers_the_environment_as_a_channel() {
use sqlite_graphrag::errors::AppError;
use sqlite_graphrag::i18n::Language;
let samples: Vec<AppError> = vec![
AppError::Validation("bad".into()),
AppError::Duplicate("dup".into()),
AppError::Conflict("stale".into()),
AppError::NotFound("missing".into()),
AppError::NamespaceError("ns".into()),
AppError::LimitExceeded("cap".into()),
AppError::Embedding("embed".into()),
AppError::DbBusy("busy".into()),
AppError::LockBusy("held".into()),
AppError::VecExtension("vec".into()),
];
let mut failures = Vec::new();
for err in &samples {
for lang in [Language::English, Language::Portuguese] {
let message = err.localized_message_for(lang);
let found = offences(&message);
if !found.is_empty() {
failures.push(format!("message/{lang:?}/{err:?}: {found:?}"));
}
if let Some(hint) = err.suggestion_for(lang) {
let found = offences(hint);
if !found.is_empty() {
failures.push(format!("suggestion/{lang:?}/{err:?}: {found:?}"));
}
}
}
}
assert!(
failures.is_empty(),
"an error envelope advertises an environment variable the product never \
reads:\n{}",
failures.join("\n")
);
}
#[test]
fn every_suggestion_is_translated_not_merely_present() {
use sqlite_graphrag::errors::AppError;
use sqlite_graphrag::i18n::Language;
let samples: Vec<AppError> = vec![
AppError::Validation("bad".into()),
AppError::Duplicate("dup".into()),
AppError::Conflict("stale".into()),
AppError::NotFound("missing".into()),
AppError::NamespaceError("ns".into()),
AppError::LimitExceeded("cap".into()),
AppError::Embedding("embed".into()),
AppError::LockBusy("held".into()),
AppError::VecExtension("vec".into()),
];
let mut untranslated = Vec::new();
for err in &samples {
let en = err.suggestion_for(Language::English);
let pt = err.suggestion_for(Language::Portuguese);
assert_eq!(
en.is_some(),
pt.is_some(),
"{err:?} offers a hint in one language only"
);
if let (Some(en), Some(pt)) = (en, pt) {
if en == pt {
untranslated.push(format!("{err:?}: {en}"));
}
}
}
assert!(
untranslated.is_empty(),
"these suggestions render identically in en and pt-BR, so they were \
never translated:\n{}",
untranslated.join("\n")
);
}
#[test]
fn the_retry_verdict_travels_with_every_classified_error() {
use sqlite_graphrag::errors::AppError;
let cases: Vec<(AppError, &str, bool)> = vec![
(AppError::DbBusy("busy".into()), "transient", true),
(AppError::LockBusy("held".into()), "transient", true),
(AppError::Validation("bad".into()), "permanent", false),
(AppError::NotFound("missing".into()), "permanent", false),
(AppError::Duplicate("dup".into()), "permanent", false),
(AppError::Conflict("stale".into()), "ambiguous", false),
(AppError::Embedding("embed".into()), "ambiguous", false),
];
for (err, expected_class, expected_retryable) in &cases {
assert_eq!(
err.error_class(),
*expected_class,
"{err:?} must classify as {expected_class}"
);
assert_eq!(
err.is_retryable(),
*expected_retryable,
"{err:?} retryable flag must agree with its class"
);
assert_eq!(
err.error_class() == "transient",
err.is_retryable(),
"{err:?}: `retryable` must be true exactly when the class is transient"
);
}
}