项目文件夹

文件
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

721 行
22 KiB
TypeScript

import { createHash, createHmac } from 'node:crypto';
import { describe, expect, it, vi } from 'vitest';
import worker, { type Env } from '../src/index';
const env: Env = {
LANGFUSE_PUBLIC_KEY: 'pk-lf-test',
LANGFUSE_SECRET_KEY: 'sk-lf-test',
LANGFUSE_BASE_URL: 'https://us.cloud.langfuse.com',
};
const objectUploadSecret = 'object-upload-secret';
function makeRequest(body: unknown): Request {
return new Request('https://telemetry.open-design.ai/api/langfuse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Open-Design-Telemetry': 'langfuse-ingestion-v1',
},
body: JSON.stringify(body),
});
}
function makeRateLimiter(success: boolean) {
return {
limit: vi.fn(async () => ({ success })),
};
}
function makeScopeKv(seed: Record<string, string> = {}) {
const values = new Map(Object.entries(seed));
return {
get: vi.fn(async (key: string) => values.get(key) ?? null),
put: vi.fn(async (key: string, value: string) => {
values.set(key, value);
}),
};
}
function objectRelayHeaders(): Record<string, string> {
return {
'Content-Type': 'application/json',
'X-Open-Design-Telemetry': 'object-ingestion-v1',
};
}
function base64Url(value: string): string {
return btoa(value).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
function uploadToken(scope: Record<string, unknown>): string {
const payload = base64Url(JSON.stringify({
version: 1,
exp: Math.floor(Date.now() / 1000) + 300,
...scope,
}));
const signature = createHmac('sha256', objectUploadSecret).update(payload).digest('hex');
return `${payload}.${signature}`;
}
function requireSha256(value: string): string {
return createHash('sha256').update(value).digest('hex');
}
function makeObjectRequest(body: unknown): Request {
let requestBody = body;
if (
body &&
typeof body === 'object' &&
!Array.isArray(body) &&
!('upload_token' in body) &&
Array.isArray((body as { objects?: unknown }).objects)
) {
const objectBody = body as {
client_id?: string;
project_id?: string;
run_id?: string;
objects: Array<Record<string, unknown>>;
};
requestBody = {
...objectBody,
upload_token: uploadToken({
client_id: objectBody.client_id ?? 'installation-1',
project_id: objectBody.project_id ?? 'proj-1',
run_id: objectBody.run_id ?? 'run-1',
objects: objectBody.objects.map((object) => {
const content = typeof object.content_base64 === 'string'
? Buffer.from(object.content_base64, 'base64')
: Buffer.alloc(0);
return {
storage_ref: object.storage_ref,
object_class: object.object_class,
size_bytes: content.byteLength,
sha256: `sha256:${createHash('sha256').update(content).digest('hex')}`,
};
}),
}),
};
}
const bodyText = JSON.stringify(requestBody);
return new Request('https://telemetry.open-design.ai/api/objects/batch', {
method: 'POST',
headers: objectRelayHeaders(),
body: bodyText,
});
}
function makeUnsignedObjectRequest(body: unknown): Request {
return new Request('https://telemetry.open-design.ai/api/objects/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Open-Design-Telemetry': 'object-ingestion-v1',
},
body: JSON.stringify(body),
});
}
function base64(value: string): string {
return btoa(value);
}
describe('telemetry worker', () => {
it('returns a health response for browser checks', async () => {
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/langfuse'),
env,
);
expect(response.status).toBe(200);
expect(await response.json()).toEqual({
ok: true,
service: 'open-design-telemetry-relay',
configured: true,
objectRelayConfigured: false,
upstream: 'https://us.cloud.langfuse.com/api/public/ingestion',
});
});
it('reports unconfigured health without exposing secrets', async () => {
const response = await worker.fetch(new Request('https://telemetry.open-design.ai/health'), {});
expect(response.status).toBe(200);
expect(await response.json()).toEqual({
ok: true,
service: 'open-design-telemetry-relay',
configured: false,
objectRelayConfigured: false,
upstream: 'https://us.cloud.langfuse.com/api/public/ingestion',
});
});
it('reports object relay unconfigured when the bucket exists without an upload secret', async () => {
const response = await worker.fetch(new Request('https://telemetry.open-design.ai/health'), {
TRACE_OBJECT_BUCKET: { put: vi.fn(async () => ({})) },
});
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({
objectRelayConfigured: false,
});
});
it('forwards valid Langfuse ingestion batches with server-side auth', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ successes: [{ id: 'evt-1' }], errors: [] }), {
status: 207,
headers: { 'Content-Type': 'application/json' },
}),
);
const response = await worker.fetch(
makeRequest({
batch: [
{
id: 'evt-1',
type: 'trace-create',
timestamp: '2026-05-11T00:00:00.000Z',
body: { id: 'trace-1', name: 'open-design-turn' },
},
],
}),
env,
);
expect(response.status).toBe(207);
expect(fetchSpy).toHaveBeenCalledTimes(1);
const [url, init] = fetchSpy.mock.calls[0]!;
expect(url).toBe('https://us.cloud.langfuse.com/api/public/ingestion');
expect((init as RequestInit).headers).toMatchObject({
Authorization: expect.stringMatching(/^Basic /),
'Content-Type': 'application/json',
});
fetchSpy.mockRestore();
});
it('rejects requests without the Open Design client marker', async () => {
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/langfuse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ batch: [] }),
}),
env,
);
expect(response.status).toBe(403);
});
it('rate limits validated batches before forwarding', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch');
const limiter = makeRateLimiter(false);
const response = await worker.fetch(
makeRequest({
batch: [
{
id: 'evt-1',
type: 'trace-create',
timestamp: '2026-05-11T00:00:00.000Z',
body: {
id: 'trace-1',
name: 'open-design-turn',
userId: 'installation-1',
},
},
],
}),
{ ...env, TELEMETRY_CLIENT_RATE_LIMITER: limiter },
);
expect(response.status).toBe(429);
expect(limiter.limit).toHaveBeenCalledWith({ key: 'client:installation-1' });
expect(fetchSpy).not.toHaveBeenCalled();
fetchSpy.mockRestore();
});
it('rejects malformed batches before forwarding', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch');
const response = await worker.fetch(makeRequest({ batch: [{ type: 'bad' }] }), env);
expect(response.status).toBe(400);
expect(await response.json()).toEqual({
error: 'body.batch[0].id must be a string',
});
expect(fetchSpy).not.toHaveBeenCalled();
fetchSpy.mockRestore();
});
it('fails closed when Langfuse credentials are absent', async () => {
const response = await worker.fetch(makeRequest({ batch: [] }), {});
expect(response.status).toBe(503);
});
it('stores object batches through the R2 binding without calling Langfuse', async () => {
const put = vi.fn(async () => ({}));
const fetchSpy = vi.spyOn(globalThis, 'fetch');
const response = await worker.fetch(
makeObjectRequest({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
mime: 'text/plain',
content_base64: base64('hello object'),
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put },
TRACE_OBJECT_PREFIX: 'observability',
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
},
);
expect(response.status).toBe(200);
expect(fetchSpy).not.toHaveBeenCalled();
expect(put).toHaveBeenCalledTimes(1);
const putCalls = (put as unknown as { mock: { calls: unknown[][] } }).mock.calls;
expect(putCalls[0]![0]).toBe(
'observability/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
);
const body = await response.json() as { objects: Array<Record<string, unknown>> };
expect(body.objects[0]).toMatchObject({
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
status: 'available',
size_bytes: 12,
});
expect(body.objects[0]?.sha256).toEqual(expect.stringMatching(/^sha256:/));
fetchSpy.mockRestore();
});
it('rejects object authorization metadata without registered telemetry scope', async () => {
const content = 'hello object';
const scopeKv = makeScopeKv();
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/objects/authorize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Open-Design-Telemetry': 'object-ingestion-v1',
'CF-Connecting-IP': '203.0.113.10',
},
body: JSON.stringify({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
size_bytes: content.length,
sha256: `sha256:${requireSha256(content)}`,
},
],
}),
}),
{
...env,
TRACE_OBJECT_BUCKET: { put: vi.fn() },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
TRACE_OBJECT_SCOPE_KV: scopeKv,
},
);
expect(response.status).toBe(403);
expect(await response.json()).toEqual({
error: 'object upload authority is not registered',
});
});
it('issues short-lived object upload tokens for scopes registered by telemetry traces', async () => {
const content = 'hello object';
const scopeKv = makeScopeKv();
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ successes: [], errors: [] }), { status: 207 }),
);
const traceResponse = await worker.fetch(
makeRequest({
batch: [
{
id: 'evt-1',
type: 'trace-create',
timestamp: '2026-06-08T00:00:00.000Z',
body: {
id: 'run-1',
name: 'open-design-turn',
userId: 'installation-1',
metadata: {
projectId: 'proj-1',
attachment_manifest: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
size_bytes: content.length,
sha256: `sha256:${requireSha256(content)}`,
},
],
},
},
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put: vi.fn() },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
TRACE_OBJECT_SCOPE_KV: scopeKv,
},
);
expect(traceResponse.status).toBe(207);
expect(scopeKv.put).toHaveBeenCalledTimes(1);
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/objects/authorize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Open-Design-Telemetry': 'object-ingestion-v1',
'CF-Connecting-IP': '203.0.113.10',
},
body: JSON.stringify({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
size_bytes: content.length,
sha256: `sha256:${requireSha256(content)}`,
},
],
}),
}),
{
...env,
TRACE_OBJECT_BUCKET: { put: vi.fn() },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
TRACE_OBJECT_SCOPE_KV: scopeKv,
},
);
expect(response.status).toBe(200);
const body = await response.json() as { upload_token?: string; expires_at?: string };
expect(body.upload_token).toEqual(expect.stringMatching(/^[A-Za-z0-9_-]+\.[a-f0-9]{64}$/));
expect(body.expires_at).toEqual(expect.stringMatching(/^\d{4}-\d{2}-\d{2}T/));
fetchSpy.mockRestore();
});
it('registers object upload scopes when only a sibling observation is rejected', async () => {
const content = 'hello object';
const scopeKv = makeScopeKv();
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
JSON.stringify({
successes: [{ id: 'trace-evt-1' }],
errors: [{ id: 'span-evt-1', status: 400 }],
}),
{ status: 207 },
),
);
const response = await worker.fetch(
makeRequest({
batch: [
{
id: 'trace-evt-1',
type: 'trace-create',
timestamp: '2026-06-08T00:00:00.000Z',
body: {
id: 'run-1',
name: 'open-design-turn',
userId: 'installation-1',
metadata: {
projectId: 'proj-1',
artifact_manifest: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/artifact/art-1/index.html',
object_class: 'artifact',
size_bytes: content.length,
sha256: `sha256:${requireSha256(content)}`,
},
],
},
},
},
{
id: 'span-evt-1',
type: 'span-create',
timestamp: '2026-06-08T00:00:00.000Z',
body: { id: 'span-1', traceId: 'run-1', name: 'agent-call' },
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put: vi.fn() },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
TRACE_OBJECT_SCOPE_KV: scopeKv,
},
);
expect(response.status).toBe(207);
expect(scopeKv.put).toHaveBeenCalledTimes(1);
fetchSpy.mockRestore();
});
it('does not register object upload scopes when Langfuse rejects the trace batch', async () => {
const scopeKv = makeScopeKv();
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ successes: [], errors: [{ id: 'evt-1', status: 400 }] }), {
status: 207,
}),
);
const response = await worker.fetch(
makeRequest({
batch: [
{
id: 'evt-1',
type: 'trace-create',
timestamp: '2026-06-08T00:00:00.000Z',
body: {
id: 'run-1',
name: 'open-design-turn',
userId: 'installation-1',
metadata: {
projectId: 'proj-1',
artifact_manifest: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/artifact/art-1/index.html',
object_class: 'artifact',
size_bytes: 12,
sha256: `sha256:${requireSha256('hello object')}`,
},
],
},
},
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put: vi.fn() },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
TRACE_OBJECT_SCOPE_KV: scopeKv,
},
);
expect(response.status).toBe(207);
expect(scopeKv.put).not.toHaveBeenCalled();
fetchSpy.mockRestore();
});
it('rejects object batches without the object marker', async () => {
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/objects/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ objects: [] }),
}),
{ ...env, TRACE_OBJECT_BUCKET: { put: vi.fn() } },
);
expect(response.status).toBe(403);
});
it('rejects marker-only object batches without upload token', async () => {
const put = vi.fn(async () => ({}));
const response = await worker.fetch(
makeUnsignedObjectRequest({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
mime: 'text/plain',
content_base64: base64('hello object'),
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
},
);
expect(response.status).toBe(400);
expect(await response.json()).toEqual({
error: 'body.upload_token must be a string',
});
expect(put).not.toHaveBeenCalled();
});
it('rate limits unsigned object batches by IP before reading the body', async () => {
const put = vi.fn(async () => ({}));
const limiter = makeRateLimiter(false);
const unreadableBody = new ReadableStream({
pull(controller) {
controller.error(new Error('object body should not be read'));
},
});
const response = await worker.fetch(
new Request('https://telemetry.open-design.ai/api/objects/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CF-Connecting-IP': '203.0.113.10',
'X-Open-Design-Telemetry': 'object-ingestion-v1',
},
body: unreadableBody,
duplex: 'half',
} as RequestInit & { duplex: 'half' }),
{
...env,
TELEMETRY_IP_RATE_LIMITER: limiter,
TRACE_OBJECT_BUCKET: { put },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
},
);
expect(response.status).toBe(429);
expect(limiter.limit).toHaveBeenCalledWith({ key: 'ip:203.0.113.10' });
expect(put).not.toHaveBeenCalled();
});
it('rejects signed-looking object batches when server upload authority is absent', async () => {
const put = vi.fn(async () => ({}));
const response = await worker.fetch(
makeObjectRequest({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
mime: 'text/plain',
content_base64: base64('hello object'),
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put },
},
);
expect(response.status).toBe(503);
expect(await response.json()).toEqual({
error: 'object relay upload authority is not configured',
});
expect(put).not.toHaveBeenCalled();
});
it('rejects object batches without upload authority before reading the body', async () => {
const put = vi.fn(async () => ({}));
const request = new Request('https://telemetry.open-design.ai/api/objects/batch', {
method: 'POST',
headers: {
'X-Open-Design-Telemetry': 'object-ingestion-v1',
},
body: 'object body should not be read',
});
const textSpy = vi.spyOn(request, 'text').mockRejectedValue(
new Error('object body should not be read'),
);
const response = await worker.fetch(
request,
{
...env,
TRACE_OBJECT_BUCKET: { put },
},
);
expect(response.status).toBe(503);
expect(await response.json()).toEqual({
error: 'object relay upload authority is not configured',
});
expect(textSpy).not.toHaveBeenCalled();
expect(put).not.toHaveBeenCalled();
});
it('rejects object refs outside the signed project and run namespace', async () => {
const put = vi.fn(async () => ({}));
const response = await worker.fetch(
makeObjectRequest({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-2/runs/run-1/attachment/att-1/brief.txt',
object_class: 'attachment',
mime: 'text/plain',
content_base64: base64('hello object'),
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put },
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
},
);
expect(response.status).toBe(400);
expect(await response.json()).toEqual({
error: 'body.objects[0].storage_ref must match the project, run, and object class',
});
expect(put).not.toHaveBeenCalled();
});
it('reports oversized objects without writing them', async () => {
const put = vi.fn(async () => ({}));
const response = await worker.fetch(
makeObjectRequest({
client_id: 'installation-1',
project_id: 'proj-1',
run_id: 'run-1',
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/artifact/art-1/index.html',
object_class: 'artifact',
mime: 'text/html',
content_base64: base64('too large'),
},
],
}),
{
...env,
TRACE_OBJECT_BUCKET: { put },
TRACE_OBJECT_MAX_BYTES: '4',
TRACE_OBJECT_UPLOAD_SECRET: objectUploadSecret,
},
);
expect(response.status).toBe(200);
expect(put).not.toHaveBeenCalled();
expect(await response.json()).toEqual({
objects: [
{
storage_ref: 'od://objects/workspaces/unknown/projects/proj-1/runs/run-1/artifact/art-1/index.html',
status: 'unavailable',
reason: 'object_too_large',
size_bytes: 9,
},
],
});
});
});