Skip to content

test: restore global state and scoped spies - #778

Merged
owjs3901 merged 1 commit into
mainfrom
fix/test-global-restore
Oct 14, 2026
Merged

owjs3901 merged 1 commit into
mainfrom
fix/test-global-restore

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Refs #683 (전수조사 #682의 L: TypeScript 테스트 전역 상태 복구). 최신 origin/main의 11790bef에서 시작한 독립 PR입니다.

요약

플러그인 테스트가 describe 등록 중 console.error = mock()을 실행하여 같은 Bun 프로세스의 이후 테스트까지 오류 출력을 침묵시키는 누수를 수정합니다. 테스트 파일 12개만 변경하며 프로덕션 코드·Rust·의존성·설정은 변경하지 않습니다.

문제와 복구 규칙

  • 전역/모듈 메서드를 교체한 테스트가 직접 그 교체를 복구합니다. mockClear()나 다른 파일의 mock.restore()에 의존하지 않습니다.
  • Vite/Webpack의 상시 console.error 교체는 기존 beforeEach에서 spy를 설치하고 afterEach에서 해당 spy를 복구합니다.
  • 실패 경로 25곳의 지역 spy는 사용한 테스트 안의 try/finally에서 복구합니다. assertion 실패, await 거절, 두 번째 spy 설치 실패 시에도 먼저 설치한 spy가 남지 않습니다.
  • window는 값뿐 아니라 원래 property descriptor/존재 여부를 매 테스트 복구합니다. 두 matchMedia fixture도 해당 테스트 안에서 복구합니다.
  • getTheme/initTheme 테스트는 data-theme와 건드린 __DF_THEME_SELECTED__ 저장값을 원래 값 또는 부재 상태로 복구합니다.
  • Next profile/plugin 테스트의 process.env는 복사본으로 바꾸지 않습니다. 원래 객체를 보존하고 추가 키 삭제·삭제/변경된 값 복구 후 원래 identity를 되돌립니다.

전수 점검

packages와 apps의 TypeScript/TSX 테스트 80개(패키지 79개, 앱 1개), 실제 spyOn 호출 218곳을 읽기 전용으로 점검했습니다. 의도적인 suite-wide spy의 afterAll 복구, 지역 fixture mock, RuleTester 코드 문자열은 실제 전역 교체와 구분했습니다.

구분 파일 / 대상 조치
상시 누수 2곳 Vite/Webpack plugin.test.ts: console.error 기존 테스트 훅에서 설치/복구
실패 경로 15곳 Next coordinator의 Date/console 4곳, loader의 HTTP 8곳, plugin의 process.on 1곳, profile의 console 2곳 각 지역 spy의 finally 복구
실패 경로 10곳 Vite resolver 1곳, Webpack plugin/loader resolver 각 1곳, Rsbuild resolver/getCss/error 3곳, import-graph CLI console/process.exit 4곳 각 지역 spy의 finally 복구
전역 객체 / 브라우저 상태 React use-safe-effect / init-theme / get-theme, Next env 훅 2곳 정확한 원상 복구

기존 기대값·matcher·테스트 이름·케이스를 유지했습니다. TypeScript AST 기반 비교에서 기존 expect 관련 호출 1,534개가 그대로이며 삭제·skip·assertion 약화가 없습니다.

기존 PR과의 겹침

열린 PR 72개의 변경 파일을 페이지 끝까지 확인했습니다. #755/#767은 아래 파일을 바꾸지만 이 전역 복구를 수행하지 않아, 코디네이터 승인에 따라 복구 hunk만 추가했습니다. 나중에 병합하는 PR에서 해당 복구 hunk의 단순 충돌을 해결해야 합니다.

  • #755와 겹침: packages/next-plugin/src/__tests__/coordinator.test.ts, loader.test.ts, plugin.test.ts.
  • #755와 겹침: packages/plugin-utils/src/import-graph.test.ts, packages/rsbuild-plugin/src/__tests__/plugin.test.ts, packages/vite-plugin/src/__tests__/plugin.test.ts.
  • #755와 겹침: packages/webpack-plugin/src/__tests__/loader.test.ts, plugin.test.ts.
  • #767과 겹침: packages/next-plugin/src/__tests__/coordinator.test.ts.
  • #741은 init-theme.browser.test.ts에서 구독 수명을 정리하지만 이번 DOM/저장값/matchMedia 복구는 하지 않습니다. import/teardown 겹침만 조정하면 됩니다.

#755는 Rsbuild 오류 전파 동작을 바꾸며 해당 console 교체를 제거합니다. 이 PR은 그 동작 변경을 가져오지 않고 현재 테스트와 assertion을 보존해 복구만 보장합니다. #752의 Next 테스트 재구성은 별도 작업이며, 해당 재구성을 병합할 때 지역 복구 규칙을 유지해야 합니다.

실제 실행: 이전 / 이후

기존 Vite/Webpack 두 파일 뒤에 별도 observer 파일을 같은 bun test 프로세스로 실행했습니다. observer는 첫 테스트 전 preload에서 실제 console.error 참조를 저장하며 어떤 mock도 대신 복구하지 않습니다.

bun test --config="C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w57-test-globals/leak.toml" --preload="C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w57-test-globals/leak-preload.ts" ./packages/vite-plugin/src/__tests__/plugin.test.ts ./packages/webpack-plugin/src/__tests__/plugin.test.ts "C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w57-test-globals/zz-console-observer.test.ts"
이전: console.error === original: false; has mock: true
       4075 pass / 1 fail (후속 파일의 복구 assertion 실패)
이후: console.error === original: true; has mock: false
       4076 pass / 0 fail

env도 별도 후속 파일에서 재현했습니다: 이전에는 원래 객체 identity가 false(32 pass / 1 fail), 이후에는 identity·모든 키/값 복구가 모두 true(33 pass / 0 fail)입니다. 환경변수 값 전체를 로그에 출력하지 않습니다.

테마 테스트는 정방향/역방향 파일 순서와 --randomize --seed=1,7,42를 각각 기존 값이 없는 상태·서로 다른 기존 값이 있는 상태에서 실행했습니다. 10회 모두 7 pass / 0 fail이며 후속 파일에서 원래 attribute/저장값/matchMedia/window descriptor를 확인했습니다.

새로 생기는 오류

없습니다. 빌드 동작과 공개 API는 변경하지 않습니다.

남는 한계 / 릴리스

  • 테스트가 교체한 전역의 복구 수명만 변경합니다. 임의의 동시 테스트 실행이나 프로세스 강제 종료의 복구 보장은 추가하지 않습니다.
  • TypeScript LSP는 기존 설치 거절 상태라 사용하지 않았습니다. 별도 strict TypeScript 6 진단 비교는 기존 41건 → 41건, 새 진단 0건입니다.
  • Patch changepack을 추가하지 않습니다. 각 패키지는 dist만 배포하고 변경된 테스트 소스는 배포하지 않습니다. plugin-utils의 src 바로 아래 테스트가 만드는 빈 .d.ts/.d.ts.map도 이전·이후 동일함을 비교했습니다.

검증

  • 실제 같은 프로세스 console/env 후속 파일 검사와 테마 순서 변경 검사 통과.
  • 루트 bun run build(WASM 포함) 통과.
  • bun test: 5,498 pass, 0 fail, 89 snapshots, 함수/라인 커버리지 100%.
  • bun lint: 0 errors, 기존 경고 2개만 유지.
  • cargo fmt --all -- --check, Rust 1.99 workspace clippy -D warnings, cargo test --workspace 통과. Rust 파일 변경 없음.
  • 모든 빌드/테스트는 CARGO_BUILD_JOBS=2, 전체 자식 프로세스 합계 6 GiB watchdog에서 실행했습니다.
  • ad51616cf80d83f50a1a0d36ac3ea523df7052a1의 표준 pre-commit(lint + tarpaulin + Bun) 통과: CI unset, CARGO_BUILD_JOBS=2, 12 GiB watchdog. 짧게 사라지는 MSYS launcher로 인한 ancestry 계측 누락을 피하기 위해 같은 커밋의 동일 훅을 foreground로 다시 실행했고, 전체 자식 합계 관측 peak 5,128,802,304 bytes, exit 0을 확인했습니다. 커밋/소스는 변경하거나 amend하지 않았습니다.
  • Windows tarpaulin 수치(97.97%)는 최종 coverage 게이트로 사용하지 않습니다. 요청한 Linux CI의 Rust/Bun 100% 결과를 확인한 뒤 ready로 전환하며 병합하지 않습니다.
  • 독립 코드 게이트 리뷰 APPROVE, confidence HIGH.
  • Linux CI 38378115558, 정확한 head ad51616cf80d83f50a1a0d36ac3ea523df7052a1: success. publish, benchmark, vinext-rsc-css-e2e, landing-next-e2e 네 작업 모두 성공했습니다.
  • Linux publish 실제 로그: Rust 100.00% (9,890/9,890 lines), Bun 5,498 pass / 0 fail, 89 snapshots, 함수/라인 커버리지 100.00% / 100.00%. --fail-under 100 게이트 통과를 코디네이터가 확인했습니다.
  • CI 확인 후 gh pr ready 778 완료. isDraft: false, mergeStateStatus: CLEAN, mergeable: MERGEABLE 확인. 병합·승인·닫기는 하지 않았습니다.

Refs #683

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/vite-plugin' via a local workspace dependency

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant