newt-core 0.8.0

Newt-Agent core types, errors, and the NeMoCode-style tier router
use super::*;

#[test]
fn parses_codex_http_with_exact_name_and_only_credential_references() {
    let text = r#"
[mcp_servers."Case.Sensitive-name"]
url = "https://mcp.example.test/rpc"
enabled = false
bearer_token_env_var = "MCP_BEARER_TOKEN"
env_http_headers = { X-API-Key = "MCP_API_KEY", X-Trace = "TRACE_ID" }
"#;

    let got = parse_codex_mcp_toml(text);
    assert_eq!(got.len(), 1);
    let server = &got[0];
    assert_eq!(server.name, "Case.Sensitive-name");
    assert!(!server.enabled);
    assert_eq!(server.transport, TransportKind::Http);
    assert_eq!(server.url.as_deref(), Some("https://mcp.example.test/rpc"));
    assert_eq!(server.trust, McpTrust::Untrusted);
    assert_eq!(server.headers.len(), 3);
    assert_eq!(
        server.headers.get("Authorization"),
        Some(&SecretValue::literal("Bearer ${env:MCP_BEARER_TOKEN}"))
    );
    assert_eq!(
        server.headers.get("X-API-Key"),
        Some(&SecretValue::Ref(SecretRef {
            env: Some("MCP_API_KEY".into()),
            ..Default::default()
        }))
    );
    assert_eq!(
        server.headers.get("X-Trace"),
        Some(&SecretValue::Ref(SecretRef {
            env: Some("TRACE_ID".into()),
            ..Default::default()
        }))
    );
}
#[test]
fn codex_parser_reports_literal_headers_and_environment_values_without_values() {
    let text = r#"
[mcp_servers.literal-env]
command = "local-server"
env = { RUST_LOG = "debug", ACCESS_TOKEN = "literal-secret" }

[mcp_servers.literal-headers]
url = "https://mcp.example.test/rpc"
http_headers = { X-Literal = "literal-secret" }
"#;

    let got = parse_codex_mcp_toml(text);
    assert_eq!(got.len(), 2);
    assert!(got
        .iter()
        .all(|entry| entry.env.is_empty() && entry.headers.is_empty()));
    let omitted = codex_mcp_omitted_field_counts(text);
    assert_eq!(omitted["literal-env"], 2);
    assert_eq!(omitted["literal-headers"], 1);
    assert!(!format!("{omitted:?}").contains("literal-secret"));
}
#[test]
fn strict_codex_import_reports_every_rejected_entry_and_hides_syntax_source() {
    let report = parse_codex_mcp_toml_for_import(
        r#"
[mcp_servers.valid]
command = "valid-mcp"

[mcp_servers.unknown]
command = "other-mcp"
policy = "literal-value-must-not-appear"
"#,
    )
    .unwrap();
    assert_eq!(report.entries.len(), 1);
    assert_eq!(report.entries[0].name, "valid");
    assert_eq!(report.rejected.len(), 1);
    assert_eq!(report.rejected[0].name.as_deref(), Some("unknown"));
    assert_eq!(report.rejected[0].issue, McpImportIssue::UnknownField);

    let malformed = "[mcp_servers.bad]\nsecret = \"never-echo-this";
    let error = parse_codex_mcp_toml_for_import(malformed).unwrap_err();
    assert_eq!(error.to_string(), "invalid configuration syntax");
    assert!(!error.to_string().contains("never-echo-this"));
}
#[test]
fn parses_codex_environment_references_and_tool_timeout() {
    let text = r#"
[mcp_servers.local]
command = "server"
env_vars = ["ACCESS_TOKEN", { name = "TRACE_ID", source = "local" }]
tool_timeout_sec = 45

[mcp_servers.remote]
url = "https://mcp.example.test/rpc"
auth = "oauth"
tool_timeout_sec = 90
required = false
"#;

    let got = parse_codex_mcp_toml(text);
    assert_eq!(got.len(), 2);

    let local = got.iter().find(|server| server.name == "local").unwrap();
    assert_eq!(local.request_timeout_secs, Some(45));
    assert_eq!(
        local.env.get("ACCESS_TOKEN"),
        Some(&SecretValue::Ref(SecretRef {
            env: Some("ACCESS_TOKEN".into()),
            ..Default::default()
        }))
    );
    assert_eq!(
        local.env.get("TRACE_ID"),
        Some(&SecretValue::Ref(SecretRef {
            env: Some("TRACE_ID".into()),
            ..Default::default()
        }))
    );

    let remote = got.iter().find(|server| server.name == "remote").unwrap();
    assert_eq!(remote.request_timeout_secs, Some(90));
}
#[test]
fn codex_parser_rejects_startup_timeout_and_unknown_nested_env_fields() {
    let text = r#"
[mcp_servers.good]
command = "good-server"

[mcp_servers.startup-timeout]
url = "https://mcp.example.test/rpc"
startup_timeout_sec = 20

[mcp_servers.unknown-env-field]
command = "server"
env_vars = [{ name = "TOKEN", source = "local", typo = "must-not-disappear" }]
"#;

    let report = parse_codex_mcp_toml_for_import(text).unwrap();
    assert_eq!(
        report
            .entries
            .iter()
            .map(|entry| entry.name.as_str())
            .collect::<Vec<_>>(),
        ["good"]
    );
    assert_eq!(report.rejected.len(), 2);
    assert!(report
        .rejected
        .iter()
        .all(|entry| entry.issue == McpImportIssue::UnsupportedSemantics));
}
#[test]
fn codex_parser_rejects_remote_or_invalid_environment_references() {
    let text = r#"
[mcp_servers.good]
command = "good-server"
env_vars = ["LOCAL_TOKEN"]

[mcp_servers.remote-source]
command = "server"
env_vars = [{ name = "REMOTE_TOKEN", source = "remote" }]

[mcp_servers.unknown-source]
command = "server"
env_vars = [{ name = "TOKEN", source = "elsewhere" }]

[mcp_servers.invalid-name]
command = "server"
env_vars = ["9TOKEN"]

[mcp_servers.http-env-vars]
url = "https://mcp.example.test/rpc"
env_vars = ["TOKEN"]
"#;

    let names: Vec<String> = parse_codex_mcp_toml(text)
        .into_iter()
        .map(|server| server.name)
        .collect();
    assert_eq!(names, ["good"]);
}
#[test]
fn codex_parser_drops_ambiguous_or_unsafe_transport_shapes() {
    let text = r#"
[mcp_servers.good]
url = "https://mcp.example.test/good"

[mcp_servers.both]
url = "https://mcp.example.test/both"
command = "server"

[mcp_servers.http-with-args]
url = "https://mcp.example.test/args"
args = []

[mcp_servers.http-with-env]
url = "https://mcp.example.test/env"
env = {}

[mcp_servers.stdio-with-bearer]
command = "server"
bearer_token_env_var = "TOKEN"

[mcp_servers.stdio-with-headers]
command = "server"
http_headers = {}

[mcp_servers.args-only]
args = ["server"]

[mcp_servers.empty-url]
url = "  "

[mcp_servers.empty-command]
command = ""
"#;

    let names: Vec<String> = parse_codex_mcp_toml(text)
        .into_iter()
        .map(|server| server.name)
        .collect();
    assert_eq!(names, ["good"]);
}
#[test]
fn codex_parser_rejects_invalid_or_conflicting_credential_references() {
    let text = r#"
[mcp_servers.good]
url = "https://mcp.example.test/good"
env_http_headers = { X-API-Key = "API_KEY" }

[mcp_servers.bad-bearer-env]
url = "https://mcp.example.test/bearer"
bearer_token_env_var = "TOKEN}${cmd:bad}"

[mcp_servers.bad-header-env]
url = "https://mcp.example.test/header-env"
env_http_headers = { X-Key = "9INVALID" }

[mcp_servers.bad-header-name]
url = "https://mcp.example.test/header-name"
env_http_headers = { "Bad Header" = "TOKEN" }

[mcp_servers.duplicate-header-case]
url = "https://mcp.example.test/duplicate"
env_http_headers = { X-Key = "ONE", x-key = "TWO" }

[mcp_servers.conflicting-authorization]
url = "https://mcp.example.test/auth"
bearer_token_env_var = "TOKEN"
env_http_headers = { authorization = "OTHER_TOKEN" }

[mcp_servers.owned-host]
url = "https://mcp.example.test/host"
env_http_headers = { hOsT = "HOST_OVERRIDE" }

[mcp_servers.owned-session]
url = "https://mcp.example.test/session"
env_http_headers = { MCP-Session-ID = "SESSION_ID" }

[mcp_servers.owned-protocol]
url = "https://mcp.example.test/protocol"
env_http_headers = { mcp-protocol-version = "PROTOCOL_VERSION" }
"#;

    let names: Vec<String> = parse_codex_mcp_toml(text)
        .into_iter()
        .map(|server| server.name)
        .collect();
    assert_eq!(names, ["good"]);
}
#[test]
fn codex_parser_rejects_unsupported_widening_and_misspelled_fields() {
    let text = r#"
[mcp_servers.good]
command = "good-server"

[mcp_servers.enabled-tools]
command = "server"
enabled_tools = ["read"]

[mcp_servers.disabled-tools]
command = "server"
disabled_tools = ["write"]

[mcp_servers.required]
command = "server"
required = true

[mcp_servers.cwd]
command = "server"
cwd = "/tmp"

[mcp_servers.explicit-type]
url = "https://mcp.example.test/type"
type = "http"

[mcp_servers.chatgpt-auth]
url = "https://mcp.example.test/chatgpt"
auth = "chatgpt"

[mcp_servers.oauth]
url = "https://mcp.example.test/oauth"
oauth_client_id = "client"

[mcp_servers.oauth-resource]
url = "https://mcp.example.test/oauth-resource"
oauth_resource = "https://resource.example.test"

[mcp_servers.wrong-case]
url = "https://mcp.example.test/case"
bearerTokenEnvVar = "TOKEN"
"#;

    let names: Vec<String> = parse_codex_mcp_toml(text)
        .into_iter()
        .map(|server| server.name)
        .collect();
    assert_eq!(names, ["good"]);
}
#[test]
fn codex_parser_drops_only_the_malformed_entry() {
    let text = r#"
[mcp_servers.before]
command = "before"

[mcp_servers.bad]
command = "bad"
args = "not-an-array"

[mcp_servers.after]
url = "https://mcp.example.test/after"
"#;

    let names: std::collections::BTreeSet<String> = parse_codex_mcp_toml(text)
        .into_iter()
        .map(|server| server.name)
        .collect();
    assert_eq!(
        names,
        std::collections::BTreeSet::from(["after".to_string(), "before".to_string()])
    );
    assert!(parse_codex_mcp_toml("not = = toml [").is_empty());
    assert!(parse_codex_mcp_toml("other = 1").is_empty());
    assert!(parse_codex_mcp_toml("mcp_servers = []").is_empty());
}