Files
callstack__agent-device/test/integration/live-device-e2e/event-timeline.ts
Michał Pierzchała 32b9db2d7a test: add Android full emulator coverage (#1484)
* test: add Android full emulator coverage

* ci: package Android helpers before nightly coverage

* fix: harden Android nightly runtime evidence

* fix: expose trace artifacts in MCP schema

* style: format Android coverage manifest

* fix: address Android coverage review findings

* refactor: share live device coverage helpers

* fix: restore fixture landmarks in device smokes

* refactor: centralize live artifact assertions

* fix: normalize fixture canary visibility
2026-07-30 21:07:06 +02:00

80 lines
2.4 KiB
TypeScript

export type EventTimelinePage = {
events?: unknown;
nextCursor?: unknown;
};
type EventTimeline = {
commands: string[];
pages: Array<{
cursor: string;
eventCount: number;
nextCursor?: string;
}>;
};
const MAX_EVENT_TIMELINE_PAGES = 100;
export async function collectPagedEventTimeline(
readPage: (cursor?: string) => Promise<EventTimelinePage>,
): Promise<EventTimeline> {
const commands: string[] = [];
const pages: EventTimeline['pages'] = [];
let cursor: string | undefined;
let cursorOffset = 0;
while (pages.length < MAX_EVENT_TIMELINE_PAGES) {
const page = await readPage(cursor);
const pageCommands = readPageCommands(page.events, cursor);
const next = readNextCursor(page.nextCursor, cursorOffset);
commands.push(...pageCommands);
pages.push({
cursor: cursor ?? '0',
eventCount: pageCommands.length,
...(next === undefined ? {} : { nextCursor: next.cursor }),
});
if (next === undefined) return { commands, pages };
cursorOffset = next.offset;
cursor = next.cursor;
}
throw new Error(`events pagination exceeded ${MAX_EVENT_TIMELINE_PAGES} pages`);
}
function readPageCommands(events: unknown, cursor: string | undefined): string[] {
if (!Array.isArray(events)) {
throw new Error('events page must contain an events array');
}
return events.map((event, index) => readEventCommand(event, index, cursor));
}
function readNextCursor(
value: unknown,
currentOffset: number,
): { cursor: string; offset: number } | undefined {
if (value === undefined) return undefined;
if (typeof value !== 'string' || !/^(?:0|[1-9]\d*)$/.test(value)) {
throw new Error('events nextCursor must be a canonical non-negative integer string');
}
const offset = Number(value);
if (!Number.isSafeInteger(offset)) {
throw new Error('events nextCursor must be a safe non-negative integer string');
}
if (offset <= currentOffset) {
throw new Error(`events pagination did not advance beyond cursor ${currentOffset}`);
}
return { cursor: value, offset };
}
function readEventCommand(event: unknown, index: number, cursor: string | undefined): string {
if (
typeof event !== 'object' ||
event === null ||
Array.isArray(event) ||
typeof (event as { command?: unknown }).command !== 'string'
) {
throw new Error(`events entry ${index} at cursor ${cursor ?? '0'} must name a command`);
}
return (event as { command: string }).command;
}