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-6602-2a152549ed68c726ad1ab44925b9bc04e7f38c04 in repository https://gitbox.apache.org/repos/asf/texera.git
commit dabc562888e1dfd16c4668089ebe2e42b0c1438d Author: Meng Wang <[email protected]> AuthorDate: Mon Jul 20 12:11:18 2026 -0700 test(frontend): extend OperatorMetadataService unit test coverage (#6602) ### What changes were proposed in this PR? Extends the existing Vitest spec for `OperatorMetadataService` (`frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.ts`, codecov ~56%). The service fetches operator metadata once over HTTP (cached via `shareReplay`) and serves lookups from it; the existing spec only lightly touched this. Adds 8 tests using `HttpClientTestingModule`, each flushing the metadata GET with the `mockOperatorMetaData` fixture. No production code changed. - **`getOperatorMetadata`** — emits the fetched metadata to subscribers. - **`getOperatorSchema`** — returns the matching `OperatorSchema` for a known type; throws for an unknown type; throws "operator metadata is undefined" before the request resolves. - **`operatorTypeExists`** — `true` for a fetched type, `false` for an unknown one and before the request resolves; matches the user-friendly name only when that filter is enabled; honors case-insensitive matching when requested. ### Any related issues, documentation, discussions? Closes #6592 ### How was this PR tested? Extended unit tests, run locally in `frontend/` (all green; the failure path was verified by breaking an assertion to confirm the suite goes red): ``` ng test --watch=false --include src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts # Tests 11 passed (11) (3 existing + 8 new) eslint <spec> # clean prettier --check # clean ``` ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context]) --- .../operator-metadata.service.spec.ts | 62 +++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts b/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts index 901c99bf6a..7b57e3aa2e 100644 --- a/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts +++ b/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts @@ -20,9 +20,11 @@ import { TestBed } from "@angular/core/testing"; import { HttpClient } from "@angular/common/http"; import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { OperatorMetadataService } from "./operator-metadata.service"; +import { OPERATOR_METADATA_ENDPOINT, OperatorMetadataService } from "./operator-metadata.service"; import { mockOperatorMetaData } from "./mock-operator-metadata.data"; import { commonTestProviders } from "../../../common/testing/test-utils"; +import { AppSettings } from "../../../common/app-setting"; +import type { OperatorMetadata } from "../../types/operator-schema.interface"; describe("OperatorMetadataService", () => { let service: OperatorMetadataService; @@ -57,4 +59,62 @@ describe("OperatorMetadataService", () => { const req = httpTestingController.match(request => request.method === "GET"); req[0].flush(mockOperatorMetaData); }); + + const metadataUrl = `${AppSettings.getApiEndpoint()}/${OPERATOR_METADATA_ENDPOINT}`; + + // The service fetches metadata once in its constructor (shareReplay), so a single + // request is pending after injection; flush it with the fixture. + const flushMetadata = () => + httpTestingController.expectOne(req => req.method === "GET" && req.url === metadataUrl).flush(mockOperatorMetaData); + + it("getOperatorMetadata emits the fetched metadata to subscribers", () => { + let emitted: OperatorMetadata | undefined; + service.getOperatorMetadata().subscribe(m => (emitted = m)); + flushMetadata(); + expect(emitted).toEqual(mockOperatorMetaData); + }); + + it("getOperatorSchema returns the schema for a known operator type", () => { + flushMetadata(); + const schema = service.getOperatorSchema("ScanSource"); + expect(schema.operatorType).toBe("ScanSource"); + expect(schema).toEqual(mockOperatorMetaData.operators.find(op => op.operatorType === "ScanSource")); + }); + + it("getOperatorSchema throws for an unknown operator type", () => { + flushMetadata(); + expect(() => service.getOperatorSchema("NoSuchOperator")).toThrow( + "can't find operator schema of type NoSuchOperator" + ); + }); + + it("getOperatorSchema throws when the metadata has not been fetched yet", () => { + // do not flush: the constructor's request is still pending, so metadata is undefined + expect(() => service.getOperatorSchema("ScanSource")).toThrow("operator metadata is undefined"); + flushMetadata(); // drain the pending request + }); + + it("operatorTypeExists is true for a fetched type and false for an unknown one", () => { + flushMetadata(); + expect(service.operatorTypeExists("ScanSource")).toBe(true); + expect(service.operatorTypeExists("NoSuchOperator")).toBe(false); + }); + + it("operatorTypeExists is false before the metadata request resolves", () => { + expect(service.operatorTypeExists("ScanSource")).toBe(false); + flushMetadata(); + }); + + it("operatorTypeExists matches the user-friendly name only when that filter is enabled", () => { + flushMetadata(); + // ScanSource's userFriendlyName is "Source: Scan" + expect(service.operatorTypeExists("Source: Scan", true)).toBe(true); + expect(service.operatorTypeExists("Source: Scan", false)).toBe(false); + }); + + it("operatorTypeExists honors case-insensitive matching when requested", () => { + flushMetadata(); + expect(service.operatorTypeExists("scansource", false, true)).toBe(true); + expect(service.operatorTypeExists("scansource", false, false)).toBe(false); + }); });
