use assert_cmd::Command;
use predicates::prelude::*;
mod common;
fn identity_fixture() -> (tempfile::TempDir, tempfile::TempDir) {
let home = common::isolated_root();
let workspace = tempfile::tempdir_in(home.path()).unwrap();
(home, workspace)
}
fn newt_in(workspace: &std::path::Path, home: &std::path::Path) -> Command {
let mut cmd = common::newt_at(home);
cmd.current_dir(workspace);
cmd
}
fn newt_identity_in(workspace: &std::path::Path, home: &std::path::Path) -> Command {
let mut cmd = newt_in(workspace, home);
cmd.arg("identity");
cmd
}
#[test]
fn identity_default_resolves_to_newt_agent_user() {
let (home, workspace) = identity_fixture();
newt_identity_in(workspace.path(), home.path())
.assert()
.success()
.stdout(predicate::str::contains(
"309460085+newt-agent@users.noreply.github.com",
))
.stdout(predicate::str::contains("compiled-in default (newt-agent)"))
.stdout(predicate::str::contains("signing-key: <none>"))
.stdout(predicate::str::contains("github-app: <none>"))
.stdout(predicate::str::contains("tokens: <none>"))
.stdout(predicate::str::contains("newt-agent").and(predicate::str::contains("[bot]").not()))
.stdout(predicate::str::contains("newt identity set"));
}
#[test]
fn identity_set_writes_home_override_and_show_picks_it_up() {
let (home, workspace) = identity_fixture();
let mut set = newt_in(workspace.path(), home.path());
set.args([
"identity",
"set",
"--name",
"my-harness",
"--email",
"my-harness@example.com",
])
.assert()
.success()
.stdout(predicate::str::contains("Wrote agent identity"))
.stdout(predicate::str::contains("my-harness@example.com"));
let written = home.path().join(".newt").join("agent-identity.toml");
assert!(
written.is_file(),
"set must land in ~/.newt/agent-identity.toml"
);
let body = std::fs::read_to_string(&written).unwrap();
assert!(body.contains("[agent-identity]"));
assert!(body.contains("my-harness"));
newt_identity_in(workspace.path(), home.path())
.assert()
.success()
.stdout(predicate::str::contains("my-harness"))
.stdout(predicate::str::contains("my-harness@example.com"))
.stdout(predicate::str::contains("home"))
.stdout(predicate::str::contains("newt identity set").not());
}
#[test]
fn identity_set_workspace_writes_local_override() {
let (home, workspace) = identity_fixture();
let mut set = newt_in(workspace.path(), home.path());
set.args([
"identity",
"set",
"--name",
"ws-agent",
"--email",
"ws@example.com",
"--workspace",
])
.assert()
.success();
let written = workspace.path().join(".newt").join("agent-identity.toml");
assert!(written.is_file());
newt_identity_in(workspace.path(), home.path())
.assert()
.success()
.stdout(predicate::str::contains("ws-agent"))
.stdout(predicate::str::contains("ws@example.com"))
.stdout(predicate::str::contains("workspace"));
}
const APP_AND_TOKEN_TOML: &str = r#"
[agent-identity]
name = "gilamonster-agent[bot]"
email = "293450354+gilamonster-agent[bot]@users.noreply.github.com"
[agent-identity.github_app]
app_id = 4046825
client_id = "Iv23li5iPGv4awNHpHbZ"
installation_id = 140120359
[agent-identity.tokens]
ci_deploy = { env = "NEWT_IDENTITY_CLI_SECRET" }
"#;
fn write_identity(root: &std::path::Path, body: &str) {
let dir = root.join(".newt");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("agent-identity.toml"), body).unwrap();
}
#[test]
fn identity_workspace_file_overrides_name_but_not_credentials() {
let (home, workspace) = identity_fixture();
write_identity(workspace.path(), APP_AND_TOKEN_TOML);
newt_identity_in(workspace.path(), home.path())
.env("NEWT_IDENTITY_CLI_SECRET", "SECRET-MUST-NOT-PRINT")
.assert()
.success()
.stdout(predicate::str::contains("gilamonster-agent[bot]"))
.stdout(predicate::str::contains(
"293450354+gilamonster-agent[bot]@users.noreply.github.com",
))
.stdout(predicate::str::contains("workspace"))
.stdout(predicate::str::contains("github-app: <none>"))
.stdout(predicate::str::contains("tokens: <none>"))
.stdout(predicate::str::contains("app_id").not())
.stdout(predicate::str::contains("ci_deploy").not())
.stdout(predicate::str::contains("SECRET-MUST-NOT-PRINT").not());
}
#[test]
fn identity_operator_file_shows_app_and_hides_secret() {
let (home, workspace) = identity_fixture();
write_identity(home.path(), APP_AND_TOKEN_TOML);
newt_identity_in(workspace.path(), home.path())
.env("NEWT_IDENTITY_CLI_SECRET", "SECRET-MUST-NOT-PRINT")
.assert()
.success()
.stdout(predicate::str::contains("gilamonster-agent[bot]"))
.stdout(predicate::str::contains("home"))
.stdout(predicate::str::contains("app_id: 4046825"))
.stdout(predicate::str::contains("installation_id: 140120359"))
.stdout(predicate::str::contains("ci_deploy"))
.stdout(predicate::str::contains("names only"))
.stdout(predicate::str::contains("SECRET-MUST-NOT-PRINT").not());
}
#[test]
fn identity_missing_signing_key_is_clean_not_panic() {
let (home, workspace) = identity_fixture();
write_identity(
home.path(),
r#"
[agent-identity]
name = "keyed-agent[bot]"
signing_key = "/nonexistent/vault/path/identity.pem"
"#,
);
newt_identity_in(workspace.path(), home.path())
.assert()
.success()
.stdout(predicate::str::contains(
"/nonexistent/vault/path/identity.pem",
))
.stdout(predicate::str::contains("<unavailable"));
}
#[test]
fn identity_workspace_signing_key_is_ignored() {
let (home, workspace) = identity_fixture();
write_identity(
workspace.path(),
r#"
[agent-identity]
name = "keyed-agent[bot]"
signing_key = "/nonexistent/vault/path/identity.pem"
"#,
);
newt_identity_in(workspace.path(), home.path())
.assert()
.success()
.stdout(predicate::str::contains("signing-key: <none>"))
.stdout(predicate::str::contains("/nonexistent/vault/path/identity.pem").not());
}