chore(vendor): import pi 0.85.1
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
315
third_party/pi/packages/telemetry/src/testing/conformance.ts
vendored
Normal file
315
third_party/pi/packages/telemetry/src/testing/conformance.ts
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
import { deepStrictEqual, doesNotThrow, fail, ok, strictEqual } from "node:assert/strict";
|
||||
import type { SpanAttributes, SpanOptions, SpanStatus, TelemetrySpan } from "../index.ts";
|
||||
import type { RecordedTelemetrySpan } from "../memory.ts";
|
||||
import type {
|
||||
TelemetryAdapterConformanceCase,
|
||||
TelemetryAdapterFixture,
|
||||
TelemetryAdapterFixtureFactory,
|
||||
} from "./types.ts";
|
||||
|
||||
type ConformanceTest = (fixture: TelemetryAdapterFixture) => Promise<void>;
|
||||
|
||||
function createCase(
|
||||
factory: TelemetryAdapterFixtureFactory,
|
||||
group: string,
|
||||
name: string,
|
||||
test: ConformanceTest,
|
||||
): TelemetryAdapterConformanceCase {
|
||||
return {
|
||||
group,
|
||||
name,
|
||||
async run() {
|
||||
await using fixture = await factory();
|
||||
await test(fixture);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function findSpan(spans: readonly RecordedTelemetrySpan[], name: string): RecordedTelemetrySpan {
|
||||
const span = spans.find((candidate) => candidate.name === name);
|
||||
ok(span, `Expected recorded span ${name}`);
|
||||
return span;
|
||||
}
|
||||
|
||||
async function rejectsWithSameValue(operation: Promise<unknown>, expected: unknown): Promise<void> {
|
||||
try {
|
||||
await operation;
|
||||
fail("Expected operation to reject");
|
||||
} catch (error) {
|
||||
strictEqual(error, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function unreadable<T extends object>(value: T): T {
|
||||
return new Proxy(value, {
|
||||
get: () => {
|
||||
throw new Error("read");
|
||||
},
|
||||
getOwnPropertyDescriptor: () => {
|
||||
throw new Error("inspect");
|
||||
},
|
||||
getPrototypeOf: () => {
|
||||
throw new Error("prototype");
|
||||
},
|
||||
ownKeys: () => {
|
||||
throw new Error("enumerate");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Creates runner-independent cases for the callback telemetry adapter contract. */
|
||||
export function createTelemetryAdapterConformance(
|
||||
factory: TelemetryAdapterFixtureFactory,
|
||||
): readonly TelemetryAdapterConformanceCase[] {
|
||||
return [
|
||||
createCase(
|
||||
factory,
|
||||
"callback lifecycle",
|
||||
"admits once synchronously and preserves the result",
|
||||
async (fixture) => {
|
||||
let admitted = false;
|
||||
let calls = 0;
|
||||
const expected = { value: 42 };
|
||||
const result = fixture.context.startSpan({ name: "success" }, () => {
|
||||
admitted = true;
|
||||
calls++;
|
||||
return expected;
|
||||
});
|
||||
|
||||
strictEqual(admitted, true);
|
||||
strictEqual(calls, 1);
|
||||
strictEqual(await result, expected);
|
||||
deepStrictEqual(findSpan(await fixture.getSpans(), "success").status, { status: "ok" });
|
||||
strictEqual(findSpan(await fixture.getSpans(), "success").settled, true);
|
||||
},
|
||||
),
|
||||
|
||||
createCase(
|
||||
factory,
|
||||
"callback lifecycle",
|
||||
"preserves synchronous and asynchronous rejection values",
|
||||
async (fixture) => {
|
||||
const syncError = new Error("sync");
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "sync-error" }, () => {
|
||||
throw syncError;
|
||||
}),
|
||||
syncError,
|
||||
);
|
||||
|
||||
const asyncError = { kind: "async" };
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "async-error" }, async () => {
|
||||
throw asyncError;
|
||||
}),
|
||||
asyncError,
|
||||
);
|
||||
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "undefined-error" }, () => Promise.reject(undefined)),
|
||||
undefined,
|
||||
);
|
||||
|
||||
const unreadableError = unreadable({ kind: "unreadable" });
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "unreadable-error" }, () => {
|
||||
throw unreadableError;
|
||||
}),
|
||||
unreadableError,
|
||||
);
|
||||
|
||||
const asyncUnreadableError = unreadable({ kind: "async-unreadable" });
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "async-unreadable-error" }, () =>
|
||||
Promise.reject(asyncUnreadableError),
|
||||
),
|
||||
asyncUnreadableError,
|
||||
);
|
||||
|
||||
const spans = await fixture.getSpans();
|
||||
for (const name of [
|
||||
"sync-error",
|
||||
"async-error",
|
||||
"undefined-error",
|
||||
"unreadable-error",
|
||||
"async-unreadable-error",
|
||||
]) {
|
||||
strictEqual(findSpan(spans, name).status.status, "error");
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
createCase(factory, "status", "uses last explicit status without automatic overwrite", async (fixture) => {
|
||||
await fixture.context.startSpan({ name: "last-status" }, (span) => {
|
||||
span.setStatus({ status: "error", error: { name: "Expected", message: "first" } });
|
||||
span.setStatus({ status: "ok" });
|
||||
});
|
||||
|
||||
const thrown = new Error("after explicit status");
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "explicit-before-throw" }, (span) => {
|
||||
span.setStatus({ status: "ok" });
|
||||
throw thrown;
|
||||
}),
|
||||
thrown,
|
||||
);
|
||||
|
||||
const rejected = new Error("after async explicit status");
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "explicit-before-rejection" }, (span) => {
|
||||
span.setStatus({ status: "error", error: { name: "Expected", message: "async failure" } });
|
||||
return Promise.reject(rejected);
|
||||
}),
|
||||
rejected,
|
||||
);
|
||||
|
||||
await fixture.context.startSpan({ name: "expected-failure" }, (span) => {
|
||||
span.setStatus({ status: "error", error: { name: "Expected", message: "returned failure" } });
|
||||
return { ok: false };
|
||||
});
|
||||
|
||||
const spans = await fixture.getSpans();
|
||||
deepStrictEqual(findSpan(spans, "last-status").status, { status: "ok" });
|
||||
deepStrictEqual(findSpan(spans, "explicit-before-throw").status, { status: "ok" });
|
||||
deepStrictEqual(findSpan(spans, "explicit-before-rejection").status, {
|
||||
status: "error",
|
||||
error: { name: "Expected", message: "async failure" },
|
||||
});
|
||||
deepStrictEqual(findSpan(spans, "expected-failure").status, {
|
||||
status: "error",
|
||||
error: { name: "Expected", message: "returned failure" },
|
||||
});
|
||||
}),
|
||||
|
||||
createCase(factory, "recording", "merges attributes and records ordered events", async (fixture) => {
|
||||
await fixture.context.startSpan(
|
||||
{
|
||||
name: "recording",
|
||||
attributes: { start: "value", overwrite: "start", ignored: undefined },
|
||||
},
|
||||
(span) => {
|
||||
span.setAttributes({ count: 1, overwrite: "middle" });
|
||||
span.setAttributes({ count: undefined, overwrite: "end" });
|
||||
span.addEvent("first", { index: 1, ignored: undefined });
|
||||
span.addEvent("second", { index: 2 });
|
||||
},
|
||||
);
|
||||
|
||||
const span = findSpan(await fixture.getSpans(), "recording");
|
||||
deepStrictEqual(span.attributes, { start: "value", overwrite: "end", count: 1 });
|
||||
deepStrictEqual(span.events, [
|
||||
{ name: "first", attributes: { index: 1 } },
|
||||
{ name: "second", attributes: { index: 2 } },
|
||||
]);
|
||||
}),
|
||||
|
||||
createCase(factory, "recording", "ignores failed attribute calls atomically", async (fixture) => {
|
||||
await fixture.context.startSpan({ name: "atomic-attributes", attributes: { retained: "value" } }, (span) => {
|
||||
const attributes: SpanAttributes = {
|
||||
partial: "must not survive",
|
||||
unreadable: unreadable(["value"]),
|
||||
};
|
||||
doesNotThrow(() => span.setAttributes(attributes));
|
||||
});
|
||||
|
||||
deepStrictEqual(findSpan(await fixture.getSpans(), "atomic-attributes").attributes, {
|
||||
retained: "value",
|
||||
});
|
||||
}),
|
||||
|
||||
createCase(factory, "recording", "makes calls after settlement inert", async (fixture) => {
|
||||
let settledSpan: TelemetrySpan | undefined;
|
||||
await fixture.context.startSpan({ name: "settled", attributes: { value: "initial" } }, (span) => {
|
||||
settledSpan = span;
|
||||
});
|
||||
const capturedSpan = settledSpan;
|
||||
if (!capturedSpan) throw new Error("Expected callback span");
|
||||
|
||||
doesNotThrow(() => capturedSpan.setAttributes({ value: "late" }));
|
||||
doesNotThrow(() => capturedSpan.addEvent("late", { value: true }));
|
||||
doesNotThrow(() => capturedSpan.setStatus({ status: "error" }));
|
||||
let childAdmitted = false;
|
||||
const childResult = capturedSpan.startSpan({ name: "late-child" }, () => {
|
||||
childAdmitted = true;
|
||||
return 7;
|
||||
});
|
||||
strictEqual(childAdmitted, true);
|
||||
strictEqual(await childResult, 7);
|
||||
|
||||
const spans = await fixture.getSpans();
|
||||
strictEqual(spans.length, 1);
|
||||
deepStrictEqual(spans[0]?.attributes, { value: "initial" });
|
||||
deepStrictEqual(spans[0]?.events, []);
|
||||
deepStrictEqual(spans[0]?.status, { status: "ok" });
|
||||
}),
|
||||
|
||||
createCase(factory, "parentage", "records nested and concurrent child relationships", async (fixture) => {
|
||||
let releaseFirst: (() => void) | undefined;
|
||||
const firstGate = new Promise<void>((resolve) => {
|
||||
releaseFirst = resolve;
|
||||
});
|
||||
|
||||
await fixture.context.startSpan({ name: "parent" }, async (parent) => {
|
||||
const first = parent.startSpan({ name: "first-child" }, async () => {
|
||||
await firstGate;
|
||||
});
|
||||
const second = parent.startSpan({ name: "second-child" }, () => "done");
|
||||
strictEqual(await second, "done");
|
||||
releaseFirst?.();
|
||||
await first;
|
||||
});
|
||||
|
||||
const spans = await fixture.getSpans();
|
||||
const parent = findSpan(spans, "parent");
|
||||
const first = findSpan(spans, "first-child");
|
||||
const second = findSpan(spans, "second-child");
|
||||
strictEqual(parent.parentId, null);
|
||||
strictEqual(first.parentId, parent.id);
|
||||
strictEqual(second.parentId, parent.id);
|
||||
ok(second.endSequence !== undefined && first.endSequence !== undefined && parent.endSequence !== undefined);
|
||||
ok(second.endSequence < first.endSequence);
|
||||
ok(first.endSequence < parent.endSequence);
|
||||
}),
|
||||
|
||||
createCase(factory, "passivity", "suppresses unreadable telemetry payload failures", async (fixture) => {
|
||||
let calls = 0;
|
||||
const options = unreadable<SpanOptions>({ name: "unreadable-options", attributes: { secret: "value" } });
|
||||
const result = fixture.context.startSpan(options, () => {
|
||||
calls++;
|
||||
return 9;
|
||||
});
|
||||
|
||||
strictEqual(calls, 1);
|
||||
strictEqual(await result, 9);
|
||||
deepStrictEqual(await fixture.getSpans(), []);
|
||||
|
||||
await fixture.context.startSpan({ name: "unreadable-recording" }, (span) => {
|
||||
const attributes = unreadable<SpanAttributes>({ secret: "value" });
|
||||
const status = unreadable<SpanStatus>({ status: "ok" });
|
||||
doesNotThrow(() => span.setAttributes(attributes));
|
||||
doesNotThrow(() => span.addEvent("unreadable-event", attributes));
|
||||
doesNotThrow(() => span.setStatus(status));
|
||||
});
|
||||
|
||||
const recorded = await fixture.getSpans();
|
||||
strictEqual(recorded.length, 1);
|
||||
deepStrictEqual(recorded[0]?.attributes, {});
|
||||
deepStrictEqual(recorded[0]?.events, []);
|
||||
deepStrictEqual(recorded[0]?.status, { status: "ok" });
|
||||
}),
|
||||
|
||||
createCase(factory, "passivity", "ignores failed status calls atomically", async (fixture) => {
|
||||
const rejection = new Error("rejected after unreadable status");
|
||||
await rejectsWithSameValue(
|
||||
fixture.context.startSpan({ name: "unreadable-status" }, (span) => {
|
||||
const status = unreadable<SpanStatus>({ status: "ok" });
|
||||
doesNotThrow(() => span.setStatus(status));
|
||||
return Promise.reject(rejection);
|
||||
}),
|
||||
rejection,
|
||||
);
|
||||
|
||||
strictEqual(findSpan(await fixture.getSpans(), "unreadable-status").status.status, "error");
|
||||
}),
|
||||
];
|
||||
}
|
||||
6
third_party/pi/packages/telemetry/src/testing/index.ts
vendored
Normal file
6
third_party/pi/packages/telemetry/src/testing/index.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export { createTelemetryAdapterConformance } from "./conformance.ts";
|
||||
export type {
|
||||
TelemetryAdapterConformanceCase,
|
||||
TelemetryAdapterFixture,
|
||||
TelemetryAdapterFixtureFactory,
|
||||
} from "./types.ts";
|
||||
18
third_party/pi/packages/telemetry/src/testing/types.ts
vendored
Normal file
18
third_party/pi/packages/telemetry/src/testing/types.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { TelemetryContext } from "../index.ts";
|
||||
import type { RecordedTelemetrySpan } from "../memory.ts";
|
||||
|
||||
/** A fresh adapter instance and normalized snapshot reader owned by one conformance case. */
|
||||
export interface TelemetryAdapterFixture extends AsyncDisposable {
|
||||
readonly context: TelemetryContext;
|
||||
getSpans(): Promise<readonly RecordedTelemetrySpan[]>;
|
||||
}
|
||||
|
||||
/** Creates an isolated adapter fixture for one conformance case. */
|
||||
export type TelemetryAdapterFixtureFactory = () => Promise<TelemetryAdapterFixture>;
|
||||
|
||||
/** A runner-independent conformance case that can be registered with any test framework. */
|
||||
export interface TelemetryAdapterConformanceCase {
|
||||
readonly group: string;
|
||||
readonly name: string;
|
||||
run(): Promise<void>;
|
||||
}
|
||||
Reference in New Issue
Block a user