Skip to content

feat: provide deterministic test runtime controls - #1730

Merged
kang-heewon merged 9 commits into
trunkfrom
fix/1483-deterministic-test-runtime
Aug 5, 2026
Merged

feat: provide deterministic test runtime controls#1730
kang-heewon merged 9 commits into
trunkfrom
fix/1483-deterministic-test-runtime

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

Provide TestKernel-owned virtual time, seeded IDs/randomness, scoped environment snapshots, explicit provider egress denial, replay metadata, and structured leak diagnostics.

Retry backoff, task timeout, and in-memory rate-limit seams now accept those controls without patching global runtime state.

Fixes #1483

Verification

  • @croco/testing — 125 tests, typecheck, lint, and formatting
  • @croco/tasks-core — 69 tests and typecheck
  • @croco/ratelimit-core — 88 tests and typecheck
  • public API snapshot, Problem registry, API-doc trigger, and changeset checks
  • pre-push release gates

Summary by CodeRabbit

  • 새로운 기능

    • 가상 시간, 시드 기반 ID·난수, 환경 격리, 네트워크 차단을 지원하는 결정적 테스트 실행 제어를 추가했습니다.
    • 예약 작업, 미완료 작업, 리소스 누수를 자동 감지하고 재현 정보를 제공합니다.
    • 작업 타임아웃, 재시도 백오프, rate limit 동작이 주입된 시간과 난수를 사용할 수 있습니다.
    • 테스트 런타임 및 관련 문제 진단 API를 공개했습니다.
    • 잘못된 rate limit 정리 간격을 명확한 오류로 안내합니다.
  • 문서

    • 결정적 테스트 제어와 데이터베이스 제공자 검증 헬퍼 사용법을 추가했습니다.
    • 문제 코드 레지스트리에 새 진단 항목을 반영했습니다.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

TestRuntimeTestKernel에 결정적 시간, ID, 난수, 환경, 네트워크, replay 정보, 누수 진단을 추가했습니다. Rate-limit 저장소와 TaskRunner는 주입된 런타임을 사용합니다. 문제 문서와 공개 API를 갱신했습니다.

Changes

결정적 테스트 런타임

Layer / File(s) Summary
TestRuntime 기반 기능
packages/testing/src/libs/TestRuntime.ts, packages/testing/src/tests/TestRuntime.spec.ts, packages/testing/README.md, packages/testing/src/index.ts
가상 시계, 시드 기반 난수·ID, 환경 스냅샷, 네트워크 정책, replay 메타데이터, 재시도 의존성을 추가했습니다.
TestKernel 런타임 통합과 누수 진단
packages/testing/src/libs/TestKernel.ts, packages/testing/src/tests/TestKernel.spec.ts
TestKernel이 런타임 제어를 관리하고, 예약 작업·transaction hook·이벤트·span·리소스 누수를 TestKernelLeakProblem으로 보고합니다. Lambda 요청의 시간과 ID도 런타임 제어에서 생성합니다.
Rate limit과 작업 타임아웃 주입
packages/ratelimit-core/src/libs/*, packages/ratelimit-core/src/index.ts, packages/tasks-core/src/libs/TaskRunner.ts, packages/tasks-core/src/index.ts, packages/tasks-core/src/tests/TaskRunner.spec.ts
Rate-limit 저장소와 TaskRunner가 주입된 시계·난수·스케줄러를 사용합니다. 정리 예약은 일회성 예약과 취소 함수로 관리합니다.
문제 문서와 릴리스 메타데이터
docs/problem-code-registry.json, packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md, .changeset/*, packages/testing/package.json, packages/testing/tsconfig.json, public-api-surface.snapshot.json
테스트 런타임 문제 코드와 소스 위치를 등록하고 네 개 패키지의 minor changeset을 추가했습니다. @croco/retry-core workspace 의존성과 경로 별칭, 공개 API 스냅샷을 갱신했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TestKernel
  participant TestRuntime
  participant TestClock
  participant RateLimitStore
  participant TaskRunner
  participant Diagnostics

  TestKernel->>TestRuntime: 런타임 옵션 생성
  TestRuntime->>TestClock: 가상 시계 제공
  TestKernel->>RateLimitStore: clock, random, scheduler 주입
  TestKernel->>TaskRunner: now, schedule 주입
  RateLimitStore->>TestClock: 현재 시각 조회 및 작업 예약
  TaskRunner->>TestClock: 타임아웃 작업 예약
  TestKernel->>Diagnostics: 누수와 replay 정보 보고
Loading

Possibly related PRs

  • croco-dev/framework#1559: @croco/testingTestKernel 기반 위에 결정적 런타임 제어와 누수 추적을 확장한 변경과 연결됩니다.
  • croco-dev/framework#1671: TestKernel의 결정적 replay 및 리소스 누수 메타데이터와 연결됩니다.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive 핵심 기능과 테스트는 구현되었지만 idempotency, trigger, telemetry flush 시나리오의 충족 여부는 제공된 요약만으로 확인할 수 없습니다. idempotency, trigger, telemetry flush 검증 결과와 관련 파일의 구현 근거를 추가로 제공하십시오.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 TestKernel 기반 결정적 테스트 런타임 제어라는 주요 변경을 명확하게 설명합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 결정적 런타임 제어, 누수 진단, 관련 패키지 연동, 테스트, 문서 및 공개 API 갱신 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1483-deterministic-test-runtime

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

📊 Benchmark Results

✅ All benchmarks passed

Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 44.2μs 30.0ms 8.2μs +441.0% -
CrocoApp lambdaHandler (10 controllers) 1.9ms 50.0ms 258.4μs +647.3% -
Lambda cold-start simulation 2.4ms 80.0ms 418.1μs +467.7% -
Lambda cold-start with headers 1.5ms 80.0ms 369.7μs +309.8% -
Lambda cold-start with binary body 1.5ms 80.0ms 339.1μs +346.7% -
Lambda cold-start with query params 1.5ms 80.0ms 301.3μs +389.5% -
Lambda cold-start with authorizer context 1.5ms 80.0ms 299.8μs +399.5% -
Lambda cold-start realistic scenario 1.5ms 80.0ms 299.2μs +394.6% -
EventBusConfig.start (10 handlers) 1.7μs 10.0ms 1.4μs +18.8% -
EventPublisher.publishNow single event 1.9μs 2.0ms 1.7μs +12.5% -
DefaultHandlerResolver.resolve × 10 0.1μs 5.0ms 0.1μs -11.2% -
Container.get singleton (cold) 87.5μs 5.0ms 70.3μs +24.5% -
Container.register × 50 components 3.4ms 10.0ms 3.2ms +4.1% -
Container.validate (50 components) 3.8ms 20.0ms 3.4ms +12.8% -
Container.get singleton (warm) 1.7μs 500.0μs 1.6μs +3.7% -
TelemetryRuntime.init (lambda preset) 2.3μs 200.0ms 1.1ms -99.8% -
lambdaPreset config creation 1.5μs 2.0ms 1.4μs +6.4% -

Updated: 2026-08-05T05:31:00.629Z · Commit: 497eb55

@kang-heewon

Copy link
Copy Markdown
Member Author

CI / validate failed only in the generated production-app-starter smoke fixture. The unchanged template test expects app.lambdaHandler() to throw transports-http/di-bootstrap-validation for MissingProviderController, but it receives no error.

This PR does not modify packages/create-croco-app, packages/transports-http, or packages/framework-context; the failed template test is byte-for-byte unchanged from origin/trunk. The issue #1483 package suites and their contract gates pass. I am leaving the generated-app expectation/runtime mismatch to its owning scope rather than extending this testing-runtime PR.

@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from 9442ad4 to a8bb660 Compare August 1, 2026 22:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/testing/src/libs/TestKernel.ts (1)

485-492: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

trackInFlight에서 거부 사유를 완전히 버린다.

operation.then(onFulfilled, onRejected)의 두 콜백 모두 this.inFlight.delete(operation)만 수행한다. 추적된 프라미스가 거부(reject)되어도 그 원인은 어디에도 기록되지 않는다.

waitUntil처럼 호출자가 반환된 프라미스를 기다리지 않는 경로에서는, 백그라운드 작업이 실패해도 정상 완료된 것과 구분할 수 없다. 이 파일이 목표로 하는 구조화된 누수 진단(structured leak diagnostics)의 취지와 어긋난다.

거부된 원인을 별도 버퍼에 남기고 expectClean/disposeOnce의 진단 정보에 포함하라.

🛠️ 실패 원인을 보존하는 수정 제안
-  private readonly inFlight = new Map<Promise<unknown>, TestKernelTrackedWork>();
+  private readonly inFlight = new Map<Promise<unknown>, TestKernelTrackedWork>();
+  private readonly trackedFailures: Array<{ error: Error; work: TestKernelTrackedWork }> = [];
   private trackInFlight<T>(operation: Promise<T>, work: TestKernelTrackedWork): Promise<T> {
     this.inFlight.set(operation, work);
     void operation.then(
       () => this.inFlight.delete(operation),
-      () => this.inFlight.delete(operation),
+      (error: unknown) => {
+        this.inFlight.delete(operation);
+        this.trackedFailures.push({ error: toError(error), work });
+      },
     );
     return operation;
   }

As per coding guidelines, "request lifecycle, trace, retry, event, Problem, DI scope, telemetry init/flush 경계에는 원인 추적 가능한 evidence를 남기며, 관측 실패를 비즈니스 성공처럼 보이게 하지 않는다."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/testing/src/libs/TestKernel.ts` around lines 485 - 492, Update
trackInFlight to preserve each rejected operation’s reason in a dedicated
diagnostic buffer instead of discarding it after removing the operation from
inFlight. Include the buffered rejection evidence in the diagnostics produced by
expectClean and disposeOnce, while keeping successful operations’ existing
cleanup behavior unchanged.

Source: Coding guidelines

packages/testing/src/tests/TestKernel.spec.ts (1)

617-629: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

rejects.toBeInstanceOf(TestKernelDisposalProblem)rejects.toThrow(TestKernelDisposalProblem)로 바꾸라. Vitest 가이드라인에서 비동기 오류는 rejects.toThrow로 검증하고, 생성자 인자를 쓴 toThrow는 동일한 instanceof 테스트를 수행한다.

  • packages/testing/src/tests/TestKernel.spec.ts#L629: rejects.toBeInstanceOf(TestKernelDisposalProblem)rejects.toThrow(TestKernelDisposalProblem)로 변경하라.
  • packages/testing/src/tests/TestKernel.spec.ts#L814: rejects.toBeInstanceOf(TestKernelDisposalProblem)rejects.toThrow(TestKernelDisposalProblem)로 변경하라.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/testing/src/tests/TestKernel.spec.ts` around lines 617 - 629, Update
both TestKernel.spec.ts sites at lines 617-629 and 789-815: replace the
asynchronous disposal assertions using
rejects.toBeInstanceOf(TestKernelDisposalProblem) with
rejects.toThrow(TestKernelDisposalProblem), preserving the existing
TestKernelDisposalProblem constructor assertion.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts`:
- Around line 13-16: Extend InMemoryRateLimitStoreOptions with a cancellable
scheduler contract that supports scheduling prune callbacks, and update all
three store implementations to use it instead of global setInterval. Ensure the
scheduler integrates with virtual time so advancing time runs pending prune
work, and make each store’s close() cancel its scheduled prune operation.

In `@packages/tasks-core/src/index.ts`:
- Line 29: Separate the combined export in packages/tasks-core/src/index.ts:
keep TaskRunner in the value export group, and add TaskRunnerRuntime as a
standalone type export in the file’s final type-export group, following the
barrel export ordering guidelines.

In `@packages/tasks-core/src/tests/TaskRunner.spec.ts`:
- Around line 671-684: Update the schedule mock in the controlled timed-task
test to capture the provided delayMs and assert it equals 100 before triggering
the callback. Change the result assertion from
rejects.toBeInstanceOf(TaskExecutionTimeoutProblem) to
rejects.toThrow(TaskExecutionTimeoutProblem), preserving the existing timeout
execution flow.

In `@packages/testing/src/libs/TestRuntime.ts`:
- Around line 190-200: Update TestNetwork.fetch to be async so denied requests
return a rejected Promise rather than throwing synchronously, and ensure
malformed URLs are converted to TestKernelOutboundCallProblem instead of leaking
a native TypeError. Update the affected TestRuntime tests to await fetch and use
rejects.toThrow assertions.

---

Outside diff comments:
In `@packages/testing/src/libs/TestKernel.ts`:
- Around line 485-492: Update trackInFlight to preserve each rejected
operation’s reason in a dedicated diagnostic buffer instead of discarding it
after removing the operation from inFlight. Include the buffered rejection
evidence in the diagnostics produced by expectClean and disposeOnce, while
keeping successful operations’ existing cleanup behavior unchanged.

In `@packages/testing/src/tests/TestKernel.spec.ts`:
- Around line 617-629: Update both TestKernel.spec.ts sites at lines 617-629 and
789-815: replace the asynchronous disposal assertions using
rejects.toBeInstanceOf(TestKernelDisposalProblem) with
rejects.toThrow(TestKernelDisposalProblem), preserving the existing
TestKernelDisposalProblem constructor assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a7c8525-e17e-42e6-908d-91b7fae46217

📥 Commits

Reviewing files that changed from the base of the PR and between 986ce2d and a8bb660.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .changeset/deterministic-test-runtime-controls.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
  • packages/ratelimit-core/src/libs/RateLimitStore.ts
  • packages/tasks-core/src/index.ts
  • packages/tasks-core/src/libs/TaskRunner.ts
  • packages/tasks-core/src/tests/TaskRunner.spec.ts
  • packages/testing/README.md
  • packages/testing/package.json
  • packages/testing/src/index.ts
  • packages/testing/src/libs/TestKernel.ts
  • packages/testing/src/libs/TestRuntime.ts
  • packages/testing/src/tests/TestKernel.spec.ts
  • packages/testing/src/tests/TestRuntime.spec.ts
  • packages/testing/tsconfig.json
  • public-api-surface.snapshot.json

Comment thread packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
Comment thread packages/tasks-core/src/index.ts Outdated
Comment thread packages/tasks-core/src/tests/TaskRunner.spec.ts Outdated
Comment thread packages/testing/src/libs/TestRuntime.ts
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from 07e45bc to 2137d46 Compare August 1, 2026 23:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts`:
- Around line 20-22: Change RateLimitPruneScheduler from a type alias to an
interface while preserving its existing schedule method signature and behavior.

In `@packages/testing/src/libs/TestRuntime.ts`:
- Around line 83-87: Update the scheduling state used by pendingWork and
nextScheduled so each queued item retains its numeric scheduledSequence
separately from its string id. When dueAtMs values are equal, sort by that
numeric sequence instead of id.localeCompare, preserving insertion order for IDs
such as scheduled-10 and scheduled-2 while leaving due-time ordering unchanged.
- Around line 116-133: Update TestRuntime.drain and its drainUntil flow to
prevent non-terminating execution when callbacks such as InMemoryRateLimitStore
cleanup reschedule work at the same dueAtMs. Add a bounded iteration safeguard
that throws an identifiable Problem after exceeding the limit, or enforce the
documented contract that callbacks may not reschedule during draining; preserve
normal draining behavior for finite schedules.

In `@packages/testing/src/tests/TestRuntime.spec.ts`:
- Around line 51-70: The test only covers string-seed initialization and not
reuse of an existing TestIdSource. Extend the test around TestRuntime
construction to pass a previously advanced TestIdSource instance via ids, then
verify the runtime continues that source’s existing sequence rather than
resetting it; preserve the existing environment, replay, and deterministic-value
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e599791d-3a66-4abe-8496-600e25f18f46

📥 Commits

Reviewing files that changed from the base of the PR and between a8bb660 and e397920.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .changeset/deterministic-test-runtime-controls.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/ratelimit-core/src/index.ts
  • packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
  • packages/ratelimit-core/src/libs/RateLimitStore.ts
  • packages/tasks-core/src/index.ts
  • packages/tasks-core/src/libs/TaskRunner.ts
  • packages/tasks-core/src/tests/TaskRunner.spec.ts
  • packages/testing/README.md
  • packages/testing/package.json
  • packages/testing/src/index.ts
  • packages/testing/src/libs/TestKernel.ts
  • packages/testing/src/libs/TestRuntime.ts
  • packages/testing/src/tests/TestKernel.spec.ts
  • packages/testing/src/tests/TestRuntime.spec.ts
  • packages/testing/tsconfig.json
  • public-api-surface.snapshot.json

Comment thread packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts Outdated
Comment thread packages/testing/src/libs/TestRuntime.ts
Comment thread packages/testing/src/libs/TestRuntime.ts
Comment thread packages/testing/src/tests/TestRuntime.spec.ts
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from c252343 to 318c779 Compare August 2, 2026 14:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/tasks-core/src/libs/TaskRunner.ts`:
- Around line 17-20: TaskRunnerRuntime의 선언을 type alias에서 interface로 변경하여 호출자가
구현하는 런타임 주입 계약으로 표현하십시오. now와 schedule 속성의 readonly 및 선택적 특성과 각 함수 시그니처는 그대로
유지하십시오.

In `@packages/tasks-core/src/tests/TaskRunner.spec.ts`:
- Line 645: Update the Component decorator on the test handler in
TaskRunner.spec.ts to explicitly set its registration scope to singleton,
preserving the handler’s existing behavior and avoiding reliance on the default
scope.

In `@packages/testing/package.json`:
- Line 63: packages/testing의 `@croco/retry-core` 의존성을 dependencies에서
devDependencies로 이동하십시오. packages/testing/src의 컴파일 및 타입 검사에 필요한 사용은 유지하되, 외부 런타임
의존성으로 설치되지 않도록 package.json의 의존성 구성을 수정하십시오.

In `@packages/testing/src/libs/TestKernel.ts`:
- Around line 516-527: Update createTestKernel’s runtimeOptions construction so
an existing options.ids TestIdSource is duplicated through its state-preserving
fork() rather than recreated from seed. Add fork() implementations to
TestIdSource and TestRandomSource that preserve sequence progress and internal
random state, then use the forked source when creating the kernel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3a489cf3-1b35-4d46-a8b8-47889e10ad4e

📥 Commits

Reviewing files that changed from the base of the PR and between e397920 and 318c779.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .changeset/deterministic-test-runtime-controls.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/ratelimit-core/src/index.ts
  • packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
  • packages/ratelimit-core/src/libs/RateLimitStore.ts
  • packages/tasks-core/src/index.ts
  • packages/tasks-core/src/libs/TaskRunner.ts
  • packages/tasks-core/src/tests/TaskRunner.spec.ts
  • packages/testing/README.md
  • packages/testing/package.json
  • packages/testing/src/index.ts
  • packages/testing/src/libs/TestKernel.ts
  • packages/testing/src/libs/TestRuntime.ts
  • packages/testing/src/tests/TestKernel.spec.ts
  • packages/testing/src/tests/TestRuntime.spec.ts
  • packages/testing/tsconfig.json
  • public-api-surface.snapshot.json

Comment thread packages/tasks-core/src/libs/TaskRunner.ts Outdated
Comment thread packages/tasks-core/src/tests/TaskRunner.spec.ts
Comment thread packages/testing/package.json Outdated
Comment thread packages/testing/src/libs/TestKernel.ts
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from 1036e87 to 0fc2d6b Compare August 4, 2026 06:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/tasks-core/src/libs/TaskRunner.ts (1)

177-180: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

TaskRunnerruntime.now를 주입해 timeout 시계를 통일해도 됩니다.

executionManager.start()Date.now()startedAt을 기록하고, new TaskRunner() 기본 구성은 runtime.now가 없을 때 Date.now()만 사용합니다. DI로 주입되는 runtime.now가 있다면 ExecutionManager의 시작 시각도 같은 시계로 기록하거나, TaskRunnerRuntime의 가상 시계도 실제 시각과 동기화되도록 설정해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tasks-core/src/libs/TaskRunner.ts` around lines 177 - 180, Unify
timeout timing between TaskRunner and ExecutionManager by using the injected
TaskRunnerRuntime.now when recording startedAt in ExecutionManager.start().
Preserve Date.now() only as the fallback when no runtime clock is provided, so
TaskRunner’s deadline calculation uses the same clock source as execution
startup.
♻️ Duplicate comments (1)
packages/testing/src/libs/TestRuntime.ts (1)

138-155: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

drain()의 전체 실행에 callback 상한을 적용하십시오.

callbackCountdrainUntil()을 호출할 때마다 0으로 초기화됩니다. 양수 지연으로 자신을 다시 예약하는 callback은 외부 while 반복마다 한 번 실행됩니다. 따라서 MAX_DRAIN_CALLBACKS에 도달하지 않으며 drain()은 종료되지 않습니다.

하나의 callback 예산을 drain()의 모든 drainUntil() 호출에서 공유하십시오. 양수 지연의 반복 작업을 사용하는 회귀 테스트도 추가하십시오.

🐛 수정 예시
   async drain(): Promise<void> {
+    const budget = { callbackCount: 0 };
     while (this.scheduled.size > 0) {
       const next = this.nextScheduled();
       if (!next) return;
-      await this.drainUntil(next.dueAtMs);
+      await this.drainUntil(next.dueAtMs, budget);
     }
   }

-  private async drainUntil(targetTime: number): Promise<void> {
-    let callbackCount = 0;
+  private async drainUntil(
+    targetTime: number,
+    budget = { callbackCount: 0 },
+  ): Promise<void> {
     while (true) {
       const next = this.nextScheduled();
       if (!next || next.dueAtMs > targetTime) return;

-      callbackCount += 1;
-      if (callbackCount > MAX_DRAIN_CALLBACKS) {
+      budget.callbackCount += 1;
+      if (budget.callbackCount > MAX_DRAIN_CALLBACKS) {
         throw new TestRuntimeDrainProblem(MAX_DRAIN_CALLBACKS);
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/testing/src/libs/TestRuntime.ts` around lines 138 - 155, Update
drain() and drainUntil() in TestRuntime so a single callback count is shared
across the entire drain operation instead of resetting on each drainUntil()
call. Enforce MAX_DRAIN_CALLBACKS for callbacks that reschedule themselves with
positive delays, and add a regression test covering this repeating-work case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts`:
- Around line 51-60: Validate pruneIntervalMs before selecting or invoking the
default scheduler in the rate-limit store setup around scheduleNext. When using
the native/default scheduler, require a finite delay within the scheduler’s
supported setTimeout range and reject invalid values, including NaN, positive
infinity, and oversized intervals, with a stable configuration Problem; preserve
the existing disabled behavior for non-positive intervals.

In `@packages/testing/src/index.ts`:
- Around line 97-102: Move the newly added type exports, including
TestKernelLeak, TestKernelTrackedWork, and TestKernelRuntime-related types, out
of their current category blocks into a final export type section at the end of
the index module. Keep all value exports in their existing category groups and
preserve each type export without changing its name or source.

In `@packages/testing/src/libs/TestRuntime.ts`:
- Line 118: Validate the parsed delay, computed dueAtMs, and replay targetTime
in TestRuntime so each remains a safe integer within the valid Date range before
storing or converting it. Reject invalid values by throwing
TestRuntimeConfigurationProblem, covering both pendingWork scheduling and replay
paths, while preserving valid timing behavior.

In `@packages/testing/src/tests/TestKernel.spec.ts`:
- Around line 13-19: Separate the TestKernel type import from the value imports
by keeping the existing ../index value import unchanged and adding a distinct
import type declaration for TestKernel from the same module.
- Line 716: Remove the non-null assertion from bootstrapReplay in
TestKernel.spec.ts and declare it as allowing undefined. After bootstrap, assert
that kernel.replay and bootstrapReplay are defined immediately before their
respective expectations, preserving the existing replay validation flow.

---

Outside diff comments:
In `@packages/tasks-core/src/libs/TaskRunner.ts`:
- Around line 177-180: Unify timeout timing between TaskRunner and
ExecutionManager by using the injected TaskRunnerRuntime.now when recording
startedAt in ExecutionManager.start(). Preserve Date.now() only as the fallback
when no runtime clock is provided, so TaskRunner’s deadline calculation uses the
same clock source as execution startup.

---

Duplicate comments:
In `@packages/testing/src/libs/TestRuntime.ts`:
- Around line 138-155: Update drain() and drainUntil() in TestRuntime so a
single callback count is shared across the entire drain operation instead of
resetting on each drainUntil() call. Enforce MAX_DRAIN_CALLBACKS for callbacks
that reschedule themselves with positive delays, and add a regression test
covering this repeating-work case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: aaf13885-2578-4d9e-a84a-42881b290499

📥 Commits

Reviewing files that changed from the base of the PR and between 318c779 and 0fc2d6b.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .changeset/deterministic-test-runtime-controls.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/ratelimit-core/src/index.ts
  • packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
  • packages/ratelimit-core/src/libs/RateLimitStore.ts
  • packages/tasks-core/src/index.ts
  • packages/tasks-core/src/libs/TaskRunner.ts
  • packages/tasks-core/src/tests/TaskRunner.spec.ts
  • packages/testing/README.md
  • packages/testing/package.json
  • packages/testing/src/index.ts
  • packages/testing/src/libs/TestKernel.ts
  • packages/testing/src/libs/TestRuntime.ts
  • packages/testing/src/tests/TestKernel.spec.ts
  • packages/testing/src/tests/TestRuntime.spec.ts
  • packages/testing/tsconfig.json
  • public-api-surface.snapshot.json

Comment thread packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
Comment thread packages/testing/src/index.ts Outdated
Comment thread packages/testing/src/libs/TestRuntime.ts Outdated
Comment thread packages/testing/src/tests/TestKernel.spec.ts
Comment thread packages/testing/src/tests/TestKernel.spec.ts Outdated
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from 4d3d57d to 4edd6b7 Compare August 4, 2026 13:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ratelimit-core/src/tests/InMemoryRateLimitStore.spec.ts`:
- Around line 28-35: Update the test file’s beforeEach setup to reset the DI
Container as its first step, then initialize the store instance. Follow the
existing Vitest setup pattern so each test, including the InMemoryRateLimitStore
constructor validation cases, starts with isolated container state.

In `@packages/tasks-core/src/index.ts`:
- Line 42: Reorganize the exports in the package index so all type-only exports
appear in the final group. Move the RegisteredTask type export from its current
position before value exports and combine it with TaskRunnerRuntime, while
preserving the existing value-export grouping.

In `@packages/testing/src/libs/TestKernel.ts`:
- Around line 536-547: Update the fidelityContext construction in the TestKernel
bootstrap flow so replay is exposed through a dynamic getter backed by the
current controls.replay value, rather than capturing controls.replay at
initialization. Preserve the TestRuntime.replay contract so reads after
clock.advanceBy() return the updated virtualTime, and add a test covering this
post-advance replay value.

In `@packages/testing/src/tests/TestRuntime.spec.ts`:
- Around line 1-13: TestRuntime 테스트에 각 테스트 전 DI 컨테이너를 초기화하는 beforeEach 훅을
추가하십시오. Vitest의 beforeEach를 import하고 resetCrocoTestingContext를 ../index에서 가져와,
rate-limit 및 retry 전역 등록 상태가 테스트마다 격리되도록 TestRuntime describe 블록에서 호출하십시오.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6022e06f-133a-4182-8ec6-20bc1bdda82b

📥 Commits

Reviewing files that changed from the base of the PR and between 0fc2d6b and 4edd6b7.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • .changeset/deterministic-test-runtime-controls.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/ratelimit-core/src/index.ts
  • packages/ratelimit-core/src/libs/InMemoryRateLimitStore.ts
  • packages/ratelimit-core/src/libs/RateLimitStore.ts
  • packages/ratelimit-core/src/libs/problems/RateLimitConfigProblems.ts
  • packages/ratelimit-core/src/tests/InMemoryRateLimitStore.spec.ts
  • packages/tasks-core/src/index.ts
  • packages/tasks-core/src/libs/TaskRunner.ts
  • packages/tasks-core/src/tests/TaskRunner.spec.ts
  • packages/testing/README.md
  • packages/testing/package.json
  • packages/testing/src/index.ts
  • packages/testing/src/libs/TestKernel.ts
  • packages/testing/src/libs/TestRuntime.ts
  • packages/testing/src/tests/TestKernel.spec.ts
  • packages/testing/src/tests/TestRuntime.spec.ts
  • packages/testing/tsconfig.json
  • public-api-surface.snapshot.json

Comment thread packages/ratelimit-core/src/tests/InMemoryRateLimitStore.spec.ts
Comment thread packages/tasks-core/src/index.ts
Comment thread packages/testing/src/libs/TestKernel.ts
Comment thread packages/testing/src/tests/TestRuntime.spec.ts Outdated
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch 5 times, most recently from c4e11f1 to 16970b2 Compare August 5, 2026 04:24
@kang-heewon
kang-heewon force-pushed the fix/1483-deterministic-test-runtime branch from 16970b2 to 97ee02c Compare August 5, 2026 05:21
@kang-heewon
kang-heewon merged commit c9c1c1d into trunk Aug 5, 2026
12 checks passed
@kang-heewon
kang-heewon deleted the fix/1483-deterministic-test-runtime branch August 5, 2026 06:34
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.

[testing] Add deterministic test runtime controls and leak detection

1 participant