@zgeoff/atc 3.4.1 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agents/agent-adapter.ts +4 -0
- package/src/agents/build-claude-query-options.ts +1 -0
- package/src/agents/build-headless-env.ts +4 -0
- package/src/agents/claude-adapter.ts +4 -3
- package/src/agents/gateway-adapter.ts +2 -2
- package/src/agents/plan-claude-line-input.ts +44 -0
- package/src/client/daemon-client.ts +6 -1
- package/src/collect-restart-env.ts +6 -3
- package/src/daemon/build-scoped-context.ts +2 -0
- package/src/daemon/check-session-scope.ts +235 -0
- package/src/daemon/daemon-connection.ts +74 -0
- package/src/daemon/daemon-context.ts +21 -0
- package/src/daemon/daemon.ts +21 -0
- package/src/daemon/find-host-branch.ts +28 -0
- package/src/daemon/materialize-workspace.ts +6 -1
- package/src/daemon/merge-recorded-scope.ts +50 -0
- package/src/daemon/published-records.ts +269 -0
- package/src/daemon/request-access-classes.ts +1 -0
- package/src/daemon/run-host-git.ts +52 -0
- package/src/daemon/sessions.ts +185 -53
- package/src/daemon/start-headless-turn.ts +3 -0
- package/src/federation/id-rules.ts +11 -2
- package/src/mcp/mcp-tools.ts +51 -1
- package/src/mcp/require-daemon-features.ts +1 -0
- package/src/mcp/run-tool.ts +14 -0
- package/src/protocol/daemon-features.ts +5 -0
- package/src/protocol/parse-declared-scope.ts +158 -0
- package/src/protocol/protocol.ts +1 -0
- package/src/protocol/published-record.ts +62 -0
- package/src/protocol/request-param-schemas.ts +16 -0
- package/src/store/parse-published-record.ts +59 -0
- package/src/store/run-migrations.ts +23 -0
- package/src/store/state-store.ts +32 -0
- package/src/test-utils/build-mock-published-record.ts +39 -0
- package/src/test-utils/build-stub-brokered-agent-adapter.ts +3 -1
- package/src/test-utils/build-stub-daemon-context.ts +2 -0
- package/src/test-utils/build-stub-daemon-requests.ts +3 -3
- package/src/test-utils/build-stub-harness-relay.ts +2 -2
- package/src/test-utils/build-stub-held-gh.ts +5 -7
- package/src/test-utils/build-stub-held-inbox-source.ts +2 -2
- package/src/test-utils/build-stub-held-zoxide.ts +4 -6
- package/src/test-utils/build-stub-host-hold.ts +7 -6
- package/src/test-utils/build-stub-launch-admission.ts +20 -0
- package/src/test-utils/build-stub-pregranted-broker-host.ts +22 -0
- package/src/test-utils/build-stub-terminal.ts +3 -3
- package/src/test-utils/create-git-fixture.ts +2 -2
- package/src/test-utils/create-stub-claude.ts +3 -1
- package/src/test-utils/create-stub-imp-port.ts +46 -37
- package/src/test-utils/create-stub-systemd.ts +6 -6
- package/src/test-utils/isolate-home.ts +8 -5
- package/src/test-utils/read-query-plan.ts +10 -6
- package/src/test-utils/register-test-cleanup.ts +10 -10
- package/src/test-utils/{run-legacy-daemon.ts → run-stub-legacy-daemon.ts} +1 -1
- package/src/test-utils/setup-mcp-home.ts +7 -6
- package/src/test-utils/setup-mcp-http.ts +6 -6
- package/src/test-utils/setup-temp-dir.ts +4 -10
- package/src/test-utils/start-cut-proxy.ts +2 -4
- package/src/test-utils/start-daemon-process.ts +13 -13
- package/src/test-utils/start-git-http-server.ts +2 -4
- package/src/test-utils/start-mcp-stdio.ts +5 -5
- package/src/test-utils/start-stub-answering-listener.ts +3 -3
- package/src/test-utils/start-stub-closing-listener.ts +3 -3
- package/src/test-utils/start-stub-dropping-daemon.ts +2 -3
- package/src/test-utils/start-stub-impd-info.ts +3 -7
- package/src/test-utils/start-stub-impd.ts +3 -5
- package/src/test-utils/start-stub-legacy-daemon.ts +2 -3
- package/src/test-utils/start-stub-mcp-server.ts +3 -5
- package/src/test-utils/start-stub-recording-listener.ts +3 -3
- package/src/test-utils/start-stub-refusing-listener.ts +3 -3
- package/src/test-utils/start-stub-reporter-socket.ts +3 -9
- package/src/test-utils/start-stub-restore-daemon.ts +2 -7
- package/src/test-utils/start-stub-session-bridge.ts +3 -9
- package/src/test-utils/start-stub-silent-server.ts +3 -6
- package/src/test-utils/start-stub-source-daemon.ts +3 -3
- package/src/test-utils/start-stub-stalled-client.ts +3 -3
- package/src/test-utils/start-stub-stalled-listener.ts +3 -3
- package/src/test-utils/start-stub-tcp-daemon.ts +3 -3
- package/src/test-utils/start-stub-telemetry-collector.ts +2 -2
- package/src/test-utils/start-stub-unanswering-listener.ts +3 -3
- package/src/test-utils/start-stub-unanswering-unix-listener.ts +3 -3
- package/src/test-utils/start-test-daemon.ts +62 -18
- package/src/test-utils/start-tui-harness.ts +17 -10
- package/src/test-utils/subscribe-to-socket-lines.ts +5 -9
- package/src/test-utils/{can-bind-addresses.ts → try-bind-addresses.ts} +4 -3
- /package/src/test-utils/{run-source-daemon.ts → run-stub-source-daemon.ts} +0 -0
|
@@ -39,12 +39,12 @@ interface DaemonBoot {
|
|
|
39
39
|
* sending no handshake, and fails at once with the daemon's stderr when the
|
|
40
40
|
* daemon exits before it listens. `restart` stops the daemon with the given
|
|
41
41
|
* signal, waits for it to exit, and starts another on the same home with the
|
|
42
|
-
* same config. `readStderr` reads what the current boot printed.
|
|
42
|
+
* same config. `readStderr` reads what the current boot printed. `stop`
|
|
43
43
|
* closes every client it opened, kills the daemon and waits for it to exit,
|
|
44
44
|
* then kills the daemon the home's state directory records, which a restart
|
|
45
|
-
* the test asked atc for may have started. That
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* the test asked atc for may have started. That stop runs once the current
|
|
46
|
+
* test finishes, so it must run inside a test; calling `stop` sooner runs it
|
|
47
|
+
* then, and a second stop does nothing.
|
|
48
48
|
*/
|
|
49
49
|
export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
50
50
|
const socketPath = join(config.home, 'atc-daemon.sock');
|
|
@@ -53,7 +53,7 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
53
53
|
const clients = new Set<DaemonClient>();
|
|
54
54
|
|
|
55
55
|
let boots = 0;
|
|
56
|
-
let
|
|
56
|
+
let stopped = false;
|
|
57
57
|
|
|
58
58
|
const boot = (): DaemonBoot => {
|
|
59
59
|
boots++;
|
|
@@ -79,8 +79,8 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
79
79
|
|
|
80
80
|
let current = boot();
|
|
81
81
|
|
|
82
|
-
const
|
|
83
|
-
|
|
82
|
+
const stop = registerTestCleanup(async () => {
|
|
83
|
+
stopped = true;
|
|
84
84
|
|
|
85
85
|
for (const client of clients) {
|
|
86
86
|
client.stop();
|
|
@@ -107,12 +107,12 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
107
107
|
}
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
// A client that opens after the wait for it ended, or after
|
|
110
|
+
// A client that opens after the wait for it ended, or after the stop,
|
|
111
111
|
// closes at once, so nothing outlives the daemon it reached.
|
|
112
112
|
const openTrackedClient = async (isAbandoned: () => boolean) => {
|
|
113
113
|
const client = await DaemonClient.open(socketPath);
|
|
114
114
|
|
|
115
|
-
if (
|
|
115
|
+
if (stopped || isAbandoned()) {
|
|
116
116
|
client.stop();
|
|
117
117
|
throw new Error('the wait for the daemon ended before this client opened');
|
|
118
118
|
}
|
|
@@ -132,7 +132,7 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
132
132
|
const openAfterExit = async () => {
|
|
133
133
|
await watched.exited;
|
|
134
134
|
|
|
135
|
-
if (settled ||
|
|
135
|
+
if (settled || stopped) {
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -146,10 +146,10 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
146
146
|
};
|
|
147
147
|
|
|
148
148
|
// Polls the socket while the daemon runs. Once the other wait settles or
|
|
149
|
-
// the daemon
|
|
149
|
+
// the daemon stops, each attempt resolves empty, which ends the
|
|
150
150
|
// polling.
|
|
151
151
|
const openWhenListening = () =>
|
|
152
|
-
waitFor(() => (settled ||
|
|
152
|
+
waitFor(() => (settled || stopped ? null : openTrackedClient(isSettled)), {
|
|
153
153
|
timeoutMs: 15_000,
|
|
154
154
|
intervalMs: 50,
|
|
155
155
|
});
|
|
@@ -188,6 +188,6 @@ export function startDaemonProcess(config: Readonly<DaemonProcessConfig>) {
|
|
|
188
188
|
|
|
189
189
|
current = boot();
|
|
190
190
|
},
|
|
191
|
-
|
|
191
|
+
stop,
|
|
192
192
|
};
|
|
193
193
|
}
|
|
@@ -7,7 +7,6 @@ interface GitHTTPServer {
|
|
|
7
7
|
// Every Authorization header a request carried, in arrival order.
|
|
8
8
|
readonly authorizations: string[];
|
|
9
9
|
readonly stop: () => Promise<void>;
|
|
10
|
-
readonly [Symbol.asyncDispose]: () => Promise<void>;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
interface GitHTTPServerOptions {
|
|
@@ -30,8 +29,8 @@ interface GitHTTPServerOptions {
|
|
|
30
29
|
* clone through it slow. An `onRequest` callback runs, and is awaited, while
|
|
31
30
|
* each authenticated request is held, so it sees the client that sent the
|
|
32
31
|
* request still running. It stops once the current test finishes, so it
|
|
33
|
-
* must run inside a test; `stop`
|
|
34
|
-
*
|
|
32
|
+
* must run inside a test; `stop` stops it sooner, and a second stop does
|
|
33
|
+
* nothing.
|
|
35
34
|
*/
|
|
36
35
|
export function startGitHTTPServer(
|
|
37
36
|
root: string,
|
|
@@ -97,7 +96,6 @@ export function startGitHTTPServer(
|
|
|
97
96
|
url: `http://127.0.0.1:${server.port}/`,
|
|
98
97
|
authorizations,
|
|
99
98
|
stop,
|
|
100
|
-
[Symbol.asyncDispose]: stop,
|
|
101
99
|
};
|
|
102
100
|
}
|
|
103
101
|
|
|
@@ -39,8 +39,8 @@ interface MCPToolResult {
|
|
|
39
39
|
* with the new session's id. A request still unanswered when the server's
|
|
40
40
|
* stdout ends rejects. When `initialize` fails, the server is stopped
|
|
41
41
|
* before the start rejects. The server stops once the current test
|
|
42
|
-
* finishes, so it must run inside a test;
|
|
43
|
-
* for it to exit, and a second
|
|
42
|
+
* finishes, so it must run inside a test; `stop` stops it sooner and waits
|
|
43
|
+
* for it to exit, and a second stop waits for the same exit. The daemon
|
|
44
44
|
* stays up for the home to stop.
|
|
45
45
|
*/
|
|
46
46
|
export async function startMCPStdio(options: MCPStdioOptions) {
|
|
@@ -58,7 +58,7 @@ export async function startMCPStdio(options: MCPStdioOptions) {
|
|
|
58
58
|
stderr: 'ignore',
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
const
|
|
61
|
+
const stop = registerTestCleanup(async (): Promise<void> => {
|
|
62
62
|
void proc.stdin.end();
|
|
63
63
|
proc.kill();
|
|
64
64
|
|
|
@@ -113,7 +113,7 @@ export async function startMCPStdio(options: MCPStdioOptions) {
|
|
|
113
113
|
|
|
114
114
|
return id;
|
|
115
115
|
},
|
|
116
|
-
|
|
116
|
+
stop,
|
|
117
117
|
};
|
|
118
118
|
|
|
119
119
|
// A failed initialize stops the server here, before the start rejects.
|
|
@@ -122,7 +122,7 @@ export async function startMCPStdio(options: MCPStdioOptions) {
|
|
|
122
122
|
capabilities: {},
|
|
123
123
|
clientInfo: { name: 'atc-test' },
|
|
124
124
|
}).catch(async (error: unknown) => {
|
|
125
|
-
await
|
|
125
|
+
await stop();
|
|
126
126
|
|
|
127
127
|
throw error;
|
|
128
128
|
});
|
|
@@ -8,7 +8,7 @@ interface StubAnsweringListener {
|
|
|
8
8
|
|
|
9
9
|
// The first read each connection sent, decoded, in arrival order.
|
|
10
10
|
readonly lines: readonly string[];
|
|
11
|
-
readonly
|
|
11
|
+
readonly stop: () => Promise<void>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -17,7 +17,7 @@ interface StubAnsweringListener {
|
|
|
17
17
|
* read each one sends, answers that read with the line `answer`, and
|
|
18
18
|
* leaves every later byte unread on the connection. Resolves once it
|
|
19
19
|
* listens. Once the current test finishes, it destroys every connection it
|
|
20
|
-
* accepted and closes, so it must run inside a test;
|
|
20
|
+
* accepted and closes, so it must run inside a test; `stop` does so
|
|
21
21
|
* sooner and resolves once the server has closed, and a second stop does
|
|
22
22
|
* nothing.
|
|
23
23
|
*/
|
|
@@ -60,6 +60,6 @@ export async function startStubAnsweringListener(path: string): Promise<StubAnsw
|
|
|
60
60
|
return {
|
|
61
61
|
peers,
|
|
62
62
|
lines,
|
|
63
|
-
|
|
63
|
+
stop,
|
|
64
64
|
};
|
|
65
65
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { registerTestCleanup } from './register-test-cleanup';
|
|
2
2
|
|
|
3
3
|
interface StubClosingListener {
|
|
4
|
-
readonly
|
|
4
|
+
readonly stop: () => void;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* A stand-in for a daemon that hangs up: it listens on the unix socket path
|
|
9
9
|
* and ends every connection as soon as it opens, reading nothing. It stops
|
|
10
|
-
* once the current test finishes, so it must run inside a test;
|
|
10
|
+
* once the current test finishes, so it must run inside a test; `stop`
|
|
11
11
|
* stops it sooner, and a second stop does nothing.
|
|
12
12
|
*/
|
|
13
13
|
export function startStubClosingListener(path: string): StubClosingListener {
|
|
@@ -26,6 +26,6 @@ export function startStubClosingListener(path: string): StubClosingListener {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
return {
|
|
29
|
-
|
|
29
|
+
stop,
|
|
30
30
|
};
|
|
31
31
|
}
|
|
@@ -25,8 +25,8 @@ interface DroppingDaemonOptions {
|
|
|
25
25
|
* idempotency key each of those requests carried, `undefined` for one that
|
|
26
26
|
* carried none. `reads` counts the reads it has taken from every connection,
|
|
27
27
|
* so a test can wait until one piece of a split write has arrived. It stops
|
|
28
|
-
* once the current test finishes, so it must run inside a test; `stop`
|
|
29
|
-
*
|
|
28
|
+
* once the current test finishes, so it must run inside a test; `stop`
|
|
29
|
+
* stops it sooner, and a second stop does nothing.
|
|
30
30
|
*/
|
|
31
31
|
export function startStubDroppingDaemon(socketPath: string, options: DroppingDaemonOptions) {
|
|
32
32
|
const keys: unknown[] = [];
|
|
@@ -100,6 +100,5 @@ export function startStubDroppingDaemon(socketPath: string, options: DroppingDae
|
|
|
100
100
|
return reads;
|
|
101
101
|
},
|
|
102
102
|
stop,
|
|
103
|
-
[Symbol.dispose]: stop,
|
|
104
103
|
};
|
|
105
104
|
}
|
|
@@ -6,9 +6,8 @@ import { registerTestCleanup } from './register-test-cleanup';
|
|
|
6
6
|
* `{ json: info }`, so a client reads `info.features` as impd's features;
|
|
7
7
|
* the test sets `info.features` to what impd should report. `paths` holds
|
|
8
8
|
* the request path of each call, in order. The server stops once the
|
|
9
|
-
* current test finishes, so it must run inside a test;
|
|
10
|
-
* sooner
|
|
11
|
-
* stop does nothing.
|
|
9
|
+
* current test finishes, so it must run inside a test; `stop` stops it
|
|
10
|
+
* sooner and resolves once it has stopped, and a second stop does nothing.
|
|
12
11
|
*/
|
|
13
12
|
export function startStubImpdInfo() {
|
|
14
13
|
const paths: string[] = [];
|
|
@@ -30,9 +29,6 @@ export function startStubImpdInfo() {
|
|
|
30
29
|
url: `http://127.0.0.1:${String(server.port)}`,
|
|
31
30
|
paths,
|
|
32
31
|
info,
|
|
33
|
-
|
|
34
|
-
void stop();
|
|
35
|
-
},
|
|
36
|
-
[Symbol.asyncDispose]: stop,
|
|
32
|
+
stop,
|
|
37
33
|
};
|
|
38
34
|
}
|
|
@@ -18,8 +18,8 @@ type StubExecReply = 'refuse' | 'count' | 'exit-early';
|
|
|
18
18
|
* whose broker is not ready by default. It takes no WebSocket message over
|
|
19
19
|
* 2 MiB, as impd refuses one over its own limit. The server stops, dropping
|
|
20
20
|
* every open connection at once, when the current test finishes, so it must
|
|
21
|
-
* run inside a test;
|
|
22
|
-
* nothing.
|
|
21
|
+
* run inside a test; `stop` stops it sooner and resolves once it has
|
|
22
|
+
* stopped, and a second stop does nothing.
|
|
23
23
|
*/
|
|
24
24
|
export function startStubImpd() {
|
|
25
25
|
const authorizations: (string | null)[] = [];
|
|
@@ -136,8 +136,6 @@ export function startStubImpd() {
|
|
|
136
136
|
controls,
|
|
137
137
|
execOpens,
|
|
138
138
|
exec,
|
|
139
|
-
|
|
140
|
-
void stop();
|
|
141
|
-
},
|
|
139
|
+
stop,
|
|
142
140
|
};
|
|
143
141
|
}
|
|
@@ -47,8 +47,8 @@ interface StubLegacyDaemonOptions {
|
|
|
47
47
|
* `connections` counts the connections it accepted and those still open.
|
|
48
48
|
* `port` holds the TCP port it bound, `null` on a unix socket. It stops
|
|
49
49
|
* once the current test finishes, so it must run inside a test, unless the
|
|
50
|
-
* options make the caller its owner; `stop`
|
|
51
|
-
*
|
|
50
|
+
* options make the caller its owner; `stop` stops it sooner, and a second
|
|
51
|
+
* stop does nothing.
|
|
52
52
|
*/
|
|
53
53
|
export function startStubLegacyDaemon(
|
|
54
54
|
address: string | StubLegacyDaemonTCPAddress,
|
|
@@ -135,7 +135,6 @@ export function startStubLegacyDaemon(
|
|
|
135
135
|
connections,
|
|
136
136
|
port: server.port,
|
|
137
137
|
stop,
|
|
138
|
-
[Symbol.dispose]: stop,
|
|
139
138
|
};
|
|
140
139
|
}
|
|
141
140
|
|
|
@@ -14,7 +14,7 @@ interface StubMCPServer {
|
|
|
14
14
|
// Every request the stand-in received, in arrival order.
|
|
15
15
|
readonly requests: readonly StubMCPRequest[];
|
|
16
16
|
|
|
17
|
-
readonly
|
|
17
|
+
readonly stop: () => Promise<void>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -22,7 +22,7 @@ interface StubMCPServer {
|
|
|
22
22
|
* request with the JSON body given, and records each request's method,
|
|
23
23
|
* path, authorization and content-type headers, and JSON body. `url` holds
|
|
24
24
|
* its origin. It stops once the current test finishes, so it must run
|
|
25
|
-
* inside a test;
|
|
25
|
+
* inside a test; `stop` stops it sooner, and a second stop does nothing.
|
|
26
26
|
*/
|
|
27
27
|
export function startStubMCPServer(answer: unknown): StubMCPServer {
|
|
28
28
|
const requests: StubMCPRequest[] = [];
|
|
@@ -48,8 +48,6 @@ export function startStubMCPServer(answer: unknown): StubMCPServer {
|
|
|
48
48
|
return {
|
|
49
49
|
url: `http://127.0.0.1:${String(server.port)}`,
|
|
50
50
|
requests,
|
|
51
|
-
|
|
52
|
-
void stop();
|
|
53
|
-
},
|
|
51
|
+
stop,
|
|
54
52
|
};
|
|
55
53
|
}
|
|
@@ -7,7 +7,7 @@ interface StubRecordingListener {
|
|
|
7
7
|
|
|
8
8
|
// What each read took from any connection, decoded, in arrival order.
|
|
9
9
|
readonly received: readonly string[];
|
|
10
|
-
readonly
|
|
10
|
+
readonly stop: () => void;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -15,7 +15,7 @@ interface StubRecordingListener {
|
|
|
15
15
|
* socket path, hands the test the server's side of the first connection it
|
|
16
16
|
* accepts, records what each read takes from any connection, and sends
|
|
17
17
|
* nothing of its own. It stops, dropping every connection it holds, once
|
|
18
|
-
* the current test finishes, so it must run inside a test;
|
|
18
|
+
* the current test finishes, so it must run inside a test; `stop` stops it
|
|
19
19
|
* sooner, and a second stop does nothing.
|
|
20
20
|
*/
|
|
21
21
|
export function startStubRecordingListener(path: string): StubRecordingListener {
|
|
@@ -42,6 +42,6 @@ export function startStubRecordingListener(path: string): StubRecordingListener
|
|
|
42
42
|
return {
|
|
43
43
|
accepted: accepted.promise,
|
|
44
44
|
received,
|
|
45
|
-
|
|
45
|
+
stop,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -6,7 +6,7 @@ interface StubRefusingListener {
|
|
|
6
6
|
// What each read took from any connection, decoded, in arrival order.
|
|
7
7
|
readonly received: readonly string[];
|
|
8
8
|
|
|
9
|
-
readonly
|
|
9
|
+
readonly stop: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -14,7 +14,7 @@ interface StubRefusingListener {
|
|
|
14
14
|
* line is not a handshake: on a loopback port, it records what each read
|
|
15
15
|
* takes from a connection and ends that connection, answering nothing.
|
|
16
16
|
* It stops once the current test finishes, so it must run inside a test;
|
|
17
|
-
*
|
|
17
|
+
* `stop` stops it sooner, and a second stop does nothing.
|
|
18
18
|
*/
|
|
19
19
|
export function startStubRefusingListener(): StubRefusingListener {
|
|
20
20
|
const received: string[] = [];
|
|
@@ -38,6 +38,6 @@ export function startStubRefusingListener(): StubRefusingListener {
|
|
|
38
38
|
return {
|
|
39
39
|
port: server.port,
|
|
40
40
|
received,
|
|
41
|
-
|
|
41
|
+
stop,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
@@ -9,8 +9,7 @@ interface StubReporterSocket {
|
|
|
9
9
|
// can wait until one piece of a split write has arrived.
|
|
10
10
|
readonly reads: number;
|
|
11
11
|
readonly waitForLine: (timeoutMs?: number) => Promise<string>;
|
|
12
|
-
readonly
|
|
13
|
-
readonly [Symbol.asyncDispose]: () => Promise<void>;
|
|
12
|
+
readonly stop: () => void;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -21,7 +20,7 @@ interface StubReporterSocket {
|
|
|
21
20
|
* connection for the sender to close. `waitForLine` resolves with the first
|
|
22
21
|
* line once one has arrived, and rejects naming the socket when none arrives
|
|
23
22
|
* within `timeoutMs`, 5 seconds by default. The listener stops once the
|
|
24
|
-
* current test finishes, so it must run inside a test;
|
|
23
|
+
* current test finishes, so it must run inside a test; `stop` stops it
|
|
25
24
|
* sooner, and a second stop does nothing.
|
|
26
25
|
*/
|
|
27
26
|
export function startStubReporterSocket(path: string): StubReporterSocket {
|
|
@@ -69,11 +68,6 @@ export function startStubReporterSocket(path: string): StubReporterSocket {
|
|
|
69
68
|
},
|
|
70
69
|
{ timeoutMs },
|
|
71
70
|
),
|
|
72
|
-
|
|
73
|
-
[Symbol.asyncDispose]: () => {
|
|
74
|
-
stop();
|
|
75
|
-
|
|
76
|
-
return Promise.resolve();
|
|
77
|
-
},
|
|
71
|
+
stop,
|
|
78
72
|
};
|
|
79
73
|
}
|
|
@@ -9,7 +9,7 @@ import { registerTestCleanup } from './register-test-cleanup';
|
|
|
9
9
|
* onto `lists`, and withholds the answer to any request that finds `lists`
|
|
10
10
|
* empty. `methods` holds the method of each request it took, in order.
|
|
11
11
|
* The listener stops once the current test finishes, so it must run inside
|
|
12
|
-
* a test;
|
|
12
|
+
* a test; `stop` stops it sooner, and a second stop does nothing.
|
|
13
13
|
*/
|
|
14
14
|
export function startStubRestoreDaemon(socketPath: string) {
|
|
15
15
|
const lists: Readonly<Record<string, unknown>>[] = [];
|
|
@@ -49,11 +49,6 @@ export function startStubRestoreDaemon(socketPath: string) {
|
|
|
49
49
|
return {
|
|
50
50
|
lists,
|
|
51
51
|
methods,
|
|
52
|
-
|
|
53
|
-
[Symbol.asyncDispose]: () => {
|
|
54
|
-
stop();
|
|
55
|
-
|
|
56
|
-
return Promise.resolve();
|
|
57
|
-
},
|
|
52
|
+
stop,
|
|
58
53
|
};
|
|
59
54
|
}
|
|
@@ -16,8 +16,7 @@ interface StubSessionBridge {
|
|
|
16
16
|
// How many reads the stand-in has taken from its connections, so a test
|
|
17
17
|
// can wait until one piece of a split write has arrived.
|
|
18
18
|
readonly reads: number;
|
|
19
|
-
readonly
|
|
20
|
-
readonly [Symbol.asyncDispose]: () => Promise<void>;
|
|
19
|
+
readonly stop: () => void;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/**
|
|
@@ -28,7 +27,7 @@ interface StubSessionBridge {
|
|
|
28
27
|
* does once it attaches the tap, and hands every other request to the
|
|
29
28
|
* responder, writing back each line it returns. A line that is not a JSON
|
|
30
29
|
* object reaches the responder as an empty request. The listener stops once
|
|
31
|
-
* the current test finishes, so it must run inside a test;
|
|
30
|
+
* the current test finishes, so it must run inside a test; `stop` stops it
|
|
32
31
|
* sooner, and a second stop does nothing.
|
|
33
32
|
*/
|
|
34
33
|
export function startStubSessionBridge(path: string, respond: BridgeResponder): StubSessionBridge {
|
|
@@ -80,12 +79,7 @@ export function startStubSessionBridge(path: string, respond: BridgeResponder):
|
|
|
80
79
|
get reads() {
|
|
81
80
|
return reads;
|
|
82
81
|
},
|
|
83
|
-
|
|
84
|
-
[Symbol.asyncDispose]: () => {
|
|
85
|
-
stop();
|
|
86
|
-
|
|
87
|
-
return Promise.resolve();
|
|
88
|
-
},
|
|
82
|
+
stop,
|
|
89
83
|
};
|
|
90
84
|
}
|
|
91
85
|
|
|
@@ -5,8 +5,8 @@ import { registerTestCleanup } from './register-test-cleanup';
|
|
|
5
5
|
* the loopback address, takes every request, and never answers one. `paths`
|
|
6
6
|
* holds the request path of each request it took, in order. The server
|
|
7
7
|
* stops, dropping the connections it holds, once the current test finishes,
|
|
8
|
-
* so it must run inside a test;
|
|
9
|
-
*
|
|
8
|
+
* so it must run inside a test; `stop` stops it sooner and resolves once it
|
|
9
|
+
* has stopped, and a second stop does nothing.
|
|
10
10
|
*/
|
|
11
11
|
export function startStubSilentServer() {
|
|
12
12
|
const paths: string[] = [];
|
|
@@ -30,9 +30,6 @@ export function startStubSilentServer() {
|
|
|
30
30
|
return {
|
|
31
31
|
url: `http://127.0.0.1:${String(server.port)}/`,
|
|
32
32
|
paths,
|
|
33
|
-
|
|
34
|
-
void stop();
|
|
35
|
-
},
|
|
36
|
-
[Symbol.asyncDispose]: stop,
|
|
33
|
+
stop,
|
|
37
34
|
};
|
|
38
35
|
}
|
|
@@ -10,10 +10,10 @@ import { registerTestCleanup } from './register-test-cleanup';
|
|
|
10
10
|
* `none`; `ATC_TEST_FIXTURE_URL` holds the repository the fixture source
|
|
11
11
|
* lists, and `ATC_TEST_SOURCE_LOG` a file it appends each listing to.
|
|
12
12
|
* The process stops once the current test finishes, so it must run inside
|
|
13
|
-
* a test;
|
|
13
|
+
* a test; `stop` stops it sooner, and a second stop does nothing.
|
|
14
14
|
*/
|
|
15
15
|
export async function startStubSourceDaemon(env: Readonly<Record<string, string | undefined>>) {
|
|
16
|
-
const daemon = Bun.spawn([process.execPath, join(import.meta.dir, 'run-source-daemon.ts')], {
|
|
16
|
+
const daemon = Bun.spawn([process.execPath, join(import.meta.dir, 'run-stub-source-daemon.ts')], {
|
|
17
17
|
env: { ATC_TEST_SOURCES: 'fixture', ...env },
|
|
18
18
|
stdout: 'pipe',
|
|
19
19
|
stderr: 'ignore',
|
|
@@ -37,5 +37,5 @@ export async function startStubSourceDaemon(env: Readonly<Record<string, string
|
|
|
37
37
|
throw new Error('the source daemon exited before it listened');
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
return { pid: daemon.pid,
|
|
40
|
+
return { pid: daemon.pid, stop };
|
|
41
41
|
}
|
|
@@ -8,7 +8,7 @@ interface StubStalledClient {
|
|
|
8
8
|
|
|
9
9
|
// How many bytes have arrived on the connection and wait unread.
|
|
10
10
|
readonly countUnreadBytes: () => number;
|
|
11
|
-
readonly
|
|
11
|
+
readonly stop: () => void;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -18,7 +18,7 @@ interface StubStalledClient {
|
|
|
18
18
|
* a promise that never resolves, so whatever the daemon sends it after
|
|
19
19
|
* that backs up. Resolves once that first chunk arrives. The connection is
|
|
20
20
|
* destroyed once the current test finishes, so it must run inside a test;
|
|
21
|
-
*
|
|
21
|
+
* `stop` destroys it sooner, and a second stop does nothing.
|
|
22
22
|
*/
|
|
23
23
|
export async function startStubStalledClient(
|
|
24
24
|
socketPath: string,
|
|
@@ -56,6 +56,6 @@ export async function startStubStalledClient(
|
|
|
56
56
|
return {
|
|
57
57
|
chunks,
|
|
58
58
|
countUnreadBytes: () => socket.readableLength,
|
|
59
|
-
|
|
59
|
+
stop,
|
|
60
60
|
};
|
|
61
61
|
}
|
|
@@ -3,7 +3,7 @@ import type { Socket } from 'node:net';
|
|
|
3
3
|
import { registerTestCleanup } from './register-test-cleanup';
|
|
4
4
|
|
|
5
5
|
interface StubStalledListener {
|
|
6
|
-
readonly
|
|
6
|
+
readonly stop: () => void;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -11,7 +11,7 @@ interface StubStalledListener {
|
|
|
11
11
|
* path and accepts every connection but never reads from it, so what the
|
|
12
12
|
* kernel does not buffer stays queued on the sender's side. Resolves once
|
|
13
13
|
* it listens. Once the current test finishes, it destroys every connection
|
|
14
|
-
* it accepted and stops, so it must run inside a test;
|
|
14
|
+
* it accepted and stops, so it must run inside a test; `stop` does so
|
|
15
15
|
* sooner, and a second stop does nothing.
|
|
16
16
|
*/
|
|
17
17
|
export async function startStubStalledListener(path: string): Promise<StubStalledListener> {
|
|
@@ -39,6 +39,6 @@ export async function startStubStalledListener(path: string): Promise<StubStalle
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
return {
|
|
42
|
-
|
|
42
|
+
stop,
|
|
43
43
|
};
|
|
44
44
|
}
|
|
@@ -12,7 +12,7 @@ interface StubTCPDaemon {
|
|
|
12
12
|
// can wait until one piece of a split write has arrived before it sends
|
|
13
13
|
// the next.
|
|
14
14
|
readonly reads: number;
|
|
15
|
-
readonly
|
|
15
|
+
readonly stop: () => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// The two request fields the stand-in reads; every other field passes
|
|
@@ -25,7 +25,7 @@ const REQUEST = z.object({ id: z.number(), m: z.string() });
|
|
|
25
25
|
* the request's method, recording every method in `seen`. It sends no
|
|
26
26
|
* handshake of its own and checks no token. `reads` counts the reads it has
|
|
27
27
|
* taken from every connection. It stops once the current test finishes, so
|
|
28
|
-
* it must run inside a test;
|
|
28
|
+
* it must run inside a test; `stop` stops it sooner, and a second stop
|
|
29
29
|
* does nothing.
|
|
30
30
|
*/
|
|
31
31
|
export function startStubTCPDaemon(): StubTCPDaemon {
|
|
@@ -70,6 +70,6 @@ export function startStubTCPDaemon(): StubTCPDaemon {
|
|
|
70
70
|
get reads() {
|
|
71
71
|
return reads;
|
|
72
72
|
},
|
|
73
|
-
|
|
73
|
+
stop,
|
|
74
74
|
};
|
|
75
75
|
}
|
|
@@ -5,7 +5,7 @@ import { registerTestCleanup } from './register-test-cleanup';
|
|
|
5
5
|
* loopback address. A process whose `BETTER_AUTH_TELEMETRY_ENDPOINT` holds
|
|
6
6
|
* `url` sends its telemetry here. Every request is answered with HTTP 204,
|
|
7
7
|
* and `received` holds the URL of each request, in order. The server stops
|
|
8
|
-
* once the current test finishes, so it must run inside a test;
|
|
8
|
+
* once the current test finishes, so it must run inside a test; `stop`
|
|
9
9
|
* stops it sooner and resolves once it has stopped, and a second stop does
|
|
10
10
|
* nothing.
|
|
11
11
|
*/
|
|
@@ -27,6 +27,6 @@ export function startStubTelemetryCollector() {
|
|
|
27
27
|
return {
|
|
28
28
|
url: `http://127.0.0.1:${String(server.port)}/`,
|
|
29
29
|
received,
|
|
30
|
-
|
|
30
|
+
stop,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
@@ -6,7 +6,7 @@ interface StubUnansweringListener {
|
|
|
6
6
|
// What each read took from any connection, decoded, in arrival order.
|
|
7
7
|
readonly received: readonly string[];
|
|
8
8
|
|
|
9
|
-
readonly
|
|
9
|
+
readonly stop: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -14,7 +14,7 @@ interface StubUnansweringListener {
|
|
|
14
14
|
* on a loopback port, it records what each read takes from a connection,
|
|
15
15
|
* sends nothing back, and keeps the connection open. It stops, dropping
|
|
16
16
|
* every connection it holds, once the current test finishes, so it must run
|
|
17
|
-
* inside a test;
|
|
17
|
+
* inside a test; `stop` stops it sooner, and a second stop does nothing.
|
|
18
18
|
*/
|
|
19
19
|
export function startStubUnansweringListener(): StubUnansweringListener {
|
|
20
20
|
const received: string[] = [];
|
|
@@ -36,6 +36,6 @@ export function startStubUnansweringListener(): StubUnansweringListener {
|
|
|
36
36
|
return {
|
|
37
37
|
port: server.port,
|
|
38
38
|
received,
|
|
39
|
-
|
|
39
|
+
stop,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
@@ -4,7 +4,7 @@ interface StubUnansweringUnixListener {
|
|
|
4
4
|
// What each read took from any connection, decoded, in arrival order.
|
|
5
5
|
readonly received: readonly string[];
|
|
6
6
|
|
|
7
|
-
readonly
|
|
7
|
+
readonly stop: () => void;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -12,7 +12,7 @@ interface StubUnansweringUnixListener {
|
|
|
12
12
|
* it: it listens on the unix socket path, records what each read takes from
|
|
13
13
|
* a connection, sends nothing back, and keeps the connection open. It
|
|
14
14
|
* stops, dropping every connection it holds, once the current test
|
|
15
|
-
* finishes, so it must run inside a test;
|
|
15
|
+
* finishes, so it must run inside a test; `stop` stops it sooner, and a
|
|
16
16
|
* second stop does nothing.
|
|
17
17
|
*/
|
|
18
18
|
export function startStubUnansweringUnixListener(path: string): StubUnansweringUnixListener {
|
|
@@ -33,6 +33,6 @@ export function startStubUnansweringUnixListener(path: string): StubUnansweringU
|
|
|
33
33
|
|
|
34
34
|
return {
|
|
35
35
|
received,
|
|
36
|
-
|
|
36
|
+
stop,
|
|
37
37
|
};
|
|
38
38
|
}
|