test: share matrix maintenance fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 20:40:20 +01:00
parent 84d8cb0826
commit e3edd408aa
5 changed files with 82 additions and 97 deletions

View File

@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import { createMatrixReplyContextResolver, summarizeMatrixReplyEvent } from "./reply-context.js";
import { createPollStartEvent } from "./test-events.js";
import type { MatrixRawEvent } from "./types.js";
describe("matrix reply context", () => {
@@ -51,25 +52,9 @@ describe("matrix reply context", () => {
});
it("summarizes poll start events from poll content", () => {
expect(
summarizeMatrixReplyEvent({
event_id: "$poll",
sender: "@alice:example.org",
type: "m.poll.start",
origin_server_ts: Date.now(),
content: {
"m.poll.start": {
question: { "m.text": "Lunch?" },
kind: "m.poll.disclosed",
max_selections: 1,
answers: [
{ id: "a1", "m.text": "Pizza" },
{ id: "a2", "m.text": "Sushi" },
],
},
},
} as MatrixRawEvent),
).toBe("[Poll]\nLunch?\n\n1. Pizza\n2. Sushi");
expect(summarizeMatrixReplyEvent(createPollStartEvent("$poll"))).toBe(
"[Poll]\nLunch?\n\n1. Pizza\n2. Sushi",
);
});
it("resolves and caches reply context", async () => {

View File

@@ -0,0 +1,21 @@
import type { MatrixRawEvent } from "./types.js";
export function createPollStartEvent(eventId: string): MatrixRawEvent {
return {
event_id: eventId,
sender: "@alice:example.org",
type: "m.poll.start",
origin_server_ts: Date.now(),
content: {
"m.poll.start": {
question: { "m.text": "Lunch?" },
kind: "m.poll.disclosed",
max_selections: 1,
answers: [
{ id: "a1", "m.text": "Pizza" },
{ id: "a2", "m.text": "Sushi" },
],
},
},
};
}

View File

@@ -1,4 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { createPollStartEvent } from "./test-events.js";
import {
createMatrixThreadContextResolver,
summarizeMatrixThreadStarterEvent,
@@ -126,24 +127,8 @@ describe("matrix thread context", () => {
});
it("summarizes poll start thread roots from poll content", () => {
expect(
summarizeMatrixThreadStarterEvent({
event_id: "$root",
sender: "@alice:example.org",
type: "m.poll.start",
origin_server_ts: Date.now(),
content: {
"m.poll.start": {
question: { "m.text": "Lunch?" },
kind: "m.poll.disclosed",
max_selections: 1,
answers: [
{ id: "a1", "m.text": "Pizza" },
{ id: "a2", "m.text": "Sushi" },
],
},
},
} as MatrixRawEvent),
).toBe("[Poll]\nLunch?\n\n1. Pizza\n2. Sushi");
expect(summarizeMatrixThreadStarterEvent(createPollStartEvent("$root"))).toBe(
"[Poll]\nLunch?\n\n1. Pizza\n2. Sushi",
);
});
});

View File

@@ -645,6 +645,19 @@ describe("handleMatrixSubagentDeliveryTarget", () => {
});
describe("concurrent spawns across accounts", () => {
function spawnForAccount(accountId: "ops" | "forge") {
return handleMatrixSubagentSpawning(fakeApi, {
threadRequested: true,
requester: {
channel: "matrix",
accountId,
to: `room:!room-${accountId}:example.org`,
},
childSessionKey: `agent:${accountId}:subagent:child-${accountId}`,
agentId: `worker-${accountId}`,
});
}
beforeEach(() => {
bindMock.mockReset();
getManagerMock.mockReset();
@@ -664,18 +677,8 @@ describe("concurrent spawns across accounts", () => {
.mockResolvedValueOnce({ conversation: { accountId: "forge", conversationId: "$t-forge" } });
const [opsResult, forgeResult] = await Promise.all([
handleMatrixSubagentSpawning(fakeApi, {
threadRequested: true,
requester: { channel: "matrix", accountId: "ops", to: "room:!room-ops:example.org" },
childSessionKey: "agent:ops:subagent:child-ops",
agentId: "worker-ops",
}),
handleMatrixSubagentSpawning(fakeApi, {
threadRequested: true,
requester: { channel: "matrix", accountId: "forge", to: "room:!room-forge:example.org" },
childSessionKey: "agent:forge:subagent:child-forge",
agentId: "worker-forge",
}),
spawnForAccount("ops"),
spawnForAccount("forge"),
]);
expect(opsResult).toMatchObject({ status: "ok", threadBindingReady: true });
@@ -709,18 +712,8 @@ describe("concurrent spawns across accounts", () => {
.mockResolvedValueOnce({ conversation: { accountId: "forge", conversationId: "$t-forge" } });
const [opsResult, forgeResult] = await Promise.all([
handleMatrixSubagentSpawning(fakeApi, {
threadRequested: true,
requester: { channel: "matrix", accountId: "ops", to: "room:!room-ops:example.org" },
childSessionKey: "agent:ops:subagent:child-ops",
agentId: "worker-ops",
}),
handleMatrixSubagentSpawning(fakeApi, {
threadRequested: true,
requester: { channel: "matrix", accountId: "forge", to: "room:!room-forge:example.org" },
childSessionKey: "agent:forge:subagent:child-forge",
agentId: "worker-forge",
}),
spawnForAccount("ops"),
spawnForAccount("forge"),
]);
expect(opsResult).toEqual(

View File

@@ -67,6 +67,31 @@ function createSuccessfulMatrixMigrationDeps() {
};
}
function createWarningOnlyMaintenanceHarness() {
return {
deps: {
maybeCreateMatrixMigrationSnapshot: vi.fn(),
autoMigrateLegacyMatrixState: vi.fn(),
autoPrepareLegacyMatrixCrypto: vi.fn(),
},
log: {
info: vi.fn(),
warn: vi.fn(),
},
};
}
function expectWarningOnlyMaintenanceSkipped(
harness: ReturnType<typeof createWarningOnlyMaintenanceHarness>,
) {
expect(harness.deps.maybeCreateMatrixMigrationSnapshot).not.toHaveBeenCalled();
expect(harness.deps.autoMigrateLegacyMatrixState).not.toHaveBeenCalled();
expect(harness.deps.autoPrepareLegacyMatrixCrypto).not.toHaveBeenCalled();
expect(harness.log.info).toHaveBeenCalledWith(
"matrix: migration remains in a warning-only state; no pre-migration snapshot was needed yet",
);
}
describe("runMatrixStartupMaintenance", () => {
beforeEach(() => {
legacyCryptoInspectorAvailability.available = true;
@@ -104,30 +129,19 @@ describe("runMatrixStartupMaintenance", () => {
it("skips snapshot creation when startup only has warning-only migration state", async () => {
await withTempHome(async (home) => {
await seedLegacyMatrixState(home);
const maybeCreateMatrixMigrationSnapshotMock = vi.fn();
const autoMigrateLegacyMatrixStateMock = vi.fn();
const autoPrepareLegacyMatrixCryptoMock = vi.fn();
const info = vi.fn();
const warn = vi.fn();
const harness = createWarningOnlyMaintenanceHarness();
await runMatrixStartupMaintenance({
cfg: makeMatrixStartupConfig(false),
env: process.env,
deps: {
maybeCreateMatrixMigrationSnapshot: maybeCreateMatrixMigrationSnapshotMock as never,
autoMigrateLegacyMatrixState: autoMigrateLegacyMatrixStateMock as never,
autoPrepareLegacyMatrixCrypto: autoPrepareLegacyMatrixCryptoMock as never,
},
log: { info, warn },
deps: harness.deps as never,
log: harness.log,
});
expect(maybeCreateMatrixMigrationSnapshotMock).not.toHaveBeenCalled();
expect(autoMigrateLegacyMatrixStateMock).not.toHaveBeenCalled();
expect(autoPrepareLegacyMatrixCryptoMock).not.toHaveBeenCalled();
expect(info).toHaveBeenCalledWith(
"matrix: migration remains in a warning-only state; no pre-migration snapshot was needed yet",
expectWarningOnlyMaintenanceSkipped(harness);
expect(harness.log.warn).toHaveBeenCalledWith(
expect.stringContaining("could not be resolved yet"),
);
expect(warn).toHaveBeenCalledWith(expect.stringContaining("could not be resolved yet"));
});
});
@@ -136,30 +150,17 @@ describe("runMatrixStartupMaintenance", () => {
await withTempHome(async (home) => {
await seedLegacyMatrixCrypto(home);
const maybeCreateMatrixMigrationSnapshotMock = vi.fn();
const autoMigrateLegacyMatrixStateMock = vi.fn();
const autoPrepareLegacyMatrixCryptoMock = vi.fn();
const info = vi.fn();
const warn = vi.fn();
const harness = createWarningOnlyMaintenanceHarness();
await runMatrixStartupMaintenance({
cfg: makeMatrixStartupConfig(),
env: process.env,
deps: {
maybeCreateMatrixMigrationSnapshot: maybeCreateMatrixMigrationSnapshotMock as never,
autoMigrateLegacyMatrixState: autoMigrateLegacyMatrixStateMock as never,
autoPrepareLegacyMatrixCrypto: autoPrepareLegacyMatrixCryptoMock as never,
},
log: { info, warn },
deps: harness.deps as never,
log: harness.log,
});
expect(maybeCreateMatrixMigrationSnapshotMock).not.toHaveBeenCalled();
expect(autoMigrateLegacyMatrixStateMock).not.toHaveBeenCalled();
expect(autoPrepareLegacyMatrixCryptoMock).not.toHaveBeenCalled();
expect(info).toHaveBeenCalledWith(
"matrix: migration remains in a warning-only state; no pre-migration snapshot was needed yet",
);
expect(warn).toHaveBeenCalledWith(
expectWarningOnlyMaintenanceSkipped(harness);
expect(harness.log.warn).toHaveBeenCalledWith(
"matrix: legacy encrypted-state warnings:\n- Legacy Matrix encrypted state was detected, but the Matrix crypto inspector is unavailable.",
);
});