This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-6689-9ca9c57eb8b7bf4b11c23269102ca737ce6f0181 in repository https://gitbox.apache.org/repos/asf/texera.git
commit a351f44fbf08920d7d9dfe806614ffc4c8ed5a83 Author: Matthew B. <[email protected]> AuthorDate: Fri Jul 24 00:20:06 2026 -0700 test(frontend): cover WorkflowUtilService untested helpers (#6689) ### What changes were proposed in this PR? - Add unit tests for updateOperatorVersion covering port rebuild, port preservation, and the unknown-type error. - Add unit tests for the static parseWorkflowInfo string parsing and object passthrough, and for getNewCommentBox default position and unique ids. ### Any related issues, documentation, discussions? Closes: #6688 ### How was this PR tested? - Run: `cd frontend && node --max-old-space-size=8192 ./node_modules/nx/dist/bin/nx.js test gui --watch=false --include=src/app/workspace/service/workflow-graph/util/workflow-util.service.spec.ts`, expect the suite passing. - Test-only change; no production code is modified. ### Was this PR authored or co-authored using generative AI tooling? Co-authored with Claude Opus 4.8 in compliance with ASF --------- Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: Xinyuan Lin <[email protected]> --- .../util/workflow-util.service.spec.ts | 128 ++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/workspace/service/workflow-graph/util/workflow-util.service.spec.ts b/frontend/src/app/workspace/service/workflow-graph/util/workflow-util.service.spec.ts index 41b47d49b4..adb4353525 100644 --- a/frontend/src/app/workspace/service/workflow-graph/util/workflow-util.service.spec.ts +++ b/frontend/src/app/workspace/service/workflow-graph/util/workflow-util.service.spec.ts @@ -22,8 +22,10 @@ import { OperatorMetadataService } from "./../../operator-metadata/operator-meta import { inject, TestBed } from "@angular/core/testing"; import { WorkflowUtilService } from "./workflow-util.service"; -import { mockScanSourceSchema } from "../../operator-metadata/mock-operator-metadata.data"; +import { mockMultiInputOutputSchema, mockScanSourceSchema } from "../../operator-metadata/mock-operator-metadata.data"; import { commonTestProviders } from "../../../../common/testing/test-utils"; +import { OperatorPredicate } from "../../../types/workflow-common.interface"; +import { ExecutionMode, Workflow, WorkflowContent } from "../../../../common/type/workflow"; describe("WorkflowUtilService", () => { let workflowUtilService: WorkflowUtilService; @@ -101,4 +103,128 @@ describe("WorkflowUtilService", () => { // assert all IDs are distinct expect(idSet.size).toEqual(repeat); }); + + it("should rebuild ports and version from the schema when the operator has no ports", () => { + const op: OperatorPredicate = { + operatorID: "op-1", + operatorType: mockMultiInputOutputSchema.operatorType, + operatorVersion: "stale-version", + operatorProperties: {}, + inputPorts: [], + outputPorts: [], + showAdvanced: false, + isDisabled: false, + }; + + const updated = workflowUtilService.updateOperatorVersion(op); + + // version is refreshed from the schema + expect(updated.operatorVersion).toEqual(mockMultiInputOutputSchema.operatorVersion); + // ports are regenerated from the schema's port metadata + expect(updated.inputPorts.map(port => port.portID)).toEqual(["input-0", "input-1", "input-2"]); + expect(updated.outputPorts.map(port => port.portID)).toEqual(["output-0", "output-1", "output-2"]); + }); + + it("should keep the operator's existing ports when they are already present", () => { + const existingInputPorts = [{ portID: "input-existing", displayName: "keep-me" }]; + const existingOutputPorts = [{ portID: "output-existing", displayName: "keep-me-too" }]; + const op: OperatorPredicate = { + operatorID: "op-2", + operatorType: mockMultiInputOutputSchema.operatorType, + operatorVersion: "stale-version", + operatorProperties: {}, + inputPorts: existingInputPorts, + outputPorts: existingOutputPorts, + showAdvanced: false, + isDisabled: false, + }; + + const updated = workflowUtilService.updateOperatorVersion(op); + + // existing ports are preserved untouched, only the version is refreshed + expect(updated.inputPorts).toEqual(existingInputPorts); + expect(updated.outputPorts).toEqual(existingOutputPorts); + expect(updated.operatorVersion).toEqual(mockMultiInputOutputSchema.operatorVersion); + }); + + it("should throw an error when updating the version of an operator with an unknown type", () => { + const op: OperatorPredicate = { + operatorID: "op-3", + operatorType: "non-exist-operator-type", + operatorVersion: "v1", + operatorProperties: {}, + inputPorts: [], + outputPorts: [], + showAdvanced: false, + isDisabled: false, + }; + + expect(() => workflowUtilService.updateOperatorVersion(op)).toThrowError(new RegExp("doesn't exist")); + }); + + it("should parse the workflow content string into an object", () => { + const content: WorkflowContent = { + operators: [], + operatorPositions: {}, + links: [], + commentBoxes: [], + settings: { dataTransferBatchSize: 400, executionMode: ExecutionMode.PIPELINED }, + }; + const workflow = { + name: "test-workflow", + description: undefined, + wid: 1, + creationTime: undefined, + lastModifiedTime: undefined, + isPublished: 0, + readonly: false, + content: JSON.stringify(content) as any, + } as unknown as Workflow; + + const parsed = WorkflowUtilService.parseWorkflowInfo(workflow); + + // the string content is replaced with the parsed object + expect(typeof parsed.content).toEqual("object"); + expect(parsed.content).toEqual(content); + }); + + it("should leave workflow content untouched when it is already an object", () => { + const content: WorkflowContent = { + operators: [], + operatorPositions: {}, + links: [], + commentBoxes: [], + settings: { dataTransferBatchSize: 400, executionMode: ExecutionMode.PIPELINED }, + }; + const workflow = { + name: "test-workflow", + description: undefined, + wid: 1, + creationTime: undefined, + lastModifiedTime: undefined, + isPublished: 0, + readonly: false, + content, + } as unknown as Workflow; + + const parsed = WorkflowUtilService.parseWorkflowInfo(workflow); + + // an object content is returned as-is + expect(parsed.content).toBe(content); + }); + + it("should create a fresh comment box at the default position", () => { + const commentBox = workflowUtilService.getNewCommentBox(); + + expect(commentBox.commentBoxID).toMatch(/^commentBox-/); + expect(commentBox.comments).toEqual([]); + expect(commentBox.commentBoxPosition).toEqual({ x: 500, y: 20 }); + }); + + it("should generate a distinct comment box each time", () => { + const first = workflowUtilService.getNewCommentBox(); + const second = workflowUtilService.getNewCommentBox(); + + expect(first.commentBoxID).not.toEqual(second.commentBoxID); + }); });
