test(qa): read authoritative delivery state in channel e2e (#168796)

* test(qa): read fresh delivery state in the ambiguous Discord announcement e2e

After 1d76f57d4b the test reused an empty cached queue observation across the child Gateway's writes and timed out (78s); read the canonical delivery projection fresh instead.

* test(qa): match Discord lifecycle drafts by tool label

cb9bdcd6e7 renders per-tool glyphs ("🛠️ Exec") while the lifecycle scenario waited for "• Exec" and timed out with the correct draft visible. Match the tool label; renderer tests keep glyph coverage.

* test(qa): read delivery rows outside the runtime cache

The isolated Gateway writes in another process, so its commits cannot invalidate the observer process’s empty-queue admission. The physical-database cache introduced in 566005dc13 survives read-only handle reopenings. Read the canonical unfinished query directly and retain the row decoder, outbound projection, and every existing custody assertion.
This commit is contained in:
Peter Steinberger 2026-10-10 20:04:22 -07:00 • committed by GitHub
parent 01ead786dc
commit cfcb7bda17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 6 deletions

View file

@ -107,13 +107,13 @@ export async function runDiscordScenario(
predicate: (message) =>
message.senderId === environment.sutIdentity.id &&
message.text.includes(run.progressLabel) &&
message.text.includes("• Exec"),
message.text.includes("Exec"),
});
await waitForDiscordMessageText({
token: environment.runtimeEnv.driverBotToken,
channelId: environment.runtimeEnv.channelId,
messageId: draft.message.messageId,
textIncludes: [run.progressLabel, "• Exec"],
textIncludes: [run.progressLabel, "Exec"],
timeoutMs: remainingMs(),
});
const final = await pollChannelMessages({
@ -156,7 +156,7 @@ export async function runDiscordScenario(
token: environment.runtimeEnv.driverBotToken,
channelId: environment.runtimeEnv.channelId,
messageId: failed.draft.message.messageId,
textIncludes: [run.progressLabel, "• Exec"],
textIncludes: [run.progressLabel, "Exec"],
timeoutMs: remainingMs(),
});
return {

View file

@ -30,6 +30,14 @@ import {
connectGatewayClient,
disconnectGatewayClient,
} from "../../../../src/gateway/test-helpers.e2e.js";
import {
deliveryQueueEntriesQuery,
inflateDeliveryQueueRow,
} from "../../../../src/infra/delivery-queue-sqlite-bound.js";
import { executeSqliteQuerySync } from "../../../../src/infra/kysely-sync.js";
import { OUTBOUND_EXECUTABLE_QUEUE_NAMES } from "../../../../src/infra/outbound/delivery-queue-namespaces.js";
import { projectOutboundDelivery } from "../../../../src/infra/outbound/delivery-queue-projection.js";
import { withOpenClawStateDatabaseReadOnly } from "../../../../src/state/openclaw-state-db-readonly.js";
import { stopQaGatewayFixture } from "../../../helpers/qa-gateway-cleanup.js";
import { readQaSubagentRuns } from "../../../helpers/qa-subagent-runs.js";
@ -1476,8 +1484,6 @@ describe("channel progress presentation through an isolated Gateway", () => {
body: JSON.stringify(inbound.providerBody),
});
expect(injected.ok, await injected.text()).toBe(true);
const { loadUnfinishedDeliveries } =
await import("../../../../src/infra/outbound/delivery-queue-storage.js");
const stateDir = gateway.runtimeEnv.OPENCLAW_STATE_DIR;
if (!stateDir) {
throw new Error("isolated Gateway state directory missing");
@ -1504,7 +1510,22 @@ describe("channel progress presentation through an isolated Gateway", () => {
lastError: run.delivery.lastError,
}
: undefined;
const pendingRows = await loadUnfinishedDeliveries(stateDir);
// The child Gateway cannot invalidate this process's physical-database queue cache.
// Read committed rows directly, retaining the canonical filters and projection.
const pendingRows = withOpenClawStateDatabaseReadOnly(
(database) =>
executeSqliteQuerySync(
database.db,
deliveryQueueEntriesQuery(database, OUTBOUND_EXECUTABLE_QUEUE_NAMES, "unfinished")
.select("queue_name")
.orderBy("enqueued_at", "asc")
.orderBy("id", "asc"),
).rows.flatMap((row) => {
const entry = inflateDeliveryQueueRow(row);
return entry ? [projectOutboundDelivery(row.queue_name, entry)] : [];
}),
{ env: gateway.runtimeEnv },
);
queueRows = pendingRows.map(({ id, channel, to, recoveryState, lastError }) => ({
id,
channel,