Skip to content

test: add unit tests for DuplicateService and AutoCloseService#509

Merged
ritesh-1918 merged 19 commits into
ritesh-1918:gssocfrom
unsiqasik:feat/unit-tests-duplicate-auto-close
May 29, 2026
Merged

test: add unit tests for DuplicateService and AutoCloseService#509
ritesh-1918 merged 19 commits into
ritesh-1918:gssocfrom
unsiqasik:feat/unit-tests-duplicate-auto-close

Conversation

@unsiqasik
Copy link
Copy Markdown

@unsiqasik unsiqasik commented May 28, 2026

Summary

Adds comprehensive unit tests for two backend services:

DuplicateService (Issue #297)

  • — verifies empty store returns no match
  • — verifies custom threshold override works
  • — verifies graceful degradation when model unavailable
  • — verifies highest similarity match is returned
  • — verifies no match when below threshold

AutoCloseService (Issue #279)

  • — verifies fallback to defaults on DB error
  • — verifies DB values are returned correctly
  • — verifies successful ticket closure
  • — verifies error handling on failure
  • — verifies correct default values

Testing

  • All tests use to avoid external dependencies
  • No API keys or secrets required
  • Tests can be run with

Closes #297, Closes #279

Summary by CodeRabbit

  • Tests
    • Added comprehensive test suite for auto-close service, covering system settings retrieval, ticket closure operations, and service initialization.
    • Added test suite for duplicate detection service, validating matching logic with various thresholds, degraded mode handling, and edge cases.

Review Change Stack

namann5 and others added 19 commits May 22, 2026 11:30
- Replace raw user_id with SHA256 hash (8-char prefix) in all log statements
- Maintains audit trail capability while protecting user identifiers (PII)
- Complies with GDPR/CCPA privacy requirements
- Hash is deterministic for correlation without exposing PII

Resolves CodeRabbit PII logging concern
…backfill

Fix tenant ticket orphaning by persisting company_id on save
…ashboard

feat: Real-time Support Dashboard Updates Using Supabase Realtime Channels
- Add test_duplicate_service.py with 5 test cases:
  - test_check_duplicate_returns_no_match_when_store_empty
  - test_check_duplicate_uses_custom_threshold
  - test_check_duplicate_handles_degraded_mode
  - test_check_duplicate_returns_best_match
  - test_check_duplicate_below_threshold

- Add test_auto_close.py with 6 test cases:
  - test_get_system_settings_returns_defaults_on_error
  - test_get_system_settings_returns_db_values
  - test_close_ticket_success
  - test_close_ticket_failure
  - test_service_initialization

Closes ritesh-1918#297, Closes ritesh-1918#279
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

Someone is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Two test modules add comprehensive unit test coverage for backend services. test_auto_close.py tests AutoCloseService system settings retrieval with error fallback, ticket closure success and failure paths, and initialization defaults. test_duplicate_service.py tests DuplicateService.check_duplicate with empty store, degraded mode, threshold override, and similarity-based best-match selection logic.

Changes

AutoCloseService Test Coverage

Layer / File(s) Summary
Test setup and get_system_settings behavior
backend/tests/test_auto_close.py
Module setup with environment configuration, mocked Supabase client instantiation via setup_method, and two get_system_settings tests: one verifying default fallback when DB query raises an exception, and one verifying successful retrieval of auto_close_days and auto_close_enabled from mocked DB response.
Ticket closure and service initialization tests
backend/tests/test_auto_close.py
Tests for _close_ticket success path returning True and incrementing stats["closed_count"], failure path returning False and incrementing stats["error_count"] on DB exception, and AutoCloseService instance defaults for enabled, default_auto_close_days, and cron_schedule.

DuplicateService Test Coverage

Layer / File(s) Summary
Test setup and check_duplicate edge cases
backend/tests/test_duplicate_service.py
Module setup with ALLOW_DEGRADED_STARTUP environment variable, DuplicateService instantiation in setup_method, and tests for empty ticket store (returns no match with similarity=0.0) and degraded mode (model not loaded, returns no duplicate).
Threshold override and best-match selection tests
backend/tests/test_duplicate_service.py
Tests verifying caller-supplied threshold parameter overrides service default, best-match logic selects highest-similarity ticket ID when above threshold, and below-threshold results return is_duplicate=False with duplicate_ticket_id=None while preserving the best similarity value.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

  • #279: Adds comprehensive unit tests for auto_close_service.py covering system settings fallback behavior, ticket status update handling, and error logging as specified in the issue requirements.
  • #297: Implements full unit test suite for DuplicateService.check_duplicate with all requested test cases: empty store behavior, threshold override, and degraded mode handling.

Poem

🐰 With whiskers twitched and nose in code,
I test the close and duplicate load,
Mock databases sing, edge cases dance,
Settings and thresholds get their chance,
Now services hop with confidence!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding unit tests for two backend services (DuplicateService and AutoCloseService).
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from both linked issues: test files for DuplicateService and AutoCloseService with required test cases covering all specified scenarios.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issues; no unrelated modifications are present outside the scope of adding unit tests for the two specified services.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/tests/test_auto_close.py (2)

16-21: ⚡ Quick win

Avoid process-wide env mutation at import time.

Setting os.environ at module import can leak state into other tests. Use per-test env patching so state is isolated and automatically restored.

Proposed refactor
-# Set environment variables before importing
-os.environ["SUPABASE_URL"] = "https://test.supabase.co"
-os.environ["SUPABASE_SERVICE_ROLE_KEY"] = "test-key"
-os.environ["AUTO_CLOSE_ENABLED"] = "true"
-os.environ["AUTO_CLOSE_DAYS"] = "7"
-
 from backend.services.auto_close_service import AutoCloseService
 
 
 class TestAutoCloseService:
@@
     def setup_method(self):
         """Reset service before each test."""
+        self._env = patch.dict(os.environ, {
+            "SUPABASE_URL": "https://test.supabase.co",
+            "SUPABASE_SERVICE_ROLE_KEY": "test-key",
+            "AUTO_CLOSE_ENABLED": "true",
+            "AUTO_CLOSE_DAYS": "7",
+        })
+        self._env.start()
         with patch("backend.services.auto_close_service.create_client") as mock_client:
             self.mock_supabase = MagicMock()
             mock_client.return_value = self.mock_supabase
             self.service = AutoCloseService()
+
+    def teardown_method(self):
+        self._env.stop()
🤖 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 `@backend/tests/test_auto_close.py` around lines 16 - 21, Remove the
module-level os.environ mutations (the lines setting "SUPABASE_URL",
"SUPABASE_SERVICE_ROLE_KEY", "AUTO_CLOSE_ENABLED", "AUTO_CLOSE_DAYS") and
instead set these variables inside the test using per-test env patching (e.g.,
pytest's monkeypatch.setenv or a fixture) so state is isolated and restored;
update the test(s) in test_auto_close.py to call
monkeypatch.setenv("AUTO_CLOSE_DAYS", "7") etc. before exercising the code that
reads the env and ensure no import-time side effects remain.

64-76: ⚡ Quick win

Assert the update contract in the success-path close test.

This test currently proves only “no exception + counter increment.” It should also assert the tickets.update(...) payload and filters (id, company_id) to lock the behavior promised by _close_ticket.

Proposed assertions
     def test_close_ticket_success(self):
         """Should successfully close a ticket and update stats."""
         # Mock successful update
         mock_table = MagicMock()
         mock_table.update.return_value.eq.return_value.eq.return_value.execute.return_value = MagicMock()
         self.mock_supabase.table.return_value = mock_table
@@
         assert result is True
         assert stats["closed_count"] == 1
         assert stats["error_count"] == 0
+        self.mock_supabase.table.assert_called_once_with("tickets")
+        update_arg = mock_table.update.call_args.args[0]
+        assert update_arg["status"] == "closed"
+        assert update_arg["auto_closed"] is True
+        assert "closed_at" in update_arg
+        mock_table.update.return_value.eq.assert_any_call("id", "ticket-123")
+        mock_table.update.return_value.eq.return_value.eq.assert_any_call("company_id", "company-123")
🤖 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 `@backend/tests/test_auto_close.py` around lines 64 - 76, Update
test_close_ticket_success to assert the supabase update contract: after calling
self.service._close_ticket("ticket-123", "company-123", stats) assert that
self.mock_supabase.table(...) was used (mock_table), that mock_table.update was
called with the expected payload (the dict your _close_ticket constructs to mark
a ticket closed, e.g. status/closed_at keys), and that the chained filter calls
were invoked with the correct identifiers by asserting
mock_table.update.return_value.eq was called with ("id", "ticket-123") and
("company_id", "company-123") (and that execute was called); keep the existing
assertions for returned True and counters.
backend/tests/test_duplicate_service.py (1)

59-65: ⚡ Quick win

Make threshold assertions resilient to default-threshold changes.

The test intent is override behavior, not a specific global default value; deriving values from SIMILARITY_THRESHOLD keeps this stable if defaults are tuned.

Proposed refactor
-            mock_util.cos_sim.return_value.item.return_value = 0.75
+            score = SIMILARITY_THRESHOLD + 0.05
+            mock_util.cos_sim.return_value.item.return_value = score
@@
-            # With default threshold (0.70), 0.75 should be a duplicate
+            # With default threshold, score should be a duplicate
             result_default = self.service.check_duplicate("new ticket text")
             assert result_default["is_duplicate"] is True
 
-            # With custom threshold (0.80), 0.75 should NOT be a duplicate
-            result_custom = self.service.check_duplicate("new ticket text", threshold=0.80)
+            # With stricter custom threshold, score should NOT be a duplicate
+            result_custom = self.service.check_duplicate(
+                "new ticket text",
+                threshold=score + 0.05,
+            )
             assert result_custom["is_duplicate"] is False
🤖 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 `@backend/tests/test_duplicate_service.py` around lines 59 - 65, The test
should not hardcode numeric thresholds; import and use the module constant
SIMILARITY_THRESHOLD (or settings.SIMILARITY_THRESHOLD) to derive expectations
and the override value. Update the assertions around
self.service.check_duplicate to compute the expected duplicate result using
SIMILARITY_THRESHOLD (e.g., assert that a score just above SIMILARITY_THRESHOLD
is duplicate) and call check_duplicate with threshold=SIMILARITY_THRESHOLD +
delta (e.g., +0.05) to verify override behavior; reference the check_duplicate
method and the SIMILARITY_THRESHOLD constant when making these replacements.
🤖 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 `@backend/tests/test_duplicate_service.py`:
- Around line 14-17: The test mutates process env at import-time by setting
ALLOW_DEGRADED_STARTUP globally; instead remove that top-level os.environ change
and set the variable only for the test scope (so it is auto-restored) — e.g., in
the test use pytest's monkeypatch.setenv("ALLOW_DEGRADED_STARTUP","1") before
importing or instantiating DuplicateService (or move the import of
DuplicateService and SIMILARITY_THRESHOLD into the test after setting the env)
so the env change does not leak across the test session.

---

Nitpick comments:
In `@backend/tests/test_auto_close.py`:
- Around line 16-21: Remove the module-level os.environ mutations (the lines
setting "SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY", "AUTO_CLOSE_ENABLED",
"AUTO_CLOSE_DAYS") and instead set these variables inside the test using
per-test env patching (e.g., pytest's monkeypatch.setenv or a fixture) so state
is isolated and restored; update the test(s) in test_auto_close.py to call
monkeypatch.setenv("AUTO_CLOSE_DAYS", "7") etc. before exercising the code that
reads the env and ensure no import-time side effects remain.
- Around line 64-76: Update test_close_ticket_success to assert the supabase
update contract: after calling self.service._close_ticket("ticket-123",
"company-123", stats) assert that self.mock_supabase.table(...) was used
(mock_table), that mock_table.update was called with the expected payload (the
dict your _close_ticket constructs to mark a ticket closed, e.g.
status/closed_at keys), and that the chained filter calls were invoked with the
correct identifiers by asserting mock_table.update.return_value.eq was called
with ("id", "ticket-123") and ("company_id", "company-123") (and that execute
was called); keep the existing assertions for returned True and counters.

In `@backend/tests/test_duplicate_service.py`:
- Around line 59-65: The test should not hardcode numeric thresholds; import and
use the module constant SIMILARITY_THRESHOLD (or settings.SIMILARITY_THRESHOLD)
to derive expectations and the override value. Update the assertions around
self.service.check_duplicate to compute the expected duplicate result using
SIMILARITY_THRESHOLD (e.g., assert that a score just above SIMILARITY_THRESHOLD
is duplicate) and call check_duplicate with threshold=SIMILARITY_THRESHOLD +
delta (e.g., +0.05) to verify override behavior; reference the check_duplicate
method and the SIMILARITY_THRESHOLD constant when making these replacements.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 961fa440-f453-4719-859d-a080ae2dd9a6

📥 Commits

Reviewing files that changed from the base of the PR and between fb6a950 and 8b962fe.

📒 Files selected for processing (2)
  • backend/tests/test_auto_close.py
  • backend/tests/test_duplicate_service.py

Comment on lines +14 to +17
# Set environment variables before importing
os.environ["ALLOW_DEGRADED_STARTUP"] = "1"

from backend.services.duplicate_service import DuplicateService, SIMILARITY_THRESHOLD
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid leaking ALLOW_DEGRADED_STARTUP across the test session.

This mutates process env at import time and never restores it, which can change behavior of later tests that rely on default startup mode.

Proposed fix
 import os
 import pytest
 from unittest.mock import patch, MagicMock
 
 # Set environment variables before importing
+_PREV_ALLOW_DEGRADED = os.environ.get("ALLOW_DEGRADED_STARTUP")
 os.environ["ALLOW_DEGRADED_STARTUP"] = "1"
 
 from backend.services.duplicate_service import DuplicateService, SIMILARITY_THRESHOLD
+
+
+def teardown_module():
+    if _PREV_ALLOW_DEGRADED is None:
+        os.environ.pop("ALLOW_DEGRADED_STARTUP", None)
+    else:
+        os.environ["ALLOW_DEGRADED_STARTUP"] = _PREV_ALLOW_DEGRADED
🤖 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 `@backend/tests/test_duplicate_service.py` around lines 14 - 17, The test
mutates process env at import-time by setting ALLOW_DEGRADED_STARTUP globally;
instead remove that top-level os.environ change and set the variable only for
the test scope (so it is auto-restored) — e.g., in the test use pytest's
monkeypatch.setenv("ALLOW_DEGRADED_STARTUP","1") before importing or
instantiating DuplicateService (or move the import of DuplicateService and
SIMILARITY_THRESHOLD into the test after setting the env) so the env change does
not leak across the test session.

@ritesh-1918 ritesh-1918 changed the base branch from main to gssoc May 29, 2026 19:28
@ritesh-1918 ritesh-1918 added the gssoc GirlScript Summer of Code label May 29, 2026
@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented May 29, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
29368972 Triggered Supabase Service Role JWT b460068 scratch/test_companies.js View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@ritesh-1918 ritesh-1918 added gssoc:approved GSSoC Approved PR level:critical Critical level difficulty quality:exceptional Exceptional code quality type:testing Testing suites, mock coverages, CI/CD integrations labels May 29, 2026
@ritesh-1918
Copy link
Copy Markdown
Owner

Hi @unsiqasik! Thanks for the contribution. I have successfully converted your PR's target branch to gssoc to keep our codebase unified.

PR approved and merged! Welcome to the family! 🚀💻


🌟 Developer Action Network

Before starting or submitting updates, please complete these quick onboarding steps:

  1. Star this repository: https://github.com/ritesh-1918/HELPDESK.AI
  2. 👤 Follow the Project Admin: https://github.com/ritesh-1918
  3. 💼 Connect on LinkedIn: https://www.linkedin.com/in/ritesh1908/

Note: All PR branches must target the gssoc branch, NOT main.

@ritesh-1918 ritesh-1918 added the type:feature New feature label May 29, 2026
@ritesh-1918 ritesh-1918 merged commit 818a1cc into ritesh-1918:gssoc May 29, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC Approved PR gssoc GirlScript Summer of Code level:critical Critical level difficulty quality:exceptional Exceptional code quality type:feature New feature type:testing Testing suites, mock coverages, CI/CD integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test : add unit tests for DuplicateService check_duplicate method test : add unit tests for auto_close_service.py utility

4 participants