Skip to content

test: Add contract tests for Log/LogWriter#122

Merged
nielsenko merged 3 commits into
serverpod:mainfrom
nielsenko:test-log-contract
Jun 6, 2026
Merged

test: Add contract tests for Log/LogWriter#122
nielsenko merged 3 commits into
serverpod:mainfrom
nielsenko:test-log-contract

Conversation

@nielsenko
Copy link
Copy Markdown
Contributor

@nielsenko nielsenko commented Jun 5, 2026

Framework-level tests pinning the Log contract

  • progress scoping & no-relabel,
  • synchronous (FutureOr) runners,
  • core dispatch + scoping
  • level gating,
  • in-order writes,
  • best-effort error swallowing,
  • scope fallback.

Summary by CodeRabbit

  • New Features

    • Introduced isolated_object package to wrap objects in dedicated isolates, enabling concurrent operation execution and keeping animations/event-loops responsive during blocking synchronous work.
  • Tests

    • Added comprehensive test suite covering isolate lifecycle, evaluation, and error handling.
    • Added tests for logging behavior and progress scope management.
  • Chores

    • Updated CI/CD workflows to include the new package in automated testing and publishing.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Complex PR? Review this PR in Change Stack to move by importance, not file order.

Review Change Stack

Warning

Review limit reached

@nielsenko, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 13 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 263b60d8-6d04-4d6a-b728-6afa520545ec

📥 Commits

Reviewing files that changed from the base of the PR and between 33ebf43 and b9138c6.

📒 Files selected for processing (5)
  • packages/serverpod_logging/test/log_dispatch_test.dart
  • packages/serverpod_logging/test/log_scope_test.dart
  • packages/serverpod_logging/test/progress_nesting_test.dart
  • packages/serverpod_logging/test/progress_relabel_test.dart
  • packages/serverpod_logging/test/progress_sync_runner_test.dart
📝 Walkthrough

Walkthrough

This PR introduces isolated_object, a new Dart package that wraps objects in dedicated child isolates with async request/response evaluation, adds it to the workspace and serverpod_logging dependencies, expands test coverage for serverpod_logging's logging and progress features, and updates CI/CD for testing and tag-based publishing.

Changes

IsolatedObject Package and Integration

Layer / File(s) Summary
Core IsolatedObject Implementation
packages/isolated_object/lib/src/isolated_object.dart, packages/isolated_object/lib/isolated_object.dart
IsolatedObject<T> establishes a parent-child isolate handshake using RawReceivePort and SendPort, spawns a child that constructs the target via Factory<T>, and implements an ID-keyed request/response protocol. evaluate sends _Action messages to the child and completes futures when either computed results or RemoteError payloads are received. close is idempotent, fails inflight requests with StateError, and signals shutdown via null message.
Comprehensive IsolatedObject Test Coverage
packages/isolated_object/test/evaluate_test.dart, packages/isolated_object/test/close_test.dart, packages/isolated_object/test/create_test.dart, packages/isolated_object/test/lifecycle_test.dart
Test suites verify successful construction with sync/async factories, exception handling, idempotent close, lifecycle state transitions, evaluation correctness for getters/mutations/sequential/concurrent operations, generic return types, async callbacks, error propagation as RemoteError, complex object state transfer, and keepIsolateAlive flag behavior.
IsolatedObject Package Metadata and Documentation
packages/isolated_object/pubspec.yaml, packages/isolated_object/analysis_options.yaml, packages/isolated_object/README.md, packages/isolated_object/LICENSE, packages/isolated_object/CHANGELOG.md
Package is configured with version 0.1.0, dev dependencies (serverpod_lints, test), analysis options extending serverpod_lints/cli.yaml with relaxed type constraints, a README describing usage and isolate behavior, BSD 3-Clause LICENSE, and CHANGELOG with initial version entry.
Workspace and Dependency Integration
pubspec.yaml, packages/serverpod_logging/pubspec.yaml
Root workspace configuration is updated to include packages/isolated_object, and serverpod_logging adds isolated_object: ^0.1.0 as a dependency.
ServerpodLogging Test Suite Expansion
packages/serverpod_logging/test/log_dispatch_test.dart, packages/serverpod_logging/test/log_scope_test.dart, packages/serverpod_logging/test/progress_nesting_test.dart, packages/serverpod_logging/test/progress_relabel_test.dart, packages/serverpod_logging/test/progress_sync_runner_test.dart
New comprehensive test suites for serverpod_logging verify Log dispatch gating, dynamic level changes at runtime, best-effort error handling during flush, LogScope root/child relationships and metadata propagation, Log.progress Zone-based scoping with nested progress support, scope relabeling prevention, and synchronous runner handling with correct success/error closure and exception rethrow.
CI/CD Pipeline Updates
.github/workflows/ci.yml, .github/workflows/publish-isolated_object.yaml
The dart_format and dart_analyze_test job matrices are expanded to include isolated_object package for Dart 3.6 and 3.12 (previous special-case exclusion logic for serverpod_logging is simplified). A new publish-isolated_object.yaml workflow triggers on tags matching isolated_object-vX.Y.Z (with optional -pre suffix) and publishes via the reusable dart-lang/setup-dart workflow.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • serverpod/cli_tools#109: Updates the GitHub Actions CI Dart SDK matrix to start at Dart 3.6 in .github/workflows/ci.yml, overlapping directly with this PR's CI configuration changes.
  • serverpod/cli_tools#38: Modifies .github/workflows/ci.yml's strategy.matrix to drive dart-lang/setup-dart from the Dart SDK version matrix, related to this PR's CI expansion logic.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'test: Add contract tests for Log/LogWriter' is partially related to the changeset. It accurately describes tests for Log/LogWriter but omits the addition of a significant new package (isolated_object) and broader CI/workflow updates that are also core to this PR. Revise the title to reflect the full scope, such as: 'test: Add isolated_object package and contract tests for Log/LogWriter' or 'chore: Add isolated_object package and expand logging tests'
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@nielsenko nielsenko marked this pull request as ready for review June 5, 2026 18:43
@nielsenko nielsenko force-pushed the test-log-contract branch 6 times, most recently from b1f9c0f to 2360dc9 Compare June 5, 2026 19:53
@nielsenko nielsenko force-pushed the test-log-contract branch from 2360dc9 to 33ebf43 Compare June 5, 2026 21:15
Copy link
Copy Markdown
Contributor

@marcelomendoncasoares marcelomendoncasoares left a comment

Choose a reason for hiding this comment

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

Just a comment about the shape of some tests.

Comment thread packages/serverpod_logging/test/log_dispatch_test.dart Outdated
nielsenko added 2 commits June 5, 2026 23:19
- progress_nesting_test: a log emitted inside a progress runner must be
  attributed to the progress scope via the Zone, and a nested progress
  must child under the outer scope.
- progress_relabel_test: progress must never surface the runner's result
  as a scope label (uses a generic recording writer, no terminal rendering).
These fail to compile if the runner is narrowed to Future<T> Function()
@nielsenko nielsenko force-pushed the test-log-contract branch from 33ebf43 to d45c27e Compare June 5, 2026 21:19
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/serverpod_logging/test/log_scope_test.dart (1)

5-16: ⚡ Quick win

Consider using setUp for shared test fixtures.

For consistency with the other test files in this PR (progress_nesting_test.dart, progress_relabel_test.dart, progress_sync_runner_test.dart), consider extracting the writer and log initialization to a setUp block. Both tests in this group create identical fixtures.

♻️ Proposed refactor
   group('Given Log scoping', () {
+    late TestLogWriter writer;
+    late Log log;
+
+    setUp(() {
+      writer = TestLogWriter();
+      log = Log(writer);
+    });
+
     test(
         'when a log is emitted outside any progress scope, '
         'then it attaches to the synthetic root scope', () async {
-      final writer = TestLogWriter();
-      final log = Log(writer);
-
       log.info('orphan');
🤖 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/serverpod_logging/test/log_scope_test.dart` around lines 5 - 16,
Extract the repeated test fixtures into a setUp block: move the TestLogWriter()
and Log(...) initialization currently created inside tests of the 'Given Log
scoping' group into a group-level setUp that assigns them to variables
accessible by the tests, so both tests reuse the same writer and log instances;
update tests to use those shared variables (referencing TestLogWriter and Log)
and ensure any needed cleanup (e.g., await log.flush()) remains in each test.
packages/isolated_object/test/create_test.dart (1)

43-46: ⚡ Quick win

Tighten the error type assertion.

The test verifies factory failures but uses throwsA(anything), which accepts any exception. For consistency with evaluate_test.dart (line 112) and to verify the protocol correctly propagates factory errors, assert that the error is a RemoteError.

✨ Suggested improvement
     await expectLater(
       isolated.evaluate((counter) => counter.value),
-      throwsA(anything),
+      throwsA(isA<RemoteError>()),
     );
🤖 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/isolated_object/test/create_test.dart` around lines 43 - 46, The
test currently asserts any exception via throwsA(anything) for the
isolated.evaluate((counter) => counter.value) call; change the assertion to
expect a RemoteError (e.g. throwsA(isA<RemoteError>())) so the test mirrors
evaluate_test.dart and ensures factory errors are propagated as RemoteError;
update the expectLater invocation around isolated.evaluate to use
throwsA(isA<RemoteError>()).
🤖 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.

Nitpick comments:
In `@packages/isolated_object/test/create_test.dart`:
- Around line 43-46: The test currently asserts any exception via
throwsA(anything) for the isolated.evaluate((counter) => counter.value) call;
change the assertion to expect a RemoteError (e.g. throwsA(isA<RemoteError>()))
so the test mirrors evaluate_test.dart and ensures factory errors are propagated
as RemoteError; update the expectLater invocation around isolated.evaluate to
use throwsA(isA<RemoteError>()).

In `@packages/serverpod_logging/test/log_scope_test.dart`:
- Around line 5-16: Extract the repeated test fixtures into a setUp block: move
the TestLogWriter() and Log(...) initialization currently created inside tests
of the 'Given Log scoping' group into a group-level setUp that assigns them to
variables accessible by the tests, so both tests reuse the same writer and log
instances; update tests to use those shared variables (referencing TestLogWriter
and Log) and ensure any needed cleanup (e.g., await log.flush()) remains in each
test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4a7df97c-361a-4005-8231-3e51ac9f06f2

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7a945 and 33ebf43.

📒 Files selected for processing (20)
  • .github/workflows/ci.yml
  • .github/workflows/publish-isolated_object.yaml
  • packages/isolated_object/CHANGELOG.md
  • packages/isolated_object/LICENSE
  • packages/isolated_object/README.md
  • packages/isolated_object/analysis_options.yaml
  • packages/isolated_object/lib/isolated_object.dart
  • packages/isolated_object/lib/src/isolated_object.dart
  • packages/isolated_object/pubspec.yaml
  • packages/isolated_object/test/close_test.dart
  • packages/isolated_object/test/create_test.dart
  • packages/isolated_object/test/evaluate_test.dart
  • packages/isolated_object/test/lifecycle_test.dart
  • packages/serverpod_logging/pubspec.yaml
  • packages/serverpod_logging/test/log_dispatch_test.dart
  • packages/serverpod_logging/test/log_scope_test.dart
  • packages/serverpod_logging/test/progress_nesting_test.dart
  • packages/serverpod_logging/test/progress_relabel_test.dart
  • packages/serverpod_logging/test/progress_sync_runner_test.dart
  • pubspec.yaml

@nielsenko nielsenko force-pushed the test-log-contract branch from d45c27e to 1d54b4c Compare June 5, 2026 21:27
Framework-level tests (no CLI rendering):

- Level gating skips the entry factory below the threshold
- Runtime logLevel changes take effect; isDebugEnabled tracks it
- Writer errors are swallowed (dispatch is best-effort)
- Writes serialize in invocation order even when earlier ones are slower
- close() drops any further dispatches
- Convenience methods map to the right level; error() attaches error + stack trace
- Logs emitted outside any progress attach to the synthetic root scope
- progress passes metadata through to the opened scope
- LogScope parent/child: root has no parent; a child keeps its own id/label/metadata
@nielsenko nielsenko merged commit 4c8220b into serverpod:main Jun 6, 2026
20 checks passed
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.

3 participants