Stop the flag tests writing into the developer's real home

c182a7ccf849 · AtlantisPleb · · parent 6a3ac28fe8b4

Stop the flag tests writing into the developer's real home

`oa` resolves its config directory from `HOME`, and the test helper spawned the
binary with the one it inherited. So every run of this suite wrote into the
real `~/.config/openagents`.

It was not theoretical. A single live audit left 79 stub authorizations in the
real `device-authorizations.json` — keyed by ephemeral `http://127.0.0.1:<port>`
origins, 23 kB of them — mixed in with a genuine pending authorization. And
because several agents ran this suite at once against that one shared file,
`repeated_scopes_are_all_sent` flaked with a write error that had nothing to do
with what it tests.

The helper now hands the child a `HOME` of this test binary's own. That is one
place rather than one per test, so a test added later cannot forget.

Checked by running the suite and comparing the real file's size before and
after: unchanged. The 79 stub entries have been removed from it by hand; the
real one was left alone.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified crates/openagents-cli/tests/flags.rs

Diff

1 file changed, +29 -0

crates/openagents-cli/tests/flags.rs modified +29

@@ -9,9 +9,12 @@

9 9
10 10
use std::io::{BufRead, BufReader, Read, Write};
11 11
use std::net::{TcpListener, TcpStream};
12
use std::path::{Path, PathBuf};
12 13
use std::process::Command;
13 14
use std::sync::mpsc;
15
use std::sync::OnceLock;
14 16
use std::thread;
17
use std::time::{SystemTime, UNIX_EPOCH};
15 18
16 19
/// A server that answers one canned body and reports the paths it was asked
17 20
/// for.

@@ -107,12 +110,38 @@ struct Output {

107 110
    status: Option<i32>,
108 111
}
109 112
113
/// A `HOME` of this test binary's own.
114
///
115
/// `oa` resolves its config directory from `HOME`, so without this every run
116
/// here writes into the developer's real `~/.config/openagents`. It did: a
117
/// single audit left 79 stub authorizations — keyed by ephemeral
118
/// `http://127.0.0.1:<port>` origins — in the real `device-authorizations.json`,
119
/// and concurrent runs racing that shared file made `repeated_scopes_are_all_sent`
120
/// flake with a write error. Tests must not touch the machine they run on.
121
fn isolated_home() -> &'static Path {
122
    static HOME: OnceLock<PathBuf> = OnceLock::new();
123
    HOME.get_or_init(|| {
124
        let at = std::env::temp_dir().join(format!(
125
            "oa-flags-home-{}-{}",
126
            std::process::id(),
127
            SystemTime::now()
128
                .duration_since(UNIX_EPOCH)
129
                .map(|d| d.as_nanos())
130
                .unwrap_or(0)
131
        ));
132
        std::fs::create_dir_all(&at).expect("make an isolated HOME");
133
        at
134
    })
135
    .as_path()
136
}
137
110 138
fn oa(args: &[&str]) -> Output {
111 139
    let result = Command::new(env!("CARGO_BIN_EXE_oa"))
112 140
        .args(args)
113 141
        // The credential store is keyed by origin, and the stub's origin has
114 142
        // no token, so these runs never carry a real one.
115 143
        .env("NO_COLOR", "")
144
        .env("HOME", isolated_home())
116 145
        .output()
117 146
        .expect("run oa");
118 147
    Output {

This page updates live while a promote is in flight · changelog