Skip to content

test(ats): add Vitest unit tests for AtsService with Gemini API mocking#882

Open
Abhushan187 wants to merge 1 commit into
Sachinchaurasiya360:mainfrom
Abhushan187:feat/ats-vitest-unit-tests
Open

test(ats): add Vitest unit tests for AtsService with Gemini API mocking#882
Abhushan187 wants to merge 1 commit into
Sachinchaurasiya360:mainfrom
Abhushan187:feat/ats-vitest-unit-tests

Conversation

@Abhushan187
Copy link
Copy Markdown

@Abhushan187 Abhushan187 commented May 31, 2026

Changes

  • server/src/module/ats/__tests__/ats.service.test.ts — 22 unit tests across 3 suites

Coverage

Method Cases covered
scoreResume User not found, IDOR guard, cache hit, URL normalization, short PDF, happy path (no JD), happy path (with JD), S3 fetch, local file fetch, invalid URL, unparseable AI, markdown-wrapped JSON, trailing commas, score clamping, missing categoryScores fallback, suggestions cap
getScoreHistory Oldest-first ordering, empty array, studentId filter, take:30 limit
applySuggestions User not found, IDOR guard, short PDF, happy path, prompt content, missing latex tag fallback

Technical Notes

  • All external calls mocked via vi.mock(): Prisma, S3 utils, AI provider registry, pdf-parse, fs/promises
  • Zero real DB/S3/Gemini calls — safe to run in CI without credentials
  • Uses existing vitest.config.ts in server/ — no config changes needed
  • Establishes __tests__/ pattern other ATS sub-services can follow

Closes #738

Summary by CodeRabbit

  • Tests
    • Added comprehensive test coverage for resume scoring functionality, score history retrieval, and suggestion application with validation of edge cases and data constraints.

@github-actions github-actions Bot added quality:clean Clean and well-structured contribution level:advanced Complex implementation or logic gssoc:approved Approved for GSSoC scoring labels May 31, 2026
@github-actions
Copy link
Copy Markdown

Hi @Abhushan187, thanks for contributing to InternHack! 🎉

I have automatically:

  • 👤 Assigned this PR to you.
  • 🏷️ Applied the gssoc:approved label.

Our workflows will now analyze your changes to classify:

  • 📈 PR Difficulty: level:*
  • 🧩 PR Type: type:*
  • 🌟 PR Quality: quality:*

Tip

Ensure your PR description references the issue it resolves (e.g. Closes #123). This allows the bot to inherit any additional labels from that issue!

Happy coding! 🚀

@github-actions github-actions Bot added type:feature New feature implementation type:testing Adds or improves tests labels May 31, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a comprehensive Vitest test suite for AtsService covering scoreResume, getScoreHistory, and applySuggestions methods. Tests validate user/ownership guards, cache behavior, PDF parsing, AI response parsing, output constraints, and edge cases using fully mocked Prisma, S3, AI provider, and filesystem dependencies.

Changes

AtsService Test Suite

Layer / File(s) Summary
Test setup, mocks, and fixtures
server/src/module/ats/__tests__/ats.service.test.ts
Imports Vitest, service modules, and dependencies; defines hoisted mocks for Prisma DB, S3 helpers, AI provider registry, AI logger, pdf-parse, and fs; creates test fixtures (student/resume IDs, resume text, valid AI JSON, mock DB rows) and helper functions; initializes safe mock defaults in beforeEach.
scoreResume method validation
server/src/module/ats/__tests__/ats.service.test.ts
Tests scoreResume across user/ownership failures, cache-hit short-circuiting, presigned URL query stripping, PDF extraction failure, new DB row creation on cache miss, job context prompt enrichment, S3/local /uploads/ buffer retrieval, invalid URL rejection, AI output parsing (markdown JSON, trailing commas), and constraints (overallScore ≤ 100, categoryScores default to 50, suggestions ≤ 10).
getScoreHistory method validation
server/src/module/ats/__tests__/ats.service.test.ts
Tests getScoreHistory for oldest-first ordering, empty array behavior, studentId filtering, and max query size enforcement (30 rows).
applySuggestions method validation
server/src/module/ats/__tests__/ats.service.test.ts
Tests applySuggestions for ownership guards, PDF extraction failure handling, <reply> and <latex> tag extraction, suggestion inclusion in prompts, and fallback when <latex> is absent.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Sachinchaurasiya360/InternHack#335: The new Vitest suite directly verifies the AtsService.applySuggestions behavior introduced in this PR, including ownership/IDOR guards, PDF extraction failure handling, and <reply>/<latex> parsing.

Suggested labels

quality:clean, level:intermediate

Poem

🐰 A burrow of tests, so snug and tight,
Mocking the API through day and night!
Scores and suggestions flow through the suite,
Each edge case handled—no surprises, so neat! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the primary change: adding comprehensive Vitest unit tests for AtsService with Gemini API mocking.
Description check ✅ Passed The description provides coverage tables, technical notes, and implementation details that align with the PR template structure, though it omits formal template sections.
Linked Issues check ✅ Passed The test suite comprehensively addresses all coding requirements from issue #738: covers happy paths, edge cases, error handling, uses Vitest with vi.mock(), and aligns with existing vitest.config.ts.
Out of Scope Changes check ✅ Passed All changes are directly scoped to testing the ATS service as required; no unrelated modifications or configuration changes are present.

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

🧹 Nitpick comments (1)
server/src/module/ats/__tests__/ats.service.test.ts (1)

184-190: ⚡ Quick win

Deduplicate repeated PDFParse mock setup using existing helper.

Both tests reimplement the same mock shape inline. Reuse mockValidPdf(text) to keep one source of truth and reduce drift.

♻️ Proposed refactor
-      vi.mocked(PDFParse).mockImplementation(
-        () =>
-          ({
-            getText: vi.fn().mockResolvedValue({ text: "too short" }),
-            destroy: vi.fn().mockResolvedValue(undefined),
-          }) as any,
-      );
+      mockValidPdf("too short");
-      vi.mocked(PDFParse).mockImplementation(
-        () =>
-          ({
-            getText: vi.fn().mockResolvedValue({ text: "tiny" }),
-            destroy: vi.fn().mockResolvedValue(undefined),
-          }) as any,
-      );
+      mockValidPdf("tiny");

As per coding guidelines, "Apply DRY principle: no duplicate helpers, shared animation variants per file".

Also applies to: 467-473

🤖 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 `@server/src/module/ats/__tests__/ats.service.test.ts` around lines 184 - 190,
Replace the inline PDFParse mock implementations with the existing test helper
mockValidPdf(text): locate the inline vi.mocked(PDFParse).mockImplementation
blocks in ats.service.test.ts and call mockValidPdf("too short") (or other text)
instead of re-creating getText/destroy mocks; do the same for the second
occurrence that duplicates the same shape so both tests use the single
mockValidPdf helper.
🤖 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 `@server/src/module/ats/__tests__/ats.service.test.ts`:
- Around line 184-190: Replace the inline PDFParse mock implementations with the
existing test helper mockValidPdf(text): locate the inline
vi.mocked(PDFParse).mockImplementation blocks in ats.service.test.ts and call
mockValidPdf("too short") (or other text) instead of re-creating getText/destroy
mocks; do the same for the second occurrence that duplicates the same shape so
both tests use the single mockValidPdf helper.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7db48798-846d-4a9b-87e3-4c1969c7af13

📥 Commits

Reviewing files that changed from the base of the PR and between 37e199d and a9cdf54.

📒 Files selected for processing (1)
  • server/src/module/ats/__tests__/ats.service.test.ts

Copy link
Copy Markdown
Owner

@Sachinchaurasiya360 Sachinchaurasiya360 left a comment

Choose a reason for hiding this comment

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

Review

Verdict: Approve

Great coverage — the safe-defaults beforeEach pattern and the IDOR guard test are highlights. A few things to tighten before merge:

Issues

1. PDFParse mock may not intercept the real call path
The import is import { PDFParse } from "pdf-parse". Verify this matches how pdf-parse v2 actually exports the constructor. If the real module uses a default export, the mock will never intercept it and the test silently passes for the wrong reason.

2. Missing newline at end of file
The last line of the test file has \ No newline at end of file. Add a trailing newline to avoid diff noise on future edits.

3. "throws when AI returns completely unparseable text" has no message assertion
.rejects.toThrow() with no argument passes on any thrown error, including a mock misconfiguration. Pin the expected message (e.g. "Failed to parse ATS score") so the assertion actually validates service behaviour.

4. No test for the cache TTL boundary
The cache-hit test mocks findFirst to always return a row, but there is no test confirming that a row older than the TTL is treated as a cache miss. If TTL logic lives in the service, it should be covered.

5. "falls back to raw AI text when <latex> tag is absent" only asserts toBeTruthy()
This accepts any non-empty string. Pin the expected fallback value (e.g. the raw AI text) so a regression in the fallback path is actually caught.

@Sachinchaurasiya360
Copy link
Copy Markdown
Owner

fix the lint and build

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Approved for GSSoC scoring level:advanced Complex implementation or logic quality:clean Clean and well-structured contribution type:feature New feature implementation type:testing Adds or improves tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add Vitest unit tests for ATS resume scoring service with Gemini API mocking

2 participants