Skip to content

Commit 240301a

Browse files
committed
test(breadcrumbs): distinguish workflow identity from launch plan
Identical project and domain values let the self-link test pass even when it read the launch-plan identifier. Separate every identity so the route assertion covers project and domain as well as the workflow name. Signed-off-by: 1fanwang <1fannnw@gmail.com>
1 parent 6380175 commit 240301a

2 files changed

Lines changed: 36 additions & 93 deletions

File tree

packages/oss-console/src/components/Breadcrumbs/async/executionContext.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,16 @@ const getExecutionData = async (projectId: string, domainId: string, executionId
5050
};
5151

5252
const isExecutionTaskOrWorkflow = (executionData: Execution) => {
53-
// Check the resourceType of the actual executed entity (workflowId),
54-
// not the launch plan identifier
5553
return executionData.closure.workflowId.resourceType === ResourceType.TASK
5654
? ResourceType.TASK
5755
: ResourceType.WORKFLOW;
5856
};
5957

6058
const getTaskOrWorkflowName = (executionData: Execution): string => {
61-
// Return the actual workflow/task name from closure.workflowId,
62-
// not the launch plan name from spec.launchPlan
6359
return executionData.closure.workflowId.name;
6460
};
6561

6662
const getTaskOrWorkflowVersion = (executionData: Execution): string => {
67-
// Return the actual workflow/task version from closure.workflowId,
68-
// not the launch plan version from spec.launchPlan
6963
return executionData.closure.workflowId.version;
7064
};
7165

@@ -132,7 +126,6 @@ export const executonTaskWorkFlowNameAsyncSelfLink: BreadcrumbEntitySelfLinkAsyn
132126
const resourceName = getTaskOrWorkflowName(executionData);
133127
const resourceType = isExecutionTaskOrWorkflow(executionData);
134128

135-
// Use closure.workflowId (the actual workflow) instead of spec.launchPlan
136129
const { project: desinationProject, domain: desinationDomain } = getExecutionSpecProjectDomain(
137130
executionData.closure.workflowId,
138131
breadcrumb,
@@ -160,7 +153,6 @@ export const executionTaskWorkflowVersions: BreadcrumbAsyncPopOverData = async (
160153
const entityResourceName = getTaskOrWorkflowName(executionData);
161154
const entityResourceVersion = getTaskOrWorkflowVersion(executionData);
162155

163-
// Use closure.workflowId (the actual workflow) instead of spec.launchPlan
164156
const { project: desinationProject, domain: desinationDomain } = getExecutionSpecProjectDomain(
165157
executionData.closure.workflowId,
166158
breadcrumb,
@@ -348,7 +340,6 @@ export const executionTaskWorkflowViewAll: BreadcrumbAsyncViewAllLink = async (
348340
const executionType = isExecutionTaskOrWorkflow(executionData);
349341
const entityResourceName = getTaskOrWorkflowName(executionData);
350342

351-
// Use closure.workflowId (the actual workflow) instead of spec.launchPlan
352343
const { project: desinationProject, domain: desinationDomain } = getExecutionSpecProjectDomain(
353344
executionData.closure.workflowId,
354345
breadcrumb,

packages/oss-console/src/components/Breadcrumbs/async/tests/executionContext.test.ts

Lines changed: 36 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,61 @@
11
import Core from '@clients/common/flyteidl/core';
22
import { Execution } from '../../../../models/Execution/types';
3+
import {
4+
executonTaskWorkFlowNameAsyncValue,
5+
executonTaskWorkFlowNameAsyncSelfLink,
6+
executonNamedEntityAsyncValue,
7+
} from '../executionContext';
38

4-
// Mock the SimpleCacheCallbackManager
59
jest.mock('@clients/primitives/SimpleCache/SimpleCacheCallbackManager', () => ({
610
SimpleCacheCallbackManager: jest.fn().mockImplementation(() => ({
711
getCachedOrFetch: jest.fn((_key: string, fn: () => Promise<Execution>) => fn()),
812
})),
913
}));
1014

11-
// Mock the execution API
1215
const mockGetExecution = jest.fn();
1316
jest.mock('../../../../models/Execution/api', () => ({
1417
getExecution: (...args: unknown[]) => mockGetExecution(...args),
1518
listExecutions: jest.fn(),
1619
}));
1720

18-
// Mock the routes
1921
jest.mock('../../../../routes/routes', () => ({
2022
Routes: {
2123
WorkflowDetails: {
22-
makeUrl: jest.fn((project: string, domain: string, name: string) =>
23-
`/projects/${project}/domains/${domain}/workflows/${name}`),
24+
makeUrl: jest.fn(
25+
(project: string, domain: string, name: string) =>
26+
`/projects/${project}/domains/${domain}/workflows/${name}`,
27+
),
2428
},
2529
TaskDetails: {
26-
makeUrl: jest.fn((project: string, domain: string, name: string) =>
27-
`/projects/${project}/domains/${domain}/tasks/${name}`),
30+
makeUrl: jest.fn(
31+
(project: string, domain: string, name: string) =>
32+
`/projects/${project}/domains/${domain}/tasks/${name}`,
33+
),
2834
},
2935
ExecutionDetails: {
30-
makeUrl: jest.fn(({ project, domain, name }: { project: string; domain: string; name: string }) =>
31-
`/projects/${project}/domains/${domain}/executions/${name}`),
36+
makeUrl: jest.fn(
37+
({ project, domain, name }: { project: string; domain: string; name: string }) =>
38+
`/projects/${project}/domains/${domain}/executions/${name}`,
39+
),
3240
},
3341
EntityVersionDetails: {
3442
makeUrl: jest.fn(),
3543
},
3644
},
3745
}));
3846

39-
// Import after mocking
40-
import {
41-
executonTaskWorkFlowNameAsyncValue,
42-
executonTaskWorkFlowNameAsyncSelfLink,
43-
executonNamedEntityAsyncValue,
44-
} from '../executionContext';
45-
46-
/**
47-
* Creates a mock execution where the launch plan name differs from the workflow name.
48-
* This simulates the bug scenario where:
49-
* - Launch plan name: 'hello_world_lp'
50-
* - Actual workflow name: 'hello_world_wf'
51-
*/
5247
const createMockExecutionWithDifferentNames = (): Execution => ({
5348
id: {
54-
project: 'flytesnacks',
55-
domain: 'development',
49+
project: 'execution-project',
50+
domain: 'execution-domain',
5651
name: 'wf-execution-001',
5752
},
5853
spec: {
5954
launchPlan: {
6055
resourceType: Core.ResourceType.LAUNCH_PLAN,
61-
project: 'flytesnacks',
62-
domain: 'development',
63-
name: 'hello_world_lp', // Launch plan name
56+
project: 'launch-plan-project',
57+
domain: 'launch-plan-domain',
58+
name: 'hello_world_lp',
6459
version: '2025-04-09-15-56-08',
6560
},
6661
inputs: { literals: {} },
@@ -74,20 +69,17 @@ const createMockExecutionWithDifferentNames = (): Execution => ({
7469
closure: {
7570
workflowId: {
7671
resourceType: Core.ResourceType.WORKFLOW,
77-
project: 'flytesnacks',
78-
domain: 'development',
79-
name: 'hello_world_wf', // Actual workflow name (different!)
72+
project: 'workflow-project',
73+
domain: 'workflow-domain',
74+
name: 'hello_world_wf',
8075
version: '2025-04-09-15-56-08',
8176
},
82-
phase: 4, // ABORTED
77+
phase: 4,
8378
createdAt: { seconds: { low: 1732000000, high: 0, unsigned: false }, nanos: 0 },
8479
startedAt: { seconds: { low: 1732000000, high: 0, unsigned: false }, nanos: 0 },
8580
},
8681
});
8782

88-
/**
89-
* Creates a mock execution for a task (not a workflow)
90-
*/
9183
const createMockTaskExecution = (): Execution => ({
9284
id: {
9385
project: 'test-project',
@@ -118,16 +110,16 @@ const createMockTaskExecution = (): Execution => ({
118110
name: 'my-actual-task',
119111
version: 'v1',
120112
},
121-
phase: 3, // SUCCEEDED
113+
phase: 3,
122114
createdAt: { seconds: { low: 1732000000, high: 0, unsigned: false }, nanos: 0 },
123115
startedAt: { seconds: { low: 1732000000, high: 0, unsigned: false }, nanos: 0 },
124116
},
125117
});
126118

127119
describe('executionContext breadcrumb functions', () => {
128120
const mockBreadcrumb = {
129-
projectId: 'flytesnacks',
130-
domainId: 'development',
121+
projectId: 'workflow-domain',
122+
domainId: 'browsing-domain',
131123
value: '',
132124
defaultValue: () => '',
133125
};
@@ -147,9 +139,7 @@ describe('executionContext breadcrumb functions', () => {
147139

148140
const result = await executonTaskWorkFlowNameAsyncValue(mockLocation, mockBreadcrumb as any);
149141

150-
// Should return the WORKFLOW name, not the launch plan name
151142
expect(result).toBe('hello_world_wf');
152-
// Should NOT return the launch plan name
153143
expect(result).not.toBe('hello_world_lp');
154144
});
155145

@@ -211,12 +201,14 @@ describe('executionContext breadcrumb functions', () => {
211201
pathname: '/projects/flytesnacks/domains/development/executions/wf-execution-001',
212202
} as Location;
213203

214-
const result = await executonTaskWorkFlowNameAsyncSelfLink(mockLocation, mockBreadcrumb as any);
204+
const result = await executonTaskWorkFlowNameAsyncSelfLink(
205+
mockLocation,
206+
mockBreadcrumb as any,
207+
);
215208

216-
// Should link to the workflow details page with the WORKFLOW name
217-
expect(result).toContain('hello_world_wf');
218-
expect(result).toContain('/workflows/');
219-
// Should NOT contain the launch plan name
209+
expect(result).toBe(
210+
'/projects/workflow-project/domains/workflow-domain/workflows/hello_world_wf',
211+
);
220212
expect(result).not.toContain('hello_world_lp');
221213
});
222214

@@ -238,44 +230,4 @@ describe('executionContext breadcrumb functions', () => {
238230
expect(result).not.toContain('my-task-launch-plan');
239231
});
240232
});
241-
242-
describe('Bug scenario: Launch plan name differs from workflow name', () => {
243-
/**
244-
* This test verifies the fix for the bug where:
245-
* 1. User is on Launch Plan page showing 'hello_world_lp'
246-
* 2. User clicks into an execution
247-
* 3. Breadcrumb should show the WORKFLOW name 'hello_world_wf'
248-
* 4. NOT the launch plan name 'hello_world_lp'
249-
*
250-
* Before the fix, clicking the breadcrumb would navigate to:
251-
* /workflows/hello_world_lp (empty page - wrong!)
252-
*
253-
* After the fix, clicking the breadcrumb navigates to:
254-
* /workflows/hello_world_wf (correct workflow page)
255-
*/
256-
it('should use workflow name in breadcrumb, not launch plan name', async () => {
257-
const mockExecution = createMockExecutionWithDifferentNames();
258-
mockGetExecution.mockResolvedValue(mockExecution);
259-
260-
const mockLocation = {
261-
pathname: '/projects/flytesnacks/domains/development/executions/wf-execution-001',
262-
} as Location;
263-
264-
// Get the workflow/task name shown in breadcrumb
265-
const breadcrumbName = await executonTaskWorkFlowNameAsyncValue(mockLocation, mockBreadcrumb as any);
266-
267-
// Get the self-link URL when clicking the breadcrumb
268-
const selfLinkUrl = await executonTaskWorkFlowNameAsyncSelfLink(mockLocation, mockBreadcrumb as any);
269-
270-
// Verify the breadcrumb shows the correct workflow name
271-
expect(breadcrumbName).toBe('hello_world_wf');
272-
273-
// Verify clicking the breadcrumb navigates to the correct workflow page
274-
expect(selfLinkUrl).toBe('/projects/flytesnacks/domains/development/workflows/hello_world_wf');
275-
276-
// Verify we're NOT using the launch plan name
277-
expect(breadcrumbName).not.toBe('hello_world_lp');
278-
expect(selfLinkUrl).not.toContain('hello_world_lp');
279-
});
280-
});
281233
});

0 commit comments

Comments
 (0)