Skip to content

Commit 8831178

Browse files
authored
Merge pull request #4265 from cardstack/worktree-cs-10419
Implement test realm management for AI-generated tests
2 parents d246e5b + cf6f094 commit 8831178

69 files changed

Lines changed: 4756 additions & 320 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ test-results/.last-run.json
1313
traefik/dynamic/*.yml
1414
.mise.local.toml
1515
packages/openrouter-realm/OpenRouterModel/
16+
packages/host/dist

packages/matrix/docker/synapse/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export async function cfgDirFromTemplate(
102102
}
103103
const configDir = dataDir
104104
? dataDir
105-
: await fse.mkdtemp(path.join(os.tmpdir(), 'synapsedocker-'));
105+
: await fse.mkdtemp(path.join(os.tmpdir(), 'sf-test-synapse-'));
106106

107107
// copy the contents of the template dir, omitting homeserver.yaml as we'll template that
108108
console.log(`Copy ${templateDir} -> ${configDir}`);

packages/realm-server/tests/realm-endpoints/cancel-indexing-job-test.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,142 @@ module(`realm-endpoints/${basename(__filename)}`, function () {
189189
);
190190
});
191191

192+
test('cancels both running and pending jobs when cancelPending is true', async function (assert) {
193+
let concurrencyGroup = `indexing:${testRealm.url}`;
194+
195+
// Create a running job (with active reservation)
196+
let [{ id: runningJobId }] = (await dbAdapter.execute(`INSERT INTO jobs
197+
(args, job_type, concurrency_group, timeout, priority)
198+
VALUES
199+
(
200+
'{"realmURL": "${testRealm.url}", "realmUsername":"node-test_realm"}',
201+
'from-scratch-index',
202+
'${concurrencyGroup}',
203+
180,
204+
0
205+
) RETURNING id`)) as { id: string }[];
206+
await dbAdapter.execute(`INSERT INTO job_reservations
207+
(job_id, locked_until ) VALUES (${runningJobId}, NOW() + INTERVAL '3 minutes')`);
208+
209+
// Create a pending job (no reservation)
210+
let [{ id: pendingJobId }] = (await dbAdapter.execute(`INSERT INTO jobs
211+
(args, job_type, concurrency_group, timeout, priority)
212+
VALUES
213+
(
214+
'{"realmURL": "${testRealm.url}", "realmUsername":"node-test_realm"}',
215+
'incremental-index',
216+
'${concurrencyGroup}',
217+
180,
218+
0
219+
) RETURNING id`)) as { id: string }[];
220+
221+
let response = await request
222+
.post('/_cancel-indexing-job')
223+
.set('Accept', 'application/json')
224+
.set(
225+
'Authorization',
226+
`Bearer ${createJWT(testRealm, 'writer', ['read', 'write'])}`,
227+
)
228+
.send({ cancelPending: true });
229+
230+
assert.strictEqual(response.status, 204, 'HTTP 204 response');
231+
232+
// Running job should be cancelled
233+
let [runningJob] = await dbAdapter.execute(
234+
`SELECT status, result, finished_at FROM jobs WHERE id = ${runningJobId}`,
235+
);
236+
assert.strictEqual(
237+
runningJob.status,
238+
'rejected',
239+
'running job was canceled',
240+
);
241+
assert.deepEqual(
242+
runningJob.result,
243+
{
244+
status: 418,
245+
message: 'User initiated job cancellation',
246+
},
247+
'running job result is cancellation payload',
248+
);
249+
assert.ok(runningJob.finished_at, 'running job has finish time');
250+
251+
// Pending job should ALSO be cancelled
252+
let [pendingJob] = await dbAdapter.execute(
253+
`SELECT status, result, finished_at FROM jobs WHERE id = ${pendingJobId}`,
254+
);
255+
assert.strictEqual(
256+
pendingJob.status,
257+
'rejected',
258+
'pending job was also canceled when cancelPending is true',
259+
);
260+
assert.deepEqual(
261+
pendingJob.result,
262+
{
263+
status: 418,
264+
message: 'User initiated job cancellation',
265+
},
266+
'pending job result is cancellation payload',
267+
);
268+
assert.ok(pendingJob.finished_at, 'pending job has finish time');
269+
});
270+
271+
test('default behavior (no body) only cancels running jobs, not pending', async function (assert) {
272+
let concurrencyGroup = `indexing:${testRealm.url}`;
273+
274+
let [{ id: runningJobId }] = (await dbAdapter.execute(`INSERT INTO jobs
275+
(args, job_type, concurrency_group, timeout, priority)
276+
VALUES
277+
(
278+
'{"realmURL": "${testRealm.url}", "realmUsername":"node-test_realm"}',
279+
'from-scratch-index',
280+
'${concurrencyGroup}',
281+
180,
282+
0
283+
) RETURNING id`)) as { id: string }[];
284+
await dbAdapter.execute(`INSERT INTO job_reservations
285+
(job_id, locked_until ) VALUES (${runningJobId}, NOW() + INTERVAL '3 minutes')`);
286+
287+
let [{ id: pendingJobId }] = (await dbAdapter.execute(`INSERT INTO jobs
288+
(args, job_type, concurrency_group, timeout, priority)
289+
VALUES
290+
(
291+
'{"realmURL": "${testRealm.url}", "realmUsername":"node-test_realm"}',
292+
'incremental-index',
293+
'${concurrencyGroup}',
294+
180,
295+
0
296+
) RETURNING id`)) as { id: string }[];
297+
298+
// No body — default behavior
299+
let response = await request
300+
.post('/_cancel-indexing-job')
301+
.set('Accept', 'application/json')
302+
.set(
303+
'Authorization',
304+
`Bearer ${createJWT(testRealm, 'writer', ['read', 'write'])}`,
305+
);
306+
307+
assert.strictEqual(response.status, 204, 'HTTP 204 response');
308+
309+
let [runningJob] = await dbAdapter.execute(
310+
`SELECT status FROM jobs WHERE id = ${runningJobId}`,
311+
);
312+
assert.strictEqual(
313+
runningJob.status,
314+
'rejected',
315+
'running job canceled',
316+
);
317+
318+
let [pendingJob] = await dbAdapter.execute(
319+
`SELECT status FROM jobs WHERE id = ${pendingJobId}`,
320+
);
321+
assert.strictEqual(
322+
pendingJob.status,
323+
'unfulfilled',
324+
'pending job NOT canceled when cancelPending is not set',
325+
);
326+
});
327+
192328
test('does not treat expired reservations as running jobs', async function (assert) {
193329
let concurrencyGroup = `indexing:${testRealm.url}`;
194330
let [{ id: jobId }] = (await dbAdapter.execute(`INSERT INTO jobs

packages/realm-server/tests/scripts/boot_preseeded.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ exec docker-entrypoint.sh postgres \
1010
-c full_page_writes=off \
1111
-c synchronous_commit=off \
1212
-c shared_buffers=16MB \
13-
-c max_connections=20 \
13+
-c max_connections=50 \
1414
-c wal_level=minimal \
1515
-c max_wal_senders=0 \
1616
-c max_replication_slots=0 \

packages/runtime-common/job-utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,31 @@ export async function cancelRunningJobsInConcurrencyGroup(
9696
}
9797
return runningJobIds;
9898
}
99+
100+
/**
101+
* Cancel ALL jobs in a concurrency group — both running (active reservations)
102+
* and pending (unfulfilled, no active reservation).
103+
*/
104+
export async function cancelAllJobsInConcurrencyGroup(
105+
dbAdapter: DBAdapter,
106+
concurrencyGroup: string,
107+
): Promise<{ cancelledRunning: string[]; cancelledPending: string[] }> {
108+
let cancelledRunning = await cancelRunningJobsInConcurrencyGroup(
109+
dbAdapter,
110+
concurrencyGroup,
111+
);
112+
113+
let pendingRows = (await query(dbAdapter, [
114+
`SELECT id FROM jobs WHERE concurrency_group =`,
115+
param(concurrencyGroup),
116+
`AND status = 'unfulfilled'`,
117+
] as Expression)) as { id: string }[];
118+
119+
let cancelledPending: string[] = [];
120+
for (let row of pendingRows) {
121+
await forceCancelJobById(dbAdapter, row.id);
122+
cancelledPending.push(row.id);
123+
}
124+
125+
return { cancelledRunning, cancelledPending };
126+
}

packages/runtime-common/realm.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,10 @@ import {
169169
type PublishabilityWarningType,
170170
type ResourceIndexEntry,
171171
} from './publishability';
172-
import { cancelRunningJobsInConcurrencyGroup } from './job-utils';
172+
import {
173+
cancelAllJobsInConcurrencyGroup,
174+
cancelRunningJobsInConcurrencyGroup,
175+
} from './job-utils';
173176

174177
export const REALM_ROOM_RETENTION_POLICY_MAX_LIFETIME = 60 * 60 * 1000;
175178

@@ -825,13 +828,31 @@ export class Realm {
825828
}
826829

827830
private async cancelIndexingJob(
828-
_request: Request,
831+
request: Request,
829832
requestContext: RequestContext,
830833
) {
831-
await cancelRunningJobsInConcurrencyGroup(
832-
this.#dbAdapter,
833-
`indexing:${this.url}`,
834-
);
834+
let cancelPending = false;
835+
try {
836+
let body = await request.text();
837+
if (body) {
838+
let parsed = JSON.parse(body) as { cancelPending?: boolean };
839+
cancelPending = parsed.cancelPending === true;
840+
}
841+
} catch {
842+
// No body or invalid JSON — use default (running only).
843+
}
844+
845+
if (cancelPending) {
846+
await cancelAllJobsInConcurrencyGroup(
847+
this.#dbAdapter,
848+
`indexing:${this.url}`,
849+
);
850+
} else {
851+
await cancelRunningJobsInConcurrencyGroup(
852+
this.#dbAdapter,
853+
`indexing:${this.url}`,
854+
);
855+
}
835856

836857
return createResponse({
837858
body: null,

0 commit comments

Comments
 (0)