This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/texera.git
commit aceca29cbda1f223ac17753efb58eba726e58b2f Author: Benjamin Le <[email protected]> AuthorDate: Sat Jun 13 11:54:04 2026 -0700 test(frontend): expand OperatorReuseCacheStatusService spec coverage (#5624) ### What changes were proposed in this PR? Expands the existing spec for `OperatorReuseCacheStatusService`, which previously only checked `should be created`. Adds behavior-focused tests covering both constructor subscriptions: the `CacheStatusUpdateEvent` websocket handler and the `getReuseCacheOperatorsChangedStream` handler. Tests verify that `JointUIService.changeOperatorReuseCacheStatus` is called correctly for each operator, that the `mainJointPaper` null guard is respected in both handlers, and that empty operator lists do not throw. ### Any related issues, documentation, discussions? Closes #5543 ### How was this PR tested? New tests run via `yarn test -- operator-reuse-cache-status` and `yarn lint`. 6 tests passing. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude (Claude Sonnet 4.6) Co-authored-by: Benjamin Le <[email protected]> --- .../operator-reuse-cache-status.service.spec.ts | 120 ++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts b/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts index 0c3ccb89fd..bbfd9c55d8 100644 --- a/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts +++ b/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts @@ -18,10 +18,13 @@ */ import { TestBed } from "@angular/core/testing"; +import { Subject } from "rxjs"; import { OperatorMetadataService } from "../operator-metadata/operator-metadata.service"; import { StubOperatorMetadataService } from "../operator-metadata/stub-operator-metadata.service"; - import { OperatorReuseCacheStatusService } from "./operator-reuse-cache-status.service"; +import { JointUIService } from "../joint-ui/joint-ui.service"; +import { WorkflowActionService } from "../workflow-graph/model/workflow-action.service"; +import { WorkflowWebsocketService } from "../workflow-websocket/workflow-websocket.service"; import { HttpClientTestingModule } from "@angular/common/http/testing"; import { commonTestProviders } from "../../../common/testing/test-utils"; @@ -46,3 +49,118 @@ describe("OperatorCacheStatusService", () => { expect(service).toBeTruthy(); }); }); + +describe("OperatorCacheStatusService - behavior", () => { + let service: OperatorReuseCacheStatusService; + let cacheStatusEvents$: Subject<any>; + let reuseCacheOpsChanged$: Subject<any>; + let mockJointUIService: any; + let mockJointGraphWrapper: any; + let mockTexeraGraph: any; + let mockWorkflowActionService: any; + let mockWorkflowWebsocketService: any; + const mockMainJointPaper = {}; + + beforeEach(() => { + cacheStatusEvents$ = new Subject<any>(); + reuseCacheOpsChanged$ = new Subject<any>(); + + mockJointUIService = { + changeOperatorReuseCacheStatus: vi.fn(), + }; + + mockJointGraphWrapper = { + getMainJointPaper: vi.fn().mockReturnValue(mockMainJointPaper), + }; + + mockTexeraGraph = { + getReuseCacheOperatorsChangedStream: vi.fn().mockReturnValue(reuseCacheOpsChanged$), + getOperator: vi.fn().mockImplementation((opID: string) => ({ operatorID: opID })), + }; + + mockWorkflowActionService = { + getTexeraGraph: vi.fn().mockReturnValue(mockTexeraGraph), + getJointGraphWrapper: vi.fn().mockReturnValue(mockJointGraphWrapper), + }; + + mockWorkflowWebsocketService = { + subscribeToEvent: vi.fn().mockReturnValue(cacheStatusEvents$), + }; + + TestBed.configureTestingModule({ + providers: [ + OperatorReuseCacheStatusService, + { provide: JointUIService, useValue: mockJointUIService }, + { provide: WorkflowActionService, useValue: mockWorkflowActionService }, + { provide: WorkflowWebsocketService, useValue: mockWorkflowWebsocketService }, + ], + }); + + service = TestBed.inject(OperatorReuseCacheStatusService); + }); + + it("calls changeOperatorReuseCacheStatus for each operator in a CacheStatusUpdateEvent", () => { + const event = { + cacheStatusMap: { + op1: "cache", + op2: "no-cache", + }, + }; + + cacheStatusEvents$.next(event); + + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledTimes(2); + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledWith( + mockMainJointPaper, + { operatorID: "op1" }, + "cache" + ); + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledWith( + mockMainJointPaper, + { operatorID: "op2" }, + "no-cache" + ); + }); + + it("does not call changeOperatorReuseCacheStatus when mainJointPaper is null on CacheStatusUpdateEvent", () => { + mockJointGraphWrapper.getMainJointPaper.mockReturnValue(null); + + cacheStatusEvents$.next({ cacheStatusMap: { op1: "cache" } }); + + expect(mockJointUIService.changeOperatorReuseCacheStatus).not.toHaveBeenCalled(); + }); + + it("calls changeOperatorReuseCacheStatus for all ops in a reuse cache operators changed event", () => { + const event = { + newReuseCacheOps: ["op1", "op2"], + newUnreuseCacheOps: ["op3"], + }; + + reuseCacheOpsChanged$.next(event); + + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledTimes(3); + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledWith(mockMainJointPaper, { + operatorID: "op1", + }); + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledWith(mockMainJointPaper, { + operatorID: "op2", + }); + expect(mockJointUIService.changeOperatorReuseCacheStatus).toHaveBeenCalledWith(mockMainJointPaper, { + operatorID: "op3", + }); + }); + + it("does not call changeOperatorReuseCacheStatus when mainJointPaper is null on reuse cache changed event", () => { + mockJointGraphWrapper.getMainJointPaper.mockReturnValue(null); + + reuseCacheOpsChanged$.next({ newReuseCacheOps: ["op1"], newUnreuseCacheOps: [] }); + + expect(mockJointUIService.changeOperatorReuseCacheStatus).not.toHaveBeenCalled(); + }); + + it("does not throw when reuse cache changed event has empty operator lists", () => { + expect(() => { + reuseCacheOpsChanged$.next({ newReuseCacheOps: [], newUnreuseCacheOps: [] }); + }).not.toThrow(); + }); +});
