mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
a799025af9
## Summary & Motivation One warm compute instance serves many invocations, so the Compute Instance ID already in this panel can't distinguish steps that ran inline within a single flow-function invocation — sibling steps sharing a Request ID did. It's also the value Vercel Logs indexes by, and this panel's View Logs button is where a reader takes it next. The key is `vercelId`, not `requestId`: that's the name world-vercel stores the SDK's request id under crossing the wire, and AnalyticsEvent's sibling `requestId` field is declared but never written. Nothing in this repository populates `vercelId` on the object the panel receives yet — only AnalyticsEvent carries it, and grouping steps by invocation needs a step-level aggregation in workflow-server first — so the row ships as forward-compatible plumbing in the slot next to Compute Instance ID. The second commit is an independent fix: `sortByAttributeOrder` guarded `indexOf` with `|| 0`, but a miss returns -1, which is truthy, so any key absent from `attributeOrder` sorted ahead of every listed key. It can be dropped on its own. ## Test Plan Unit tests added; the two ordering assertions fail against the unfixed comparator. The new row could not be verified by hand — no local code path populates it.
102 lines
3.3 KiB
TypeScript
102 lines
3.3 KiB
TypeScript
import { createElement } from 'react';
|
||
import { renderToStaticMarkup } from 'react-dom/server';
|
||
import { describe, expect, it } from 'vitest';
|
||
import { AttributePanel } from '../src/components/sidebar/attribute-panel.js';
|
||
|
||
const render = (data: Record<string, unknown>): string =>
|
||
renderToStaticMarkup(createElement(AttributePanel, { data }));
|
||
|
||
/**
|
||
* `vercelId` is the key the analytics read contract stores a flow-function
|
||
* invocation's request id under (the SDK sends it as `requestId`). The panel
|
||
* surfaces it as "Request ID" to match what Vercel Logs calls the value, since
|
||
* this panel's View Logs button is where a reader takes it next.
|
||
*/
|
||
describe('AttributePanel request id', () => {
|
||
it('renders vercelId as a copyable "Request ID" row', () => {
|
||
const markup = render({
|
||
stepId: 'step_1',
|
||
vercelId: 'iad1::abc-123-def',
|
||
});
|
||
|
||
expect(markup).toContain('Request ID');
|
||
expect(markup).toContain('iad1::abc-123-def');
|
||
// Copy affordance, matching the other opaque ids in this section.
|
||
expect(markup).toContain('aria-label="Copy Request ID"');
|
||
});
|
||
|
||
it('places Request ID above the coarser Compute Instance ID', () => {
|
||
const markup = render({
|
||
stepId: 'step_1',
|
||
deploymentId: 'dpl_1',
|
||
computeInstanceId: 'cinst_01JQ',
|
||
vercelId: 'iad1::abc-123-def',
|
||
});
|
||
|
||
expect(markup.indexOf('Request ID')).toBeGreaterThan(
|
||
markup.indexOf('Deployment ID')
|
||
);
|
||
expect(markup.indexOf('Request ID')).toBeLessThan(
|
||
markup.indexOf('Compute Instance ID')
|
||
);
|
||
});
|
||
|
||
/**
|
||
* The analytics schemas type both provenance ids as nullable, and the panel
|
||
* only drops a row whose display fn returns `null` — so stringifying an
|
||
* absent value would render the literal text "null" beside the label.
|
||
*/
|
||
it('omits provenance rows whose analytics value is null', () => {
|
||
const markup = render({
|
||
stepId: 'step_1',
|
||
vercelId: null,
|
||
computeInstanceId: null,
|
||
});
|
||
|
||
expect(markup).not.toContain('Request ID');
|
||
expect(markup).not.toContain('Compute Instance ID');
|
||
expect(markup).toContain('step_1');
|
||
});
|
||
});
|
||
|
||
/**
|
||
* Rows are ordered by their position in the panel's explicit `attributeOrder`
|
||
* list. Keys missing from that list sorted ahead of every listed key, because
|
||
* `indexOf`'s `-1` miss is truthy and so defeated the `|| 0` fallback meant to
|
||
* catch it — which put a failed step's Error Code above its own name.
|
||
*/
|
||
describe('AttributePanel row ordering', () => {
|
||
it("keeps a failed step's Error Code below its identity rows", () => {
|
||
const markup = render({
|
||
stepId: 'step_1',
|
||
stepName: 'step//./src/workflows/order//processPayment',
|
||
status: 'failed',
|
||
errorCode: 'USER_ERROR',
|
||
});
|
||
|
||
expect(markup).toContain('Error Code');
|
||
expect(markup.indexOf('Error Code')).toBeGreaterThan(
|
||
markup.indexOf('Module')
|
||
);
|
||
expect(markup.indexOf('Error Code')).toBeGreaterThan(
|
||
markup.indexOf('Step ID')
|
||
);
|
||
});
|
||
|
||
it('keeps a hook’s classification flags below its token', () => {
|
||
const markup = render({
|
||
hookId: 'hook_1',
|
||
token: 'tok_1',
|
||
isWebhook: true,
|
||
isSystem: false,
|
||
});
|
||
|
||
expect(markup.indexOf('isWebhook')).toBeGreaterThan(
|
||
markup.indexOf('Token')
|
||
);
|
||
expect(markup.indexOf('isWebhook')).toBeGreaterThan(
|
||
markup.indexOf('Hook ID')
|
||
);
|
||
});
|
||
});
|