Skip to content

Conversation

@techouse
Copy link
Owner

@techouse techouse commented Sep 26, 2025

This pull request significantly expands unit test coverage for the qs_dart package, focusing on edge cases, error handling, and utility behaviors in both encoding and decoding logic. The changes add new test groups and cases across several test files, targeting areas such as cycle detection, list formatting, surrogate character encoding, custom object handling, and decoder fallback logic.

Encode/Decode Logic Coverage:

  • Added targeted tests to decode_test.dart for comma splitting with list limits, strict/non-strict depth handling, parameter limit coercion, and dot segment parsing, improving coverage of edge behaviors in decoding (test/unit/decode_test.dart).
  • Added extensive edge case tests to encode_edge_cases_test.dart, including cycle detection, strict null handling, custom encoder/filter behaviors, empty list handling, and shared object serialization (test/unit/encode_edge_cases_test.dart).
  • Improved custom object property access and filter expansion logic in encoding tests, and added cases for empty lists, comma round-trip, and cycle detection in encode_test.dart (test/unit/encode_test.dart). [1] [2] [3]

Utility Function Coverage:

  • Added a comprehensive suite of tests to utils_test.dart for surrogate character encoding, charset edge cases, merge logic (scalars, iterables, maps), encoding boundaries, primitive detection, numeric entity interpretation, and index map creation (test/unit/utils_test.dart).

Options and List Format Testing:

  • Added tests for legacy decoder fallback and decoder forwarding in decode_options_test.dart (test/unit/models/decode_options_test.dart).
  • Added new tests for all ListFormat generator variants and their string representation in list_format_test.dart (test/unit/list_format_test.dart).

These additions substantially improve code coverage, especially for error branches, boundary cases, and custom configuration paths.

@techouse techouse self-assigned this Sep 26, 2025
@techouse techouse added the test label Sep 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds and updates unit tests across decode, encode, list format, utils, and decode options, increasing edge-case coverage for comma/list limits, depth/strictDepth semantics, allowDots behavior, legacy decoder fallback, surrogate/charset handling and many encode edge cases. No runtime or public API changes except a test-only CustomObject adjustment.

Changes

Cohort / File(s) Summary
Decode tests
test/unit/decode_test.dart, test/unit/models/decode_options_test.dart
Adds targeted decode coverage: comma list truncation and limits, strict vs non-strict depth and remainder handling, parameterLimit/listLimit edge cases, allowDots behavior for hyphen-prefixed and non-identifier starts, RangeError scenarios, and legacy decoder fallback/forwarding tests.
Encode tests
test/unit/encode_test.dart, test/unit/encode_edge_cases_test.dart
Refactors test CustomObject to expose a public value and non-null operator[]; switches to filter-based encode path in tests and adds extensive encode edge-case coverage (cycle detection, strictNullHandling, allowEmptyLists, comma round-trip, custom encoders, shared-object scenarios).
ListFormat tests
test/unit/list_format_test.dart
New file testing ListFormat generators (brackets, comma, repeat, indices) and enum toString() representation.
Utils tests
test/unit/utils_test.dart
Expands utils coverage: surrogate-pair and lone-surrogate UTF-8 handling, latin1 decoding, Utils.merge branches (Undefined, scalar/list wrapping, index maps), helpers (isNonNullishPrimitive, interpretNumericEntities, createIndexMap); adds dart:collection import.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor TestCaller
  participant QS
  participant DecodeOptions
  participant PrimaryDecoder as Primary decoder
  participant LegacyDecoder as Legacy decoder

  TestCaller->>QS: QS.decode(input, options)
  QS->>DecodeOptions: resolve decoders, limits, flags
  alt primary decoder present
    QS->>PrimaryDecoder: decode(value, charset, kind?)
    PrimaryDecoder-->>QS: decoded value
  else fallback to legacy decoder
    QS->>LegacyDecoder: decode(value, charset)
    LegacyDecoder-->>QS: decoded value
  end
  QS-->>TestCaller: decoded structure (applying depth/list limits)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

In burrows of tests I hop and peep,
Chasing commas where lists sleep.
Brackets, dots and legacy trails,
I tuck away all fragile fails.
A twitch, a nibble — edge cases trimmed, hooray! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description provides a detailed summary of the test additions but does not follow the repository’s required template, as it lacks the “## Description” heading, issue reference, “## Type of change”, “## How Has This Been Tested?”, and the “## Checklist” sections. Please update the PR description to include the missing template sections: add a “## Description” section with summary and issue reference, a “## Type of change” list, a “## How Has This Been Tested?” section detailing the tests run, and the “## Checklist” section.
Title Check ❓ Inconclusive The title “:white_check_mark: increase coverage” is related to the primary change of improving coverage but is overly generic and includes an emoji, offering little context about what coverage is being increased or where. Please consider removing the emoji and making the title more descriptive and specific, for example “Increase unit test coverage for encoding and decoding logic in qs_dart”.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/more-tests

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64bca00 and 7e11b30.

📒 Files selected for processing (1)
  • test/unit/encode_edge_cases_test.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/unit/encode_edge_cases_test.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis

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.

@codacy-production
Copy link

codacy-production bot commented Sep 26, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+3.58% (target: -1.00%)
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (fb0988d) 895 846 94.53%
Head commit (7e11b30) 895 (+0) 878 (+32) 98.10% (+3.58%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#40) 0 0 ∅ (not applicable)

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codecov
Copy link

codecov bot commented Sep 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.10%. Comparing base (fb0988d) to head (7e11b30).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #40      +/-   ##
==========================================
+ Coverage   94.52%   98.10%   +3.57%     
==========================================
  Files          14       14              
  Lines         895      895              
==========================================
+ Hits          846      878      +32     
+ Misses         49       17      -32     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 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.

@techouse techouse marked this pull request as ready for review September 26, 2025 20:48
Copy link

@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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/unit/models/decode_options_test.dart (1)

351-363: Rename test for accuracy (it’s not using the deprecated path)

This test directly invokes opts.decoder(...). It doesn’t validate any “deprecated” forwarding; it asserts the primary decoder receives kind/charset/value. Consider renaming for clarity.

Apply this diff:

-    test('deprecated decoder forwards to decode implementation', () {
+    test('decoder exposes kind and charset arguments', () {
test/unit/encode_test.dart (1)

18-23: Optional: make the helper less throwy

operator [] throwing on unknown keys can hide unrelated failures if the object leaks into encode without the filter. Returning null is often safer in test doubles.

-  String? operator [](String key) {
-    if (key == 'prop') return value;
-    throw UnsupportedError('Only prop supported');
-  }
+  String? operator [](String key) => key == 'prop' ? value : null;
test/unit/utils_additional_test.dart (1)

17-19: Avoid asserting on concrete Map subtype

Casting to SplayTreeMap makes the test brittle against internal changes. Assert on Map emptiness instead.

-      final splay = result as SplayTreeMap;
-      expect(splay.isEmpty, isTrue);
+      expect(result, isA<Map>());
+      expect((result as Map).isEmpty, isTrue);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb0988d and fb64587.

📒 Files selected for processing (5)
  • test/unit/decode_test.dart (1 hunks)
  • test/unit/encode_test.dart (2 hunks)
  • test/unit/list_format_test.dart (1 hunks)
  • test/unit/models/decode_options_test.dart (1 hunks)
  • test/unit/utils_additional_test.dart (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (3)
test/unit/list_format_test.dart (1)

22-24: LGTM

Good coverage for generator outputs and enum string representation.

test/unit/encode_test.dart (1)

97-110: LGTM

Nice, concise filter-based expansion path; asserts the exact encoded shape.

test/unit/decode_test.dart (1)

2796-2874: LGTM

Useful, targeted additions for comma limits, depth strictness, and parameter limit coercion.

Copy link

@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.

Actionable comments posted: 3

🧹 Nitpick comments (5)
lib/src/extensions/encode.dart (1)

131-148: Optional: extract dot-in-keys handling into a small helper.

The primitive fast‑path now has its own dot-encoding logic in addition to the non‑primitive path. Extracting a String _applyDotEncoding(String prefix, {required bool allowDots, required bool encodeDotInKeys, required bool isLeaf}) would centralise the rules and reduce future drift.

test/unit/encode_test.dart (1)

18-23: Tighten operator return type.

operator [] never returns null (it throws for unsupported keys), so the return type can be String instead of String?.

Apply this diff:

-  String? operator [](String key) {
+  String operator [](String key) {
     if (key == 'prop') return value;
     throw UnsupportedError('Only prop supported');
   }
test/unit/utils_test.dart (1)

1243-1273: Avoid coupling tests to internal chunk size (1024).

Hard-coding segmentLimit = 1024 makes the test brittle if the implementation changes its internal chunking. Prefer an assertion that verifies surrogate pairs aren’t split and the encoded emoji sequence appears intact, without depending on the exact boundary.

For example, drop the boundary constant and only assert:

  • startsWith the expected run of 'a's,
  • contains the %F0%9F%98%80 sequence,
  • endsWith the tail.
test/unit/encode_edge_cases_test.dart (2)

68-82: Make percent-encoding checks case-insensitive to avoid brittle failures

Some encoders may emit lowercase hex. Use case-insensitive regex instead of literal .contains.

-      expect(encoded.contains('a%5Bx%5D%5Bk%5D=v'), isTrue);
-      expect(encoded.contains('b%5By%5D%5Bz%5D%5Bk%5D=v'), isTrue);
+      expect(RegExp(r'a%5Bx%5D%5Bk%5D=v', caseSensitive: false).hasMatch(encoded), isTrue);
+      expect(RegExp(r'b%5By%5D%5Bz%5D%5Bk%5D=v', caseSensitive: false).hasMatch(encoded), isTrue);

25-26: Tighten the occurrence check to the specific key path

Counting every "=1" is permissive and could false-positive. Count the exact 'z' path occurrences instead.

-      final occurrences = '=1'.allMatches(encoded).length;
+      final occurrences =
+          RegExp(r'%5Bz%5D=1', caseSensitive: false).allMatches(encoded).length;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb64587 and 4775b94.

📒 Files selected for processing (5)
  • lib/src/extensions/encode.dart (1 hunks)
  • test/unit/decode_test.dart (1 hunks)
  • test/unit/encode_edge_cases_test.dart (1 hunks)
  • test/unit/encode_test.dart (3 hunks)
  • test/unit/utils_test.dart (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/unit/decode_test.dart
🔇 Additional comments (9)
lib/src/extensions/encode.dart (1)

150-155: LGTM: key/value emission respects encoder/formatter semantics.

Using encoder for values (and for keys when encodeValuesOnly == false) and always passing through formatter aligns with the rest of the encoder.

test/unit/encode_test.dart (2)

97-110: LGTM: filter-based expansion of custom objects.

The test clearly validates the filter hook maps CustomObject into a serialisable map, matching encoder expectations.


5240-5283: LGTM: additional coverage meaningfully exercises edge paths.

Good assertions for allowEmptyLists, comma round-trip, dot-in-keys with allowDots, and cycle detection.

test/unit/utils_test.dart (4)

3-3: LGTM: dart:collection import is appropriate.

Required for SplayTreeMap use in merge tests.


1164-1184: LGTM: surrogate and charset edge cases improve decoder/encoder robustness.

Good coverage for lone surrogates and latin1 behaviour on invalid escapes.


1186-1241: LGTM: merge edge branches are well covered.

Solid scenarios around Undefined, scalar/iterable combinations, and index map promotion.


1275-1295: LGTM: helper coverage is precise.

Nice checks on isNonNullishPrimitive with Uri, astral-plane numeric entity handling, and index map materialisation.

test/unit/encode_edge_cases_test.dart (2)

110-121: Confirm intended behaviour: encoder mutates keys when strictNullHandling omits the value

This asserts the encoder is applied to the key (yielding 'X_nil') when the value is null and strictNullHandling is true. Please confirm this is an intentional, documented behaviour so future refactors don’t break it silently. If intentional, consider adding a brief note in the EncodeOptions/encoder docs.


84-97: LGTM: good fast‑path coverage for dot-in-keys with encodeValuesOnly

This nicely exercises dot encoding on keys while restricting the encoder to values.

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (6)
test/unit/encode_edge_cases_test.dart (4)

10-10: Guarantee the throwing indexer path is exercised

If the encoder checks membership (e.g., containsKey/keys) before indexing, 'boom' not being present in _store could skip operator[] and not hit the try/catch path. Seed 'boom' with a null entry so the indexer is invoked and throws as intended.

Apply:

-  final Map<String, dynamic> _store = {'ok': 42};
+  final Map<String, dynamic> _store = {'ok': 42, 'boom': null};

134-137: Tighten assertion to expect percent-encoded brackets

The suite generally asserts percent-encoded brackets. Make the check strict to avoid masking regressions.

-      // Allow either percent-encoded or raw bracket form (both are acceptable depending on encoding path),
-      // and an optional trailing '=' if future changes emit an explicit empty value.
-      final pattern = RegExp(r'^(outer%5Bp%5D%5B%5D=?|outer\[p\]\[\](=?))$');
-      expect(encoded, matches(pattern));
+      // Expect percent-encoded bracket form; allow optional trailing '='.
+      expect(encoded.startsWith('outer%5Bp%5D%5B%5D'), isTrue);

As per previous review feedback.


46-49: Prefer asserting explicit key paths over counting '=1'

Counting '=1' is brittle. Assert both expected encoded paths to remove ambiguity.

-      // Accept either ordering; just verify two occurrences of '=1'.
-      final occurrences = '=1'.allMatches(encoded).length;
-      expect(occurrences, 2);
+      expect(encoded.contains('a%5Bz%5D=1'), isTrue);
+      expect(encoded.contains('b%5Bz%5D=1'), isTrue);

171-172: Make the mutating encoder null-safe

Future changes could accidentally pass null to the encoder; avoid a null dereference.

-String _mutatingEncoder(dynamic v, {Encoding? charset, Format? format}) =>
-    'X_${v.toString()}';
+String _mutatingEncoder(dynamic v, {Encoding? charset, Format? format}) =>
+    v == null ? '' : 'X_${v.toString()}';
test/unit/encode_test.dart (2)

97-110: Filter expansion of custom objects looks good

Good coverage of the filter=function path. Optionally add a negative case demonstrating that unsupported keys on CustomObject are never accessed when the filter expands it, to guard against future regressions in filter handling.


5240-5271: Avoid redundant coverage; consider consolidating with existing tests

The added tests duplicate existing cases (allowEmptyLists, commaRoundTrip single item, cyclic RangeError). Consolidate or reference earlier tests to keep the suite lean.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4775b94 and 64bca00.

📒 Files selected for processing (2)
  • test/unit/encode_edge_cases_test.dart (1 hunks)
  • test/unit/encode_test.dart (3 hunks)
🔇 Additional comments (1)
test/unit/encode_test.dart (1)

18-23: CustomObject: clear contract for supported keys

Non-nullable operator[] with UnsupportedError for unsupported keys is appropriate and matches the updated tests.

@techouse techouse merged commit 5d75e28 into main Sep 26, 2025
14 checks passed
@techouse techouse deleted the chore/more-tests branch September 26, 2025 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants