deepClone spec flakes on CI: expect() recurses over the 600-level fixture · Issue #5504 · marktext/marktext · GitHub
Skip to content

deepClone spec flakes on CI: expect() recurses over the 600-level fixture #5504

Closed

Description

@Jocs

Summary

packages/muya/src/utils/__tests__/deepClone.spec.ts > copies a 600-level nested list without exhausting the stack (#4747) fails intermittently on CI with RangeError: Maximum call stack size exceeded. It has now failed on two unrelated PRs (#5496, #5502) and passed on re-run with no code change, so it is roughly a coin flip on GitHub runners.

The code under test is fine

deepClone is iterative (the #5259 / 8b94f6f rewrite) and has no depth problem at all. Measured ceilings for the exact fixture shape the spec builds:

Operation Ceiling
deepClone(nested(d)) alone 100 000 levels, never threw
The spec's test body, verbatim ok ≤ 900, RangeError @ 1000
expect(a).not.toBe(b) on nested(d) ok ≤ 900, RangeError @ 1000
structuredClone (the #4747 control) ok ≤ 600, throws @ 700
JSON.stringify (control) ok ≤ 1000, throws @ 2000

Root cause

The recursion is in expect() itself, not in deepClone. These two lines hand the whole 600-level structure (~2400 nested containers) to Vitest, which recurses once per level while inspecting the argument:

expect(copy).not.toBe(state)
expect(copy[0]).not.toBe(state[0])

So the test's margin is 600 → ~900, under 1.5×. The ceiling moves with the Node build, the worker thread's stack size and how deep the caller's stack already is, so CI lands on the wrong side of it about half the time.

The spec's own comment shows the author knew to avoid this in the helpers — "structuredClone overflows the engine stack on this input (#4747), and so would a recursive equality check, hence the iterative walk below" — but expect(deepObject) is itself a recursive walk.

Fix

Compute identity outside the matcher so only booleans reach expect:

expect(copy === state).toBe(false)
expect(copy[0] === state[0]).toBe(false)

That removes the only per-level recursion left in the test, giving it the full headroom deepClone actually has (100 000+ levels) instead of 1.5×.

Reproduce

Not reliably reproducible on a dev machine at depth 600 — the local ceiling is ~900. To see it locally, raise the fixture depth to 1000, or shrink the worker stack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions