Skip to content

fix(pdf): reject empty PDFs with 0 pages instead of returning success - #780

Merged
murdore merged 1 commit into
juspay:releasefrom
y-naaz:fix/pdf-015-empty-pdf-check
Jan 15, 2026
Merged

murdore merged 1 commit into
juspay:releasefrom
y-naaz:fix/pdf-015-empty-pdf-check

Conversation

@y-naaz

@y-naaz y-naaz commented Jan 15, 2026 •

Copy link
Copy Markdown

Description

What does this PR do?

Adds validation to detect and reject PDFs with 0 pages during image conversion, providing clear error feedback instead of silently returning an empty result.

Related Issues

Fixes #335

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Motivation and Context

Why is this change needed? What problem does it solve?

Previously, when PDFProcessor.convertToImages() processed a PDF with 0 pages, the page iteration loop would never execute, resulting in an empty array being returned with a success message. This was confusing for users who expected either valid results or a clear error.

Impact of the bug:

  • Empty PDFs appeared to succeed but returned no results
  • Confusing for users expecting an error
  • Wasted API calls
  • No clear feedback about the problem

Changes Made

  • Added zero-page check after PDF page iteration in convertToImages() method
  • Throws clear error: "PDF has 0 pages. Cannot convert empty PDF to images."
  • Added comprehensive test coverage for empty PDF handling

Files modified:

  • src/lib/utils/pdfProcessor.ts - Added empty PDF validation
  • test/unit/utils/pdfProcessor.test.ts - Added 3 new test cases

Breaking Changes

  • No breaking changes

Testing

  • Unit tests added/updated
  • Existing tests pass

Test Coverage:

  • All new code is covered by tests
  • Existing tests pass

New tests added:

  1. should throw error for PDF with 0 pages - Verifies rejection of empty PDFs
  2. should succeed for PDF with at least 1 page - Verifies normal PDFs still work
  3. should provide clear error message mentioning 0 pages - Verifies error message is actionable

Manual Testing Steps:

  1. Run pnpm test -- --run test/unit/utils/pdfProcessor.test.ts
  2. Verify all 12 tests pass (3 new + 9 existing)

Code Quality

  • Code follows the project's style guidelines (ESLint passes)
  • Code is properly formatted (Prettier applied)
  • Self-review of code completed
  • TypeScript strict mode compliance
  • Proper error handling implemented

Documentation

  • JSDoc comments added/updated for public APIs (existing docs sufficient)

Commit Message Format

  • Commit message follows format: type(scope): description

Summary by CodeRabbit

  • Bug Fixes

    • Added validation to prevent processing of empty PDFs, displaying a clear error message when a PDF contains zero pages.
  • Tests

    • Expanded test coverage with new scenarios for empty PDF validation and single-page PDF handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Added a runtime validation in PDF conversion to reject empty PDFs by throwing "PDF has 0 pages. Cannot convert empty PDF to images." after page conversion. Added unit tests that mock page conversion for 0- and 1-page PDFs and validate error messaging.

Changes

Cohort / File(s) Summary
PDF Conversion Validation
src/lib/utils/pdfProcessor.ts
After converting pages to images, check if resulting images array is empty and throw error "PDF has 0 pages. Cannot convert empty PDF to images." to reject empty PDFs.
Empty PDF Test Suite
test/unit/utils/pdfProcessor.test.ts
Added tests mocking pdf-to-img to simulate empty and single-page PDFs; asserts thrown error message for 0 pages and correct behavior for 1 page; uses runtime module mocking and re-imports.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Caller as Caller
participant Processor as PDFProcessor
participant Converter as pdf-to-img
Caller->>Processor: request convertToImages(pdfBuffer)
Processor->>Converter: iterate pages -> yield images
alt images yielded
Converter-->>Processor: image pages
Processor->>Processor: collect images
Processor-->>Caller: return { images, pageCount }
else no pages yielded
Converter-->>Processor: no pages
Processor->>Caller: throw "PDF has 0 pages. Cannot convert empty PDF to images."
end

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

released

Suggested reviewers

  • murdore

Poem

🐇 I hopped through bytes and pages thin,
Found zero leaves — and gave a grin.
"No pages here!" I loudly cheer,
Tests now guard what I hold dear. 📄✨

🚥 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 validation to reject empty PDFs instead of silently returning success.
Linked Issues check ✅ Passed The PR implementation fully satisfies all objectives from issue #335: zero-page check added, correct error message thrown, comprehensive test coverage added, and no regressions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #335 requirements: empty PDF validation, error handling, and corresponding test coverage with no extraneous modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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.

@y-naaz

y-naaz commented Jan 15, 2026

Copy link
Copy Markdown
Author

@coderabbitai Full review and check if the changes are according to the issue #335

@y-naaz

y-naaz commented Jan 15, 2026

Copy link
Copy Markdown
Author

@coderabbitai help

@coderabbitai

coderabbitai Bot commented Jan 15, 2026

Copy link
Copy Markdown

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai

coderabbitai Bot commented Jan 15, 2026

Copy link
Copy Markdown
🧩 Analysis chain

@y-naaz, I'll fetch issue #335 to verify the requirements and then perform a complete review of the changes.


🏁 Script executed:

#!/bin/bash
# Fetch issue `#335` to understand the requirements
gh issue view 335 --json title,body,labels,state,url

Length of output: 1846


🏁 Script executed:

#!/bin/bash
# Fetch the implementation to verify it matches issue requirements
cat src/lib/utils/pdfProcessor.ts | head -300

Length of output: 8432


🏁 Script executed:

#!/bin/bash
# Get more of the file, particularly the convertToImages method around line 264
cat src/lib/utils/pdfProcessor.ts | sed -n '240,310p'

Length of output: 2315


🏁 Script executed:

#!/bin/bash
# Get the convertToImages method implementation
cat src/lib/utils/pdfProcessor.ts | sed -n '300,380p'

Length of output: 2873


🏁 Script executed:

#!/bin/bash
# Get the rest of the convertToImages method to see where the empty check is
cat src/lib/utils/pdfProcessor.ts | sed -n '370,450p'

Length of output: 2374


🏁 Script executed:

#!/bin/bash
# Check the test file to verify test coverage
cat test/unit/utils/pdfProcessor.test.ts

Length of output: 7626


@y-naaz, I've reviewed PR #780 against issue #335. Here's my analysis:

✅ Alignment with Issue #335

The implementation successfully addresses all requirements from issue #335:

Requirements Met:

  1. Zero Page Check: ✅ Added at line 417-419 in pdfProcessor.ts
  2. Error Message: ✅ Exact match - "PDF has 0 pages. Cannot convert empty PDF to images."
  3. Test Coverage: ✅ Comprehensive tests added covering:
    • PDF with 0 pages throws error
    • PDF with 1+ pages succeeds
    • Clear error messaging verification

Implementation Approach:

Note on Location Difference:
The issue suggested checking pdfDocument.numPages immediately after loading (line 264), but the implementation checks images.length === 0 after the conversion loop (line 417). This is actually a better approach because:

  • The pdf-to-img library returns an async iterator, not a document object with a numPages property
  • Checking after the loop is more defensive - it catches PDFs that claim to have pages but produce no output
  • Functionally equivalent: if PDF has 0 pages → loop doesn't execute → images.length === 0 → error thrown

Code Quality Observations:

Strengths:

  • Error message is clear and actionable
  • Validation happens before logging success metrics
  • Existing error handling captures and transforms the error message correctly
  • Tests use proper mocking to simulate 0-page scenarios

Test Coverage:
The tests are thorough and include:

  • Happy path (1+ pages)
  • Error path (0 pages)
  • Error message validation
  • Integration with existing format validation tests

Verdict: ✅ Implementation Correctly Addresses Issue #335

All acceptance criteria are satisfied. The PR is ready for merge pending full automated review.

✅ Actions performed

Full review triggered.

@y-naaz

y-naaz commented Jan 15, 2026

Copy link
Copy Markdown
Author

@murdore Please review and merge

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/lib/utils/pdfProcessor.ts`:
- Around line 414-417: Replace the plain throw new Error(...) in the PDF page
check with ErrorFactory.invalidParameters to follow project error patterns:
where images.length === 0 currently throws, construct and throw
ErrorFactory.invalidParameters("pdf-processor", new Error("PDF has 0 pages.
Cannot convert empty PDF to images.")); locate this in the pdfProcessor.ts
function that processes PDF pages and ensure the new ErrorFactory call mirrors
the message and type used elsewhere (or add a dedicated
ErrorFactory.pdfProcessing(...) helper if you expect reuse).

In `@test/unit/utils/pdfProcessor.test.ts`:
- Around line 6-31: Add vi.resetModules() before calling vi.doMock() in this
test to clear the module cache so importing
"../../../src/lib/utils/pdfProcessor.js" picks up the mocked "pdf-to-img";
specifically, insert vi.resetModules() immediately before
vi.doMock("pdf-to-img", ...) in the test that imports MockedPDFProcessor and
asserts on MockedPDFProcessor.convertToImages, and apply the same change to the
other two tests in this suite that use vi.doMock()/vi.doUnmock() so each test
re-imports a fresh module instance.
🧹 Nitpick comments (1)
test/unit/utils/pdfProcessor.test.ts (1)

58-85: Test validates error message clarity as required.

This test fulfills the acceptance criterion: "Error message explicitly mentions 0 pages and is actionable." The try/catch pattern works, though you could also use rejects.toThrow() combined with checking the error object:

♻️ Alternative using rejects pattern
     it("should provide clear error message mentioning 0 pages", async () => {
       // ... setup code ...

-      try {
-        await MockedPDFProcessor.convertToImages(validPdfHeader);
-        expect.fail("Should have thrown an error");
-      } catch (error) {
-        const errorMessage = (error as Error).message;
-        expect(errorMessage).toContain("0 pages");
-        expect(errorMessage).toContain("empty PDF");
-      }
+      await expect(
+        MockedPDFProcessor.convertToImages(validPdfHeader),
+      ).rejects.toSatisfy((error: Error) => {
+        expect(error.message).toContain("0 pages");
+        expect(error.message).toContain("empty PDF");
+        return true;
+      });

       vi.doUnmock("pdf-to-img");
     });
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5c785f and df3c9c9.

📒 Files selected for processing (2)
  • src/lib/utils/pdfProcessor.ts
  • test/unit/utils/pdfProcessor.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/lib/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/lib/**/*.ts: Use MessageBuilder (src/lib/utils/messageBuilder.ts) as the central component for constructing all messages with multimodal support (text, images, PDFs, CSV)
Wrap async operations with withTimeout utility for timeout handling
Use ErrorFactory for creating typed errors instead of generic Error objects
thinkingLevel option supports "minimal", "low", "medium" (default), and "high" values for extended thinking on supported models (Anthropic Claude, Gemini 2.5+, Gemini 3)

Files:

  • src/lib/utils/pdfProcessor.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain strict TypeScript type safety across all modules with no any types unless justified

Files:

  • src/lib/utils/pdfProcessor.ts
🧠 Learnings (1)
📚 Learning: 2025-08-19T05:02:34.555Z
Learnt from: coder-dodo
Repo: juspay/neurolink PR: 87
File: test/provider-edge-cases.test.ts:16-21
Timestamp: 2025-08-19T05:02:34.555Z
Learning: When using vi.mock() in Vitest, mocking the same import path that contains the system under test replaces the actual implementation with a mock, even if the physical file has a different extension (.ts vs .js). TypeScript's NodeNext module resolution treats .js imports as references to .ts files.

Applied to files:

  • test/unit/utils/pdfProcessor.test.ts
🧬 Code graph analysis (1)
test/unit/utils/pdfProcessor.test.ts (1)
src/lib/utils/logger.ts (1)
  • error (239-241)
🔇 Additional comments (1)
test/unit/utils/pdfProcessor.test.ts (1)

33-56: LGTM!

Good positive test case verifying that a 1-page PDF succeeds. The assertions for pageCount and images.length properly validate the conversion result.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +414 to +417
// Check for empty PDF (0 pages)
if (images.length === 0) {
throw new Error("PDF has 0 pages. Cannot convert empty PDF to images.");
}

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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if ErrorFactory exists and how it's used
echo "=== Checking for ErrorFactory usage ==="
rg -n "ErrorFactory" --type ts -A 3

echo ""
echo "=== Checking file path and content around the target lines ==="
if [ -f "src/lib/utils/pdfProcessor.ts" ]; then
  echo "File exists, checking content around lines 414-417:"
  sed -n '410,445p' src/lib/utils/pdfProcessor.ts | cat -n
else
  echo "File not found, searching for pdfProcessor:"
  fd pdfProcessor
fi

Repository: juspay/neurolink

Length of output: 15532


🏁 Script executed:

# Check error patterns in src/lib/utils
echo "=== Errors in src/lib/utils with ErrorFactory vs generic Error ==="
rg -n "throw" src/lib/utils/ --type ts -B 1 -A 1 | head -50

Repository: juspay/neurolink

Length of output: 2533


🏁 Script executed:

# Search for all methods in ErrorFactory to see available options
echo "=== ErrorFactory method definitions ==="
sed -n '117,400p' src/lib/utils/errorHandling.ts | grep -E "^\s+(static\s+)?[a-zA-Z]+\(" | head -30

Repository: juspay/neurolink

Length of output: 717


🏁 Script executed:

# Check if there's a generic processingError or conversionError method
rg -n "conversionError|processingError|fileProcessing" src/lib/utils/errorHandling.ts

Repository: juspay/neurolink

Length of output: 42


Replace generic Error with ErrorFactory to comply with coding guidelines.

The throw new Error() on line 416 should use ErrorFactory instead, per the coding guidelines for src/lib/**/*.ts. The most appropriate option is ErrorFactory.invalidParameters("pdf-processor", new Error(...)) to match the validation pattern used elsewhere in the codebase. Alternatively, consider adding a dedicated PDF processing error method to ErrorFactory if PDF-specific error handling is expected to be reused.

The error re-wrapping in the catch block (line 434) is intentional and correct—the original message is preserved within the new context.

🤖 Prompt for AI Agents
In `@src/lib/utils/pdfProcessor.ts` around lines 414 - 417, Replace the plain
throw new Error(...) in the PDF page check with ErrorFactory.invalidParameters
to follow project error patterns: where images.length === 0 currently throws,
construct and throw ErrorFactory.invalidParameters("pdf-processor", new
Error("PDF has 0 pages. Cannot convert empty PDF to images.")); locate this in
the pdfProcessor.ts function that processes PDF pages and ensure the new
ErrorFactory call mirrors the message and type used elsewhere (or add a
dedicated ErrorFactory.pdfProcessing(...) helper if you expect reuse).

Comment on lines +6 to +31
it("should throw error for PDF with 0 pages", async () => {
// Create a valid PDF header buffer that will pass initial validation
const validPdfHeader = Buffer.from(
"%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Count 0/Kids[]>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\ntrailer<</Size 3/Root 1 0 R>>\nstartxref\n150\n%%EOF",
);

// Mock pdf-to-img to return an empty async iterator (0 pages)
vi.doMock("pdf-to-img", () => ({
pdf: vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
// Empty iterator - no pages
},
}),
}));

// Re-import to get the mocked version
const { PDFProcessor: MockedPDFProcessor } = await import(
"../../../src/lib/utils/pdfProcessor.js"
);

await expect(
MockedPDFProcessor.convertToImages(validPdfHeader),
).rejects.toThrow("PDF has 0 pages. Cannot convert empty PDF to images.");

vi.doUnmock("pdf-to-img");
});

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 | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Vitest vi.doMock vi.resetModules module isolation best practices

💡 Result:

Short best-practices for module isolation with vi.doMock / vi.resetModules (Vitest)

  1. Know hoisting vs non-hoisting
  • vi.mock is hoisted (runs before imports). Use it when you want the module mocked for all imports in the file. (v3.vitest.dev)
  1. Use vi.doMock when you need the mock to run at runtime
  • vi.doMock is NOT hoisted and will only affect subsequent dynamic imports. Use it when the mock needs to reference variables defined in the test scope. (vitest.dev)
  1. Reset module cache to isolate module-local state
  • vi.resetModules() clears the module cache so subsequent dynamic imports are re-evaluated; call it in beforeEach or inside vi.hoisted/setup to ensure fresh modules per test. Top-level static imports cannot be re-evaluated. (vitest.dev)
  1. Reset mocks separate from module cache
  • vi.resetModules() does NOT clear mock registry. Use vi.unmock / vi.doUnmock or vi.resetAllMocks()/vi.restoreAllMocks() as appropriate to clear mocks between tests. (vitest.dev)
  1. Prefer dynamic import after reset/mock when you need isolation
  • Pattern: vi.resetModules(); vi.doMock(...) (if non-hoisted mocking needed); then await import('./module') so the import picks up the intended mocked or fresh implementation. (vitest.dev)
  1. Avoid importing the target module earlier (including in setup files)
  • Modules imported in setup files or earlier in the file will be cached and not affected by later doMock/resetModules; if you mock in setup, ensure you manage cache (vi.resetModules()) or avoid pre-importing. Real-world reports show unexpected failures when setup imports block later mocks. (v1.vitest.dev)
  1. For Jest parity / advanced isolation
  • Jest’s isolateModules/isolateModulesAsync provides per-callback registries; Vitest relies on resetModules + dynamic imports and doMock/hoisting semantics instead. Use the dynamic-import pattern for similar isolation. (jestjs.io)

Minimal example pattern

  • beforeEach(() => { vi.resetModules(); vi.resetAllMocks(); })
  • vi.doMock('./dep', () => ({ fn: vi.fn(()=>'x') })) // if non-hoisted needed
  • const mod = await import('./module') // will see mocked dep / fresh module. (vitest.dev)

If you want, I can convert these into copy-paste test examples (Common pitfalls + working examples).

Citations:


Add vi.resetModules() to ensure proper test isolation with dynamic mocks.

The test correctly verifies the exact error message for the 0-page case. However, vi.doUnmock() clears only the mock registry, not the module cache. Without vi.resetModules(), subsequent tests may reuse a cached module from the previous test rather than reimporting with the new mock applied. Add vi.resetModules() before each vi.doMock() to ensure each test gets a fresh module instance.

🔧 Required fix for test isolation
     it("should throw error for PDF with 0 pages", async () => {
       // Create a valid PDF header buffer that will pass initial validation
       const validPdfHeader = Buffer.from(
         "%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Count 0/Kids[]>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\ntrailer<</Size 3/Root 1 0 R>>\nstartxref\n150\n%%EOF",
       );

+      // Reset module registry to ensure clean state
+      vi.resetModules();
+
       // Mock pdf-to-img to return an empty async iterator (0 pages)
       vi.doMock("pdf-to-img", () => ({

Apply the same pattern to the other two tests in this suite.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("should throw error for PDF with 0 pages", async () => {
// Create a valid PDF header buffer that will pass initial validation
const validPdfHeader = Buffer.from(
"%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Count 0/Kids[]>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\ntrailer<</Size 3/Root 1 0 R>>\nstartxref\n150\n%%EOF",
);
// Mock pdf-to-img to return an empty async iterator (0 pages)
vi.doMock("pdf-to-img", () => ({
pdf: vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
// Empty iterator - no pages
},
}),
}));
// Re-import to get the mocked version
const { PDFProcessor: MockedPDFProcessor } = await import(
"../../../src/lib/utils/pdfProcessor.js"
);
await expect(
MockedPDFProcessor.convertToImages(validPdfHeader),
).rejects.toThrow("PDF has 0 pages. Cannot convert empty PDF to images.");
vi.doUnmock("pdf-to-img");
});
it("should throw error for PDF with 0 pages", async () => {
// Create a valid PDF header buffer that will pass initial validation
const validPdfHeader = Buffer.from(
"%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Count 0/Kids[]>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\ntrailer<</Size 3/Root 1 0 R>>\nstartxref\n150\n%%EOF",
);
// Reset module registry to ensure clean state
vi.resetModules();
// Mock pdf-to-img to return an empty async iterator (0 pages)
vi.doMock("pdf-to-img", () => ({
pdf: vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
// Empty iterator - no pages
},
}),
}));
// Re-import to get the mocked version
const { PDFProcessor: MockedPDFProcessor } = await import(
"../../../src/lib/utils/pdfProcessor.js"
);
await expect(
MockedPDFProcessor.convertToImages(validPdfHeader),
).rejects.toThrow("PDF has 0 pages. Cannot convert empty PDF to images.");
vi.doUnmock("pdf-to-img");
});
🤖 Prompt for AI Agents
In `@test/unit/utils/pdfProcessor.test.ts` around lines 6 - 31, Add
vi.resetModules() before calling vi.doMock() in this test to clear the module
cache so importing "../../../src/lib/utils/pdfProcessor.js" picks up the mocked
"pdf-to-img"; specifically, insert vi.resetModules() immediately before
vi.doMock("pdf-to-img", ...) in the test that imports MockedPDFProcessor and
asserts on MockedPDFProcessor.convertToImages, and apply the same change to the
other two tests in this suite that use vi.doMock()/vi.doUnmock() so each test
re-imports a fresh module instance.

@murdore
murdore force-pushed the fix/pdf-015-empty-pdf-check branch from df3c9c9 to 717ef2a Compare January 15, 2026 20:15
@murdore
murdore merged commit 92d8d4e into juspay:release Jan 15, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 8.35.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

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.

PDF-015: Empty PDF Not Caught

2 participants