Skip to content

Conversation

@Abhaytomar2004
Copy link

@Abhaytomar2004 Abhaytomar2004 commented Oct 19, 2025

This PR adds comprehensive unit tests for the occurrences utility function in src/utils/occurrences.ts. The function counts the number of times a substring appears in a string, including overlapping occurrences using regex lookahead with pattern caching for performance.

Changes Made
Created src/utils/occurrences.spec.ts with 20 comprehensive test cases

Covered basic counting functionality, edge cases, and overlapping patterns

Added property-based tests using fast-check for robust validation

Verified handling of special characters and regex escaping

Ensured all tests pass successfully

Summary by CodeRabbit

  • Tests
    • Added comprehensive test suite for the occurrences function covering basic counting, overlapping patterns, special-character/escaping behavior, Unicode, long strings and other edge cases.
    • Introduced property-based tests to validate correctness across diverse inputs and ensure counts remain consistent under varied scenarios.

@changeset-bot
Copy link

changeset-bot bot commented Oct 19, 2025

⚠️ No Changeset found

Latest commit: 606c40a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new comprehensive test suite for the occurrences utility, covering basic counts, overlapping patterns, special-character/regex escaping, property-based checks with fast-check, and various edge cases including Unicode and long strings.

Changes

Cohort / File(s) Summary
Test suite for occurrences utility
src/utils/occurrences.spec.ts
New test file using vitest and lifecycle hooks. Mocks escapeRegExp. Contains sections for basic counting (single/multiple chars, case sensitivity), overlapping patterns, special characters and regex escaping, property-based tests with fast-check (non-negative counts, bounds, empty-substring behavior, etc.), and edge cases (very long strings, Unicode, whitespace, not-found).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰
I count the hops in every line,
Overlaps and brackets all align,
Fast-check dreams and Unicode cheer,
Tests snug in burrows, bright and clear,
A rabbit's nod — the code is here. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add unit tests for occurrences utility function #507" directly and clearly describes the main change in the pull request: adding a comprehensive test suite for the occurrences utility function. The title is specific, concise, and accurately reflects the changeset's primary objective of introducing src/utils/occurrences.spec.ts with 20 test cases covering various scenarios. The title is not vague, misleading, or overly broad—it provides clear information about what was changed without unnecessary details.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Abhaytomar2004, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the reliability and correctness of the occurrences utility function by introducing a comprehensive suite of unit tests. The tests validate the function's behavior across a wide range of scenarios, from basic string matching to complex overlapping patterns and special character handling, ensuring its robustness and preventing future regressions.

Highlights

  • New Test File Added: A new unit test file, src/utils/occurrences.spec.ts, has been added to provide comprehensive test coverage for the occurrences utility function.
  • Comprehensive Test Coverage: The new test suite includes over 20 test cases covering basic counting, handling of empty strings and substrings, case sensitivity, and complex overlapping patterns.
  • Regex Special Character Handling: Tests specifically verify the correct handling and escaping of regex special characters, ensuring the occurrences function behaves as expected with various patterns.
  • Property-Based Testing: Robustness is enhanced through the inclusion of property-based tests using fast-check, validating fundamental properties like non-negative counts, substring length constraints, and behavior with empty substrings.
  • Edge Case Validation: The tests also cover various edge cases, including very long strings, unicode characters, whitespace patterns, and scenarios where the substring is not found.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds a comprehensive set of unit tests for the occurrences utility function. The tests are well-structured and cover many edge cases, including the use of property-based testing, which is excellent. My main feedback is to improve the testing strategy by removing the mock of the escapeRegExp function. Mocking it by re-implementing it makes the tests brittle. Instead, I suggest testing against the real implementation and adding a test to verify the caching behavior using spies. This will make the test suite more robust and reliable.

Comment on lines 13 to 19
beforeEach(() => {
vi.mocked(escapeRegExp).mockClear();
});

afterEach(() => {
vi.clearAllMocks();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

With the mock removed, these hooks are no longer correct. We only need to ensure spies are restored after each test.

Suggested change
beforeEach(() => {
vi.mocked(escapeRegExp).mockClear();
});
afterEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});

Comment on lines 74 to 77
it('should call escapeRegExp for patterns', () => {
occurrences('test', 'test');
expect(escapeRegExp).toHaveBeenCalled();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test was for the mock. Now that we're using the real implementation, we can write a more valuable test to verify the caching behavior, which is a key performance feature of the occurrences function.

    it('should use cache for repeated patterns', () => {
      const escapeSpy = vi.spyOn(escapeRegExpModule, 'escapeRegExp');
      occurrences('test string', 't');
      expect(escapeSpy).toHaveBeenCalledTimes(1);

      occurrences('another test', 't');
      expect(escapeSpy).toHaveBeenCalledTimes(1); // Should not be called again for the same substring
    });

Copy link
Contributor

@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 (4)
src/utils/occurrences.spec.ts (4)

6-10: Consider removing the mock or using a spy instead.

The mock duplicates the real escapeRegExp implementation exactly, which defeats the purpose of mocking. Since the test at lines 74-77 verifies that escapeRegExp is called, consider using vi.spyOn to spy on the real implementation instead:

-vi.mock('./escapeRegExp', () => ({
-  escapeRegExp: vi.fn((str: string) => {
-    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
-  })
-}));
+vi.spyOn(await import('./escapeRegExp'), 'escapeRegExp');

Alternatively, if you're testing the integration between occurrences and escapeRegExp, remove the mock entirely.


13-19: Simplify mock lifecycle management.

Both beforeEach and afterEach are clearing mocks, which is redundant. Choose one approach:

  • Use only beforeEach with vi.clearAllMocks() to ensure clean state before each test, or
  • Use only afterEach with vi.clearAllMocks() to clean up after tests.

The current setup with both is unnecessarily verbose.


74-77: Reconsider testing implementation detail.

This test verifies that escapeRegExp is called (implementation detail) rather than testing observable behavior. The tests at lines 65-72 already validate that special characters are handled correctly, which is the important behavior.

Consider removing this test or replacing it with a behavior-focused assertion. Implementation-detail tests are fragile and make refactoring harder.


1-165: Consider adding tests for pattern cache behavior.

The occurrences function uses PATTERN_CACHE for performance optimization (see src/utils/occurrences.ts lines 11-13), but there are no tests verifying cache behavior. Consider adding tests to validate:

  • Patterns are cached on first use
  • Subsequent calls with the same substring reuse cached patterns
  • Cache behavior doesn't cause issues with different strings but same substring

Example test:

it('should cache patterns for reuse', () => {
  vi.mocked(escapeRegExp).mockClear();
  occurrences('test1', 'test');
  expect(escapeRegExp).toHaveBeenCalledTimes(1);
  
  occurrences('test2', 'test');
  expect(escapeRegExp).toHaveBeenCalledTimes(1); // Should not call again
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc4fc57 and c52269f.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • web-csv-toolbox-wasm/pkg/web_csv_toolbox_wasm_bg.wasm is excluded by !**/*.wasm
📒 Files selected for processing (1)
  • src/utils/occurrences.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/utils/occurrences.spec.ts (2)
src/utils/escapeRegExp.ts (1)
  • escapeRegExp (8-10)
src/utils/occurrences.ts (1)
  • occurrences (11-20)
🔇 Additional comments (4)
src/utils/occurrences.spec.ts (4)

21-49: LGTM! Comprehensive basic functionality coverage.

The tests correctly validate:

  • Single and multiple character counting
  • Empty string edge cases (including the subtle occurrences('', '') = 1)
  • Empty substring behavior (matching at every position)
  • Case sensitivity

All assertions are accurate based on the function's regex lookahead implementation.


51-72: Excellent coverage of overlapping patterns and special character escaping.

These tests effectively validate the function's core features:

  • Overlapping occurrences using regex lookahead ('aaa'/'aa' = 2, 'abababa'/'aba' = 3)
  • Proper escaping of regex metacharacters (., *, +, ?, ^, $)

The test cases are well-chosen and comprehensive.


93-139: Excellent property-based testing with fast-check.

These property tests validate important invariants:

  • Non-negativity of counts
  • Correct handling when substring exceeds string length (proper use of fc.pre)
  • Upper bound validation for non-empty substrings
  • Empty substring edge case
  • Self-search identity property

The properties are mathematically sound and provide broad validation across random inputs. Great use of property-based testing!


141-164: Comprehensive edge case coverage.

The edge cases validate:

  • Performance with long strings (1000+ chars)
  • Unicode handling including emojis and multi-byte characters
  • Whitespace patterns (single, double, multiple spaces)
  • Not-found scenarios including length mismatches

These tests ensure the function handles real-world edge cases correctly.

Copy link
Owner

@kamiazya kamiazya left a comment

Choose a reason for hiding this comment

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

Please remove Package-lock.json and web-csv-toolbox-wasm/pkg/web_csv_toolbox_wasm_bg.wasm as they are unrelated to this pull request.

@kamiazya
Copy link
Owner

@Abhaytomar2004
I'm not in a rush, but I'm mentioning you in case you haven't noticed the comment.

@Abhaytomar2004
Copy link
Author

@Abhaytomar2004 I'm not in a rush, but I'm mentioning you in case you haven't noticed the comment.

Thanks for reminding , will be reverting the changes you mention earlier , in the latest commit .

Copy link
Contributor

@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

♻️ Duplicate comments (2)
src/utils/occurrences.spec.ts (2)

4-4: Good switch to module import for spying.

Importing the module namespace enables reliable vi.spyOn without brittle mocks. Nice.


1-1: Remove redundant beforeEach restore; keep only afterEach.

Restoring twice is unnecessary. Trim the hook and its import.

-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
+import { describe, it, expect, afterEach, vi } from 'vitest';
@@
-describe('occurrences', () => {
-  beforeEach(() => {
-    vi.restoreAllMocks();
-  });
+describe('occurrences', () => {
@@
   afterEach(() => {
     vi.restoreAllMocks();
   });

Also applies to: 7-13

🧹 Nitpick comments (3)
src/utils/occurrences.spec.ts (3)

68-75: Harden cache-behavior test against accidental pre-population.

Use a unique key and assert the called argument; avoids flakiness if someone adds a prior test using 't'.

   it('should use cache for repeated patterns', () => {
     const escapeSpy = vi.spyOn(escapeRegExpModule, 'escapeRegExp');
-    occurrences('test string', 't');
-    expect(escapeSpy).toHaveBeenCalledTimes(1);
+    const key = '__CACHE_KEY__';
+    occurrences('test string', key);
+    expect(escapeSpy).toHaveBeenCalledTimes(1);
+    expect(escapeSpy).toHaveBeenLastCalledWith(key);
 
-    occurrences('another test', 't');
+    occurrences('another test', key);
     expect(escapeSpy).toHaveBeenCalledTimes(1);
   });

Optional: for full isolation, reset modules and re-import occurrences within this test before spying.

// vi.resetModules(); const { occurrences } = await import('./occurrences');

58-66: Add a couple more regex metacharacters for completeness.

Cover pipe and closing brackets to fully pin escaping behavior.

   it('should handle regex special characters by escaping them', () => {
     expect(occurrences('a.b.c', '.')).toBe(2);
     expect(occurrences('a*b*c', '*')).toBe(2);
     expect(occurrences('a+b+c', '+')).toBe(2);
     expect(occurrences('a?b?c', '?')).toBe(2);
     expect(occurrences('a^b$c', '^')).toBe(1);
     expect(occurrences('a$b$c', '$')).toBe(2);
+    expect(occurrences('a|b|c', '|')).toBe(2);
   });
@@
   it('should handle parentheses and brackets', () => {
     expect(occurrences('a(b)c', '(')).toBe(1);
     expect(occurrences('a)b)c', ')')).toBe(2);
     expect(occurrences('a[b]c', '[')).toBe(1);
     expect(occurrences('a{b}c', '{')).toBe(1);
+    expect(occurrences('a[b]c', ']')).toBe(1);
+    expect(occurrences('a{b}c', '}')).toBe(1);
   });

Also applies to: 77-82


101-109: Prefer generator construction over fc.pre to avoid discarded runs.

Build a strictly-longer substring directly; reduces shrink/discard noise and speeds up CI.

-    it('property: if substring is longer than string, result should be 0', () => {
-      fc.assert(
-        fc.property(fc.string(), fc.string(), (str, substr) => {
-          fc.pre(substr.length > str.length && substr.length > 0);
-          const count = occurrences(str, substr);
-          expect(count).toBe(0);
-        })
-      );
-    });
+    it('property: if substring is longer than string, result should be 0', () => {
+      fc.assert(
+        fc.property(
+          fc.string(),                      // str
+          fc.string({ minLength: 1 }),      // suffix to make it longer
+          (str, suffix) => {
+            const substr = str + suffix;
+            expect(occurrences(str, substr)).toBe(0);
+          }
+        )
+      );
+    });

Optional: add { numRuns: 200 } (and a fixed seed) to fc.assert for reproducibility in CI.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c52269f and 606c40a.

⛔ Files ignored due to path filters (1)
  • web-csv-toolbox-wasm/pkg/web_csv_toolbox_wasm_bg.wasm is excluded by !**/*.wasm
📒 Files selected for processing (1)
  • src/utils/occurrences.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/utils/occurrences.spec.ts (1)
src/utils/occurrences.ts (1)
  • occurrences (11-20)
🔇 Additional comments (1)
src/utils/occurrences.spec.ts (1)

6-163: Overall: strong, thorough test suite.

Good coverage of overlaps, empty substrings, escaping, unicode, and PBT. Aside from minor nits above, this is ready.

@Abhaytomar2004
Copy link
Author

@kamiazya I've removed the unrelated files as requested. All tests are passing and the PR is ready for your review.

Copy link
Owner

Choose a reason for hiding this comment

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

@Abhaytomar2004
It's also unrelated, please remove it too.

Copy link
Owner

Choose a reason for hiding this comment

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

@Abhaytomar2004 Hi, just a reminder, since it's last day of the Hactoberfest.

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.

2 participants