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


The following commit(s) were added to refs/heads/main by this push:
     new 83e6ef3ecd test(frontend): cover the missing branches in the dataset 
modal, UDF debug, hub detail and dataset page (#7688)
83e6ef3ecd is described below

commit 83e6ef3ecdcc5c6affefb4f1dfc5a17f6d24b5e8
Author: Meng Wang <[email protected]>
AuthorDate: Sat Aug 15 05:00:52 2026 +0000

    test(frontend): cover the missing branches in the dataset modal, UDF debug, 
hub detail and dataset page (#7688)
    
    ### What changes were proposed in this PR?
    
    Takes the missing side of each conditional in the four files, so their
    partial
    branches clear. No production code was changed.
    
    **`DatasetSelectionModalComponent`** (+4) — no dataset selected, so no
    version
    list is fetched; a dataset selected in non-file mode, where the versions
    load but
    none is auto-selected; a dataset with no version, so the file tree is
    not
    fetched; and a file click in non-file mode, which leaves the path alone.
    
    **`UdfDebugService`** (+12) — a condition set on a line that has no
    breakpoint;
    clearing a breakpoint that lost its id; console events from another
    operator, with
    no messages, and not from the debugger; a status update that is not
    `Uninitialized`; stepping and deletion messages that carry no line
    number;
    a deletion for a line with no debug state; creation messages missing the
    id, and
    missing both id and line; a stepping message on an existing breakpoint;
    and the
    two `markContinue` combinations the existing test did not reach.
    
    **`HubWorkflowDetailComponent`** (+8) — a workflow with a description
    and one
    with an empty description, asserting the placeholder reaches the
    description
    child; `postUnlike` reporting failure; the refreshed counts carrying no
    `like`
    on both the like and the unlike path; and the two `wid` re-checks inside
    the
    like/unlike handlers. The two like/unlike responses are asynchronous in
    production, so a `Subject` stands in for the pending request and the id
    is
    cleared between issuing the call and the response arriving — `of(...)`
    resolves
    too early to reach those `return`s.
    
    **`UserDatasetComponent`** (+9) — both view-child accessors read before
    the view
    is initialized, so each `throw` runs, and read after assignment; the
    filter
    component reporting a change; and the search de-duplication guard driven
    one
    condition at a time: unchanged, forced, a changed sort method, a filter
    added,
    and a filter replaced by another (same length, different contents, which
    only the
    element-wise comparison distinguishes).
    
    Two spots named by the issue are not reachable and are left uncovered:
    
    - `UserDatasetComponent`'s `if (!this.searchResultsComponent) throw new
    Error("searchResultsComponent is undefined.")` is dead. The accessor
    above it
    either returns a truthy component or throws, so the negation is never
    true —
    the "before it is initialized" error is what actually fires, and that is
    the
      one the tests assert.
    - `hub-workflow-detail.component.ts:65` is the class declaration line,
    which the
    source map attributes TypeScript's emitted decorator helper to. It was
    already
    uncovered before this change (baseline unhit lines were `65,239,257`;
    239 and
      257 are the two `return`s this PR covers).
    
    ### Any related issues, documentation, discussions?
    
    Closes #7686
    
    ### How was this PR tested?
    
    Extended unit tests, run locally in `frontend/` (all green; the failure
    paths
    were verified by breaking assertions in each file and confirming the
    suites go
    red and exit non-zero):
    
    ```
    ng test --watch=false --include 
.../dataset-selection-modal.component.spec.ts  # 11 passed
    ng test --watch=false --include .../udf-debug.service.spec.ts               
   # 33 passed
    ng test --watch=false --include .../hub-workflow-detail.component.spec.ts   
   # 42 passed
    ng test --watch=false --include .../user-dataset.component.spec.ts          
   # 35 passed
    prettier --write <specs>   # clean
    eslint  <specs>            # clean
    ```
    
    The coverage report was re-run over the four specs to confirm the
    partials
    cleared: `dataset-selection-modal.component.ts` and
    `udf-debug.service.ts` reach
    100% of statements with no partial or never-taken branch left;
    `hub-workflow-detail.component.ts` and `user-dataset.component.ts` are
    left only
    with the two unreachable spots described above.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
---
 .../user-dataset/user-dataset.component.spec.ts    |  94 ++++++++++++
 .../detail/hub-workflow-detail.component.spec.ts   |  90 ++++++++++-
 .../dataset-selection-modal.component.spec.ts      |  44 ++++++
 .../operator-debug/udf-debug.service.spec.ts       | 169 ++++++++++++++++++++-
 4 files changed, 395 insertions(+), 2 deletions(-)

diff --git 
a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
index 1ccd5918fb..698cb1f8f9 100644
--- 
a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
@@ -381,6 +381,100 @@ describe("UserDatasetComponent", () => {
       expect(makeFreshComponent().viewType).toBe("card");
     });
   });
+
+  describe("view child accessors", () => {
+    // The outer beforeEach assigns both view children, so these build a 
component that
+    // has never had a view attached.
+    const componentWithNoView = () =>
+      new UserDatasetComponent(
+        modalServiceMock as any,
+        {
+          userChanged: () => new Subject<User | undefined>().asObservable(),
+          isLogin: () => true,
+          getCurrentUser: () => ({ uid: 42 }) as User,
+        } as any,
+        routerMock as any,
+        searchServiceMock as any,
+        datasetServiceMock as any,
+        messageMock as any
+      );
+
+    it("rejects reading searchResultsComponent before the view is 
initialized", () => {
+      expect(() => componentWithNoView().searchResultsComponent).toThrowError(
+        "Property cannot be accessed before it is initialized."
+      );
+    });
+
+    it("rejects reading filters before the view is initialized", () => {
+      expect(() => componentWithNoView().filters).toThrowError("Property 
cannot be accessed before it is initialized.");
+    });
+
+    it("returns the view children once they are assigned", () => {
+      expect(component.searchResultsComponent).toBe(searchResultsStub);
+      expect(component.filters).toBe(filtersStub);
+    });
+
+    it("re-runs the search when the filter component reports a change", () => {
+      const search = vi.spyOn(component, 
"search").mockResolvedValue(undefined);
+
+      // the setter is what wires this subscription up
+      filtersStub.masterFilterListChange.next();
+
+      expect(search).toHaveBeenCalled();
+    });
+  });
+
+  describe("search de-duplication", () => {
+    it("skips a repeat search when the filters and sort are unchanged", async 
() => {
+      filtersStub.masterFilterList = ["a"];
+      await component.search();
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(1);
+
+      await component.search();
+
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(1);
+    });
+
+    it("runs the search again when it is forced", async () => {
+      filtersStub.masterFilterList = ["a"];
+      await component.search();
+
+      await component.search(true);
+
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(2);
+    });
+
+    it("runs the search again when the sort method changed", async () => {
+      filtersStub.masterFilterList = ["a"];
+      await component.search();
+
+      component.sortMethod = SortMethod.NameAsc;
+      await component.search();
+
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(2);
+    });
+
+    it("runs the search again when a filter was added", async () => {
+      await component.search();
+
+      filtersStub.masterFilterList = ["a"];
+      await component.search();
+
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(2);
+    });
+
+    it("runs the search again when a filter was replaced by another", async () 
=> {
+      filtersStub.masterFilterList = ["a"];
+      await component.search();
+
+      // same length, different contents: only the element-wise comparison can 
tell
+      // these two lists apart
+      filtersStub.masterFilterList = ["b"];
+      await component.search();
+
+      expect(searchResultsStub.loadMore).toHaveBeenCalledTimes(2);
+    });
+  });
 });
 /**
  * The existing suite constructs the component directly, so its template has 
never been rendered.
diff --git 
a/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.spec.ts
 
b/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.spec.ts
index 2e45eaa8e0..31c3c8e0a4 100644
--- 
a/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.spec.ts
+++ 
b/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.spec.ts
@@ -23,7 +23,8 @@ import { ActivatedRoute, Router } from "@angular/router";
 import { NzIconModule } from "ng-zorro-antd/icon";
 import { NZ_MODAL_DATA } from "ng-zorro-antd/modal";
 import { ArrowLeftOutline, EyeOutline, LikeOutline, UserOutline } from 
"@ant-design/icons-angular/icons";
-import { config, of, throwError } from "rxjs";
+import { By } from "@angular/platform-browser";
+import { config, of, Subject, throwError } from "rxjs";
 import { vi } from "vitest";
 
 import { HubWorkflowDetailComponent, THROTTLE_TIME_MS } from 
"./hub-workflow-detail.component";
@@ -280,6 +281,24 @@ describe("HubWorkflowDetailComponent", () => {
       build({ modalData: { wid: 1 }, userOverride: undefined });
       expect(hubServiceMock.isLiked).not.toHaveBeenCalled();
     });
+
+    it("assigns the fetched description and passes it to the description 
child", () => {
+      workflowPersistServiceMock.getWorkflowDescription.mockReturnValue(of("a 
real description"));
+      build({ modalData: { wid: 1 } });
+      expect(component.workflowDescription).toBe("a real description");
+      expect(
+        
fixture.debugElement.query(By.directive(StubMarkdownDescriptionComponent)).componentInstance.description
+      ).toBe("a real description");
+    });
+
+    it("substitutes a placeholder when the workflow has no description", () => 
{
+      
workflowPersistServiceMock.getWorkflowDescription.mockReturnValue(of(""));
+      build({ modalData: { wid: 1 } });
+      expect(component.workflowDescription).toBe("No description available");
+      expect(
+        
fixture.debugElement.query(By.directive(StubMarkdownDescriptionComponent)).componentInstance.description
+      ).toBe("No description available");
+    });
   });
 
   describe("ngAfterViewInit / loadWorkflowWithId", () => {
@@ -421,6 +440,75 @@ describe("HubWorkflowDetailComponent", () => {
       expect(component.isLiked).toBe(false);
       expect(hubServiceMock.getCounts).not.toHaveBeenCalled();
     });
+
+    it("does not flip isLiked when postUnlike returns false", () => {
+      hubServiceMock.postUnlike.mockReturnValue(of(false));
+      build({ modalData: { wid: 1 } });
+      component.isLiked = true;
+      hubServiceMock.getCounts.mockClear();
+      component.toggleLike();
+      expect(component.isLiked).toBe(true);
+      expect(hubServiceMock.getCounts).not.toHaveBeenCalled();
+    });
+
+    it("defaults likeCount to 0 when the counts refreshed after a like carry 
none", () => {
+      hubServiceMock.getCounts
+        .mockReturnValueOnce(of([{ entityId: 1, entityType: 
EntityType.Workflow, counts: { like: 4, clone: 0 } }]))
+        .mockReturnValueOnce(of([{ entityId: 1, entityType: 
EntityType.Workflow, counts: {} }]));
+      build({ modalData: { wid: 1 } });
+      expect(component.likeCount).toBe(4);
+
+      component.isLiked = false;
+      component.toggleLike();
+
+      expect(component.likeCount).toBe(0);
+    });
+
+    it("defaults likeCount to 0 when the counts refreshed after an unlike 
carry none", () => {
+      hubServiceMock.getCounts
+        .mockReturnValueOnce(of([{ entityId: 1, entityType: 
EntityType.Workflow, counts: { like: 4, clone: 0 } }]))
+        .mockReturnValueOnce(of([{ entityId: 1, entityType: 
EntityType.Workflow, counts: {} }]));
+      build({ modalData: { wid: 1 } });
+      expect(component.likeCount).toBe(4);
+
+      component.isLiked = true;
+      component.toggleLike();
+
+      expect(component.likeCount).toBe(0);
+    });
+
+    // The like/unlike responses are asynchronous in production, so `wid` is 
re-checked
+    // inside each handler. A subject stands in for the pending request so the 
id can be
+    // cleared between issuing the call and the response arriving.
+    it("skips the like refresh when wid disappears before the response", () => 
{
+      const pending = new Subject<boolean>();
+      hubServiceMock.postLike.mockReturnValue(pending);
+      build({ modalData: { wid: 1 } });
+      component.isLiked = false;
+      component.toggleLike();
+      hubServiceMock.getCounts.mockClear();
+
+      component.wid = undefined;
+      pending.next(true);
+
+      expect(component.isLiked).toBe(true);
+      expect(hubServiceMock.getCounts).not.toHaveBeenCalled();
+    });
+
+    it("skips the unlike refresh when wid disappears before the response", () 
=> {
+      const pending = new Subject<boolean>();
+      hubServiceMock.postUnlike.mockReturnValue(pending);
+      build({ modalData: { wid: 1 } });
+      component.isLiked = true;
+      component.toggleLike();
+      hubServiceMock.getCounts.mockClear();
+
+      component.wid = undefined;
+      pending.next(true);
+
+      expect(component.isLiked).toBe(false);
+      expect(hubServiceMock.getCounts).not.toHaveBeenCalled();
+    });
   });
 
   describe("formatCount", () => {
diff --git 
a/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.spec.ts
 
b/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.spec.ts
index ddd595a10f..1eb0d1e646 100644
--- 
a/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.spec.ts
@@ -171,4 +171,48 @@ describe("DatasetSelectionModalComponent", () => {
 
     expect(modalRef.close).toHaveBeenCalledWith("/some/path");
   });
+
+  // Both handlers guard on a dataset (and a version) having been chosen, and 
behave
+  // differently in the two modes; the cases above only drive the file-mode 
arms.
+  it("onDatasetChange does nothing while no dataset is selected", () => {
+    build();
+    datasetService.retrieveDatasetVersionList.mockClear();
+    component.selectedDataset = undefined;
+
+    component.onDatasetChange();
+
+    expect(datasetService.retrieveDatasetVersionList).not.toHaveBeenCalled();
+    expect(component.fileTree).toEqual([]);
+  });
+
+  it("onDatasetChange loads the versions but auto-selects none in non-file 
mode", () => {
+    build(); // modalData.fileMode is false by default
+    component.selectedDataset = dataset;
+
+    component.onDatasetChange();
+
+    
expect(datasetService.retrieveDatasetVersionList).toHaveBeenCalledWith(dataset.dataset.did);
+    expect(component.datasetVersions).toEqual([version]);
+    expect(component.selectedVersion).toBeUndefined();
+  });
+
+  it("onVersionChange does nothing while no version is selected", () => {
+    build();
+    component.selectedDataset = dataset;
+    component.selectedVersion = undefined;
+    datasetService.retrieveDatasetVersionFileTree.mockClear();
+
+    component.onVersionChange();
+
+    
expect(datasetService.retrieveDatasetVersionFileTree).not.toHaveBeenCalled();
+  });
+
+  it("onFileSelected ignores the node in non-file mode", () => {
+    build(); // fileMode false
+    component.selectedPath = "/kept";
+
+    component.onFileSelected(fileNode);
+
+    expect(component.selectedPath).toBe("/kept");
+  });
 });
diff --git 
a/frontend/src/app/workspace/service/operator-debug/udf-debug.service.spec.ts 
b/frontend/src/app/workspace/service/operator-debug/udf-debug.service.spec.ts
index 3ea9184bdd..5f22bd3dbd 100644
--- 
a/frontend/src/app/workspace/service/operator-debug/udf-debug.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/operator-debug/udf-debug.service.spec.ts
@@ -30,7 +30,7 @@ import { mockPoint, mockPythonUDFPredicate } from 
"../workflow-graph/model/mock-
 import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.service";
 import { StubOperatorMetadataService } from 
"../operator-metadata/stub-operator-metadata.service";
 import * as Y from "yjs";
-import { ConsoleUpdateEvent } from "../../types/workflow-common.interface";
+import { ConsoleMessage, ConsoleUpdateEvent } from 
"../../types/workflow-common.interface";
 import { TexeraWebsocketEvent } from 
"../../types/workflow-websocket.interface";
 import { commonTestProviders } from "../../../common/testing/test-utils";
 import type { Mocked } from "vitest";
@@ -464,4 +464,171 @@ describe("UdfDebugServiceSpec", () => {
     expect(debugState.get("1")).toEqual({ breakpointId: 1, condition: "x > 5", 
hit: false });
     expect(debugState.has("2")).toBe(false);
   });
+
+  // Builds the (Pdb) DEBUGGER console event shape that every handler below 
filters on.
+  function pdbEvent(title: string, overrides: Partial<ConsoleMessage> = {}): 
ConsoleUpdateEvent {
+    return {
+      operatorId: mockPythonUDFPredicate.operatorID,
+      messages: [
+        {
+          workerId: stubWorker,
+          timestamp: { nanos: 0, seconds: 0 },
+          title,
+          source: "(Pdb)",
+          msgType: { name: "DEBUGGER" },
+          message: "",
+          ...overrides,
+        },
+      ],
+    };
+  }
+
+  it("should not send a condition for a line that has no breakpoint", () => {
+    // The condition differs from the empty default, so the early return is 
passed and
+    // the `isDefined(breakpointInfo)` guard is the one that stops the update.
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+
+    service.doUpdateBreakpointCondition(mockPythonUDFPredicate.operatorID, 7, 
"x < 10");
+
+    expect(mockWorkflowWebsocketService.send).not.toHaveBeenCalled();
+    expect(debugState.has("7")).toBe(false);
+  });
+
+  it("should clear a breakpoint that lost its id with an empty id", () => {
+    // State a hit breakpoint is left in once pdb deleted it: still present, 
so the
+    // command is `clear`, but with no id to clear by. The trailing space is 
pinned
+    // deliberately — it is the literal payload that goes over the websocket.
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    debugState.set("10", { breakpointId: undefined, condition: "", hit: true 
});
+
+    service.doModifyBreakpoint(mockPythonUDFPredicate.operatorID, 10);
+
+    
expect(mockWorkflowWebsocketService.send).toHaveBeenCalledWith("DebugCommandRequest",
 {
+      operatorId: mockPythonUDFPredicate.operatorID,
+      workerId: stubWorker,
+      cmd: "clear ",
+    });
+  });
+
+  it("should ignore console events from another operator or with no messages", 
() => {
+    vi.spyOn(service as any, "markBreakpointAsHit");
+
+    consoleUpdateEventStream.next({
+      ...pdbEvent("> /path/to/file.py(10)<module>()"),
+      operatorId: "some-other-operator",
+    });
+    consoleUpdateEventStream.next({ operatorId: 
mockPythonUDFPredicate.operatorID, messages: [] });
+
+    expect(service["markBreakpointAsHit"]).not.toHaveBeenCalled();
+  });
+
+  it("should ignore console messages that are not from the debugger", () => {
+    vi.spyOn(service as any, "markBreakpointAsHit");
+
+    consoleUpdateEventStream.next(pdbEvent("> /path/to/file.py(10)<module>()", 
{ source: "stdout" }));
+    consoleUpdateEventStream.next(pdbEvent("> /path/to/file.py(10)<module>()", 
{ msgType: { name: "PRINT" } }));
+
+    expect(service["markBreakpointAsHit"]).not.toHaveBeenCalled();
+  });
+
+  it("should keep the debug state on a status update that is not 
Uninitialized", () => {
+    const operatorId = mockPythonUDFPredicate.operatorID;
+    const debugState = service.getDebugState(operatorId);
+    debugState.set("10", { breakpointId: 1, condition: "", hit: false });
+
+    const running: OperatorStatistics = {
+      operatorState: OperatorState.Running,
+      aggregatedInputRowCount: 0,
+      aggregatedOutputRowCount: 0,
+      inputPortMetrics: {},
+      outputPortMetrics: {},
+    };
+    statusUpdateStream.next({ [operatorId]: running });
+    statusUpdateStream.next({ "some-other-operator": { ...running, 
operatorState: OperatorState.Uninitialized } });
+
+    expect(debugState.size).toBe(1);
+  });
+
+  it("should ignore a stepping message that carries no line number", () => {
+    vi.spyOn(service as any, "markBreakpointAsHit");
+    // spied so the assertions below cannot be satisfied by the message never
+    // reaching the handler at all
+    vi.spyOn(service as any, "extractInfo");
+
+    consoleUpdateEventStream.next(pdbEvent("> <stdin> in the interactive 
shell"));
+
+    expect(service["extractInfo"]).toHaveBeenCalledWith("> <stdin> in the 
interactive shell");
+    expect(service["markBreakpointAsHit"]).not.toHaveBeenCalled();
+  });
+
+  it("should ignore a deletion message that carries no line number", () => {
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    debugState.set("10", { breakpointId: 1, condition: "", hit: false });
+    vi.spyOn(service, "doContinue");
+    vi.spyOn(service as any, "extractInfo");
+
+    consoleUpdateEventStream.next(pdbEvent("Deleted all breakpoints"));
+
+    expect(service["extractInfo"]).toHaveBeenCalledWith("Deleted all 
breakpoints");
+    expect(debugState.has("10")).toBe(true);
+    expect(service.doContinue).not.toHaveBeenCalled();
+  });
+
+  it("should ignore a deletion message for a line with no debug state", () => {
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    vi.spyOn(service, "doContinue");
+
+    consoleUpdateEventStream.next(pdbEvent("Deleted breakpoint 1 at 
/path/to/file.py:10"));
+
+    expect(debugState.size).toBe(0);
+    // the handler returns before reaching the continue check
+    expect(service.doContinue).not.toHaveBeenCalled();
+  });
+
+  it("should not record a breakpoint whose creation message has no id", () => {
+    // `Breakpoint <id> at <file>:<line>` is what carries an id; without one 
only the
+    // line is recovered, so the entry is skipped but the execution still 
continues.
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    vi.spyOn(service, "doContinue");
+
+    consoleUpdateEventStream.next(pdbEvent("Breakpoint at 
/path/to/file.py:10"));
+
+    expect(debugState.has("10")).toBe(false);
+    
expect(service.doContinue).toHaveBeenCalledWith(mockPythonUDFPredicate.operatorID,
 stubWorker);
+  });
+
+  it("should not record a breakpoint whose creation message has neither id nor 
line", () => {
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    vi.spyOn(service, "doContinue");
+
+    consoleUpdateEventStream.next(pdbEvent("Breakpoint already exists"));
+
+    expect(debugState.size).toBe(0);
+    
expect(service.doContinue).toHaveBeenCalledWith(mockPythonUDFPredicate.operatorID,
 stubWorker);
+  });
+
+  it("should mark an existing breakpoint as hit without discarding its 
condition", () => {
+    // The companion of the existing markBreakpointAsHit test, which starts 
from an
+    // empty state and therefore only creates the placeholder entry.
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    debugState.set("10", { breakpointId: 3, condition: "x > 5", hit: false });
+
+    consoleUpdateEventStream.next(pdbEvent("> 
/path/to/file.py(10)<module>()"));
+
+    expect(debugState.get("10")).toEqual({ breakpointId: 3, condition: "x > 
5", hit: true });
+  });
+
+  it("should keep a hit breakpoint that still has an id when continuing", () 
=> {
+    // The two combinations the existing markContinue test does not reach: a 
hit
+    // breakpoint with an id is reset rather than dropped, and an idle 
temporary one
+    // is dropped even though it was never hit.
+    const debugState = 
service.getDebugState(mockPythonUDFPredicate.operatorID);
+    debugState.set("1", { breakpointId: 1, condition: "x > 5", hit: true });
+    debugState.set("2", { breakpointId: undefined, condition: "", hit: false 
});
+
+    service["markContinue"](mockPythonUDFPredicate.operatorID);
+
+    expect(debugState.get("1")).toEqual({ breakpointId: 1, condition: "x > 5", 
hit: false });
+    expect(debugState.has("2")).toBe(false);
+  });
 });

Reply via email to