Skip to content

Comments

Add Jest unit and integration tests for utils, controller, and routes#352

Open
Sran012 wants to merge 3 commits intozhravan:mainfrom
Sran012:main
Open

Add Jest unit and integration tests for utils, controller, and routes#352
Sran012 wants to merge 3 commits intozhravan:mainfrom
Sran012:main

Conversation

@Sran012
Copy link

@Sran012 Sran012 commented Sep 22, 2025

  1. Adds focused tests for utils, controller, and an integration path for GET /quote (service mocked)
  2. No production code changes in this PR
  3. How to run: npm install && npm test && npm run test:coverage
  4. Status: All tests pass locally (4 suites, 7 tests)

Summary by CodeRabbit

  • New Features
    • No user-facing features added.
  • Tests
    • Added unit and integration tests covering the quotes endpoint, network requests, URL validation, and test environment setup.
  • Chores
    • Introduced Jest-based test tooling, scripts (test, watch, coverage), and test environment initialization for local/CI use.
    • Relaxed the frontend build tool version constraint to allow compatible updates.
  • Style
    • Minor whitespace tidy-up with no functional impact.

@vercel
Copy link

vercel bot commented Sep 22, 2025

@Sran012 is attempting to deploy a commit to the shravan20's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

Adds Jest testing infrastructure and multiple tests, updates devDependencies and npm test scripts, changes frontend react-scripts spec to a caret range, and makes one async change in quotesService plus a whitespace edit in src/app.js.

Changes

Cohort / File(s) Summary of changes
Testing configuration & tooling
jest.config.js, package.json (scripts, devDependencies), tests/setup.js
Added a Jest config, test scripts (test, test:watch, test:coverage), devDependencies (jest, supertest, @types/jest), and test setup that sets NODE_ENV=test and mocks console methods.
Unit & integration tests
tests/api/controllers/quotesController.test.js, tests/integration/app.routes.test.js, tests/utils/fetchApi.test.js, tests/utils/validateUrl.test.js
Added controller, integration, and utility tests covering /quote response headers/body, fetchApi success/error behavior, and URL validation (including GitHub blob→raw transformation).
Service async change
src/api/services/quotesService.js
cardTemplate.generateTemplate(template) call is now awaited, making SVG generation asynchronous within getQuote.
Frontend dependency spec
frontend/package.json
Relaxed react-scripts version from "5.0.1" to "^5.0.1".
Whitespace-only edit
src/app.js
Inserted blank lines; no behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant QuotesService
  participant CardTemplate

  Client->>QuotesService: request getQuote(params)
  Note right of QuotesService #D3E4CD: validate inputs, prepare template
  QuotesService->>CardTemplate: generateTemplate(template)
  activate CardTemplate
  CardTemplate-->>QuotesService: (promise) SVG string
  deactivate CardTemplate
  QuotesService-->>Client: return SVG
  Note left of Client #F0F4FF: response contains image/svg+xml headers
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my whiskers, tests take flight,
Jest hums softly into the night.
SVGs hop from async dreams,
URLs streaming raw in beams.
A patch of code — a carrot bite 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately and concisely reflects the primary change: adding Jest unit and integration tests for utilities, the quote controller, and route handling, which aligns with the PR objectives and the raw_summary that lists new test files, Jest config, and test scripts. It is a single clear sentence that avoids noise or ambiguous wording and is useful for teammates scanning history. No misleading or unrelated content is present in the title.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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.

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 (10)
package.json (1)

27-30: Add a CI-friendly test script.
Consider a dedicated CI script to avoid watch mode defaults and ensure a non-interactive run with coverage.

   "scripts": {
+    "test:ci": "jest --ci --coverage --reporters=default --reporters=jest-junit",
tests/setup.js (1)

2-11: Restore console after tests to avoid leaking mocks across environments.
Overriding global.console is fine; add teardown to reinstate originals so other tooling isn’t affected.

-process.env.NODE_ENV = 'test';
-
-global.console = {
-  ...console,
-  log: jest.fn(),
-  debug: jest.fn(),
-  info: jest.fn(),
-  warn: jest.fn(),
-  error: jest.fn(),
-};
+process.env.NODE_ENV = 'test';
+
+const originalConsole = { ...console };
+global.console = {
+  ...console,
+  log: jest.fn(),
+  debug: jest.fn(),
+  info: jest.fn(),
+  warn: jest.fn(),
+  error: jest.fn(),
+};
+
+afterAll(() => {
+  // Restore the original console to avoid affecting other tools/runners
+  // that may execute after Jest in the same process.
+  // eslint-disable-next-line no-global-assign
+  global.console = originalConsole;
+});
tests/integration/app.routes.test.js (2)

13-20: Silence HTTP logs in tests.
morgan writes to stdout and can spam CI logs. No-op it in tests.

+jest.mock('morgan', () => () => (_req, _res, next) => next());
 ...
-  const morgan = require('morgan');
   const routes = require('../../src/api/routes/quotes-router');
   const cors = require('cors');
   app.use(cors());
   app.use(bodyParser.json());
   app.use(bodyParser.urlencoded({ extended: false }));
-  app.use(morgan('dev'));
+  // morgan is mocked above to a no-op

24-31: Also assert cache headers to mirror controller contract.
Add header checks to catch regressions end-to-end.

     const res = await request(server).get('/quote');
     expect(res.statusCode).toBe(200);
     expect(res.headers['content-type']).toMatch(/image\/svg\+xml/);
+    expect(res.headers['cache-control']).toBe('no-cache,max-age=0,no-store,s-maxage=0,proxy-revalidate');
+    expect(res.headers['pragma']).toBe('no-cache');
+    expect(res.headers['expires']).toBe('-1');
     const body = res.text ?? (Buffer.isBuffer(res.body) ? res.body.toString() : undefined);
     expect(body).toBe('<svg>integration</svg>');
tests/api/controllers/quotesController.test.js (1)

15-27: Add call assertion and error-path coverage.
Strengthen the test by verifying the service is invoked and by covering the catch branch.

   it('sets headers and sends SVG response', async () => {
     const req = { query: {} };
     const res = createMockRes();

     await quoteController(req, res);

+    const { getQuote } = require('../../../src/api/services/quotesService');
+    expect(getQuote).toHaveBeenCalledTimes(1);
     expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'image/svg+xml');
     expect(res.header).toHaveBeenCalledWith('Cache-Control', 'no-cache,max-age=0,no-store,s-maxage=0,proxy-revalidate');
     expect(res.header).toHaveBeenCalledWith('Pragma', 'no-cache');
     expect(res.header).toHaveBeenCalledWith('Expires', '-1');
     expect(res.send).toHaveBeenCalledWith('<svg>mock</svg>');
   });
+
+  it('handles service errors', async () => {
+    const { getQuote } = require('../../../src/api/services/quotesService');
+    getQuote.mockRejectedValueOnce(new Error('boom'));
+    const req = { query: {} };
+    const res = createMockRes();
+    await quoteController(req, res);
+    expect(res.send).toHaveBeenCalledWith(expect.objectContaining({ name: expect.any(String), message: 'boom' }));
+  });
tests/utils/validateUrl.test.js (1)

1-21: Nice coverage of happy-path and GitHub blob transform.
Consider an extra case to ensure raw GitHub URLs are left unchanged.

 describe('getValidUrl', () => {
@@
   it('transforms github blob URL to raw URL', async () => {
     const res = await getValidUrl('https://github.com/user/repo/blob/main/file.json');
     expect(res.isValidUrl).toBe(true);
     expect(res.customQuotesUrl).toBe('https://raw.githubusercontent.com/user/repo/main/file.json');
   });
+
+  it('keeps raw.githubusercontent.com URLs unchanged', async () => {
+    const url = 'https://raw.githubusercontent.com/user/repo/main/file.json';
+    const res = await getValidUrl(url);
+    expect(res.isValidUrl).toBe(true);
+    expect(res.customQuotesUrl).toBe(url);
+  });
 });
tests/utils/fetchApi.test.js (2)

11-16: Relax the call expectation to avoid coupling to undefined options.
Future changes may pass {} or headers; keep the assertion tolerant.

   it('returns parsed json on success', async () => {
     fetch.mockResolvedValue({ json: async () => ({ ok: true }) });
     const res = await fetchApi('https://example.com');
     expect(res).toEqual({ ok: true });
-    expect(fetch).toHaveBeenCalledWith('https://example.com', undefined);
+    expect(fetch).toHaveBeenCalledWith('https://example.com', expect.anything());
   });

18-22: Optional: cover JSON parse failure.
Add a test to ensure errors from response.json propagate as rejections.

   it('throws on fetch error', async () => {
     const err = new Error('network');
     fetch.mockRejectedValue(err);
     await expect(fetchApi('https://example.com')).rejects.toThrow('network');
   });
+
+  it('throws when response.json fails', async () => {
+    fetch.mockResolvedValue({ json: async () => { throw new Error('bad json'); } });
+    await expect(fetchApi('https://example.com')).rejects.toThrow('bad json');
+  });
jest.config.js (2)

3-3: Simplify testMatch to avoid redundant patterns and future TS/JSX support.

-  testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
+  testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],

2-12: Consider default mock hygiene to reduce test flakiness.

   testEnvironment: 'node',
+  clearMocks: true,
+  restoreMocks: true,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d88dde and 148423c.

⛔ Files ignored due to path filters (2)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • frontend/package.json (1 hunks)
  • jest.config.js (1 hunks)
  • package.json (1 hunks)
  • src/app.js (1 hunks)
  • tests/api/controllers/quotesController.test.js (1 hunks)
  • tests/integration/app.routes.test.js (1 hunks)
  • tests/setup.js (1 hunks)
  • tests/utils/fetchApi.test.js (1 hunks)
  • tests/utils/validateUrl.test.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/api/controllers/quotesController.test.js (1)
src/api/controllers/quotesController.js (1)
  • quoteController (7-74)
tests/integration/app.routes.test.js (1)
src/app.js (6)
  • express (1-1)
  • app (12-12)
  • bodyParser (4-4)
  • morgan (5-5)
  • routes (3-3)
  • cors (9-9)
🔇 Additional comments (9)
src/app.js (1)

31-33: No-op whitespace change — OK to merge.
No behavioral impact; routing and Swagger wiring remain unchanged.

package.json (1)

17-21: Jest + Supertest tooling — LGTM.
Dev dependencies are appropriate for the test suites added in this PR.

tests/integration/app.routes.test.js (1)

1-6: Good: service-level mock keeps the test hermetic.
Mocking quotesService.getQuote at module level ensures deterministic SVG output.

tests/api/controllers/quotesController.test.js (1)

1-5: Unit isolation looks solid.
Controller is tested with the service mocked; good focus on headers and payload.

frontend/package.json (1)

14-14: Resolved — lockfile pins react-scripts@5.0.1
frontend/package-lock.json shows react-scripts resolved to 5.0.1.

jest.config.js (4)

11-11: LGTM — setupFilesAfterEnv path present and conventional.
tests/setup.js present.


1-1: Approved — keep jest.config.js as CommonJS

package.json "type" is "commonjs", so jest.config.js using module.exports is correct; no change needed.


13-13: Incorrect — keep in testPathIgnorePatterns.
Jest supports the token in testPathIgnorePatterns, so the existing '/frontend/' pattern is valid and more robust than '/frontend/'. (jestjs.io)

Likely an incorrect or invalid review comment.


4-8: Adjust collectCoverageFrom to match this repo — don’t add controllers/routes/utils (they don’t exist).

jest.config.js currently only collects 'src/**/*.js' and excludes tests only under src; the repo has no controllers/, routes/, or utils/ directories, contains server.js at the repo root, and frontend/ is explicitly ignored by testPathIgnorePatterns. Update coverage to include actual top-level/server files and exclude test files globally.

File: jest.config.js (collectCoverageFrom) — suggested replacement:
collectCoverageFrom: [
'server.js',
'src//*.js',
'!
/.test.js',
'!**/
.spec.js'
],

Likely an incorrect or invalid review comment.

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 (1)
src/api/services/quotesService.js (1)

61-62: Inline return to drop the temporary.

Minor tidy-up; same behavior and still caught by the try/catch.

-    let svg = await cardTemplate.generateTemplate(template);
-    return svg;
+    return await cardTemplate.generateTemplate(template);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 148423c and c5e5105.

📒 Files selected for processing (1)
  • src/api/services/quotesService.js (1 hunks)
🔇 Additional comments (1)
src/api/services/quotesService.js (1)

61-61: Awaiting template generation is correct — confirm non-test change was intentional.

generateTemplate is async (src/utils/generateTemplate.js) and getQuote is awaited by the controller (src/api/controllers/quotesController.js:54); awaiting here lets the surrounding try/catch surface rejections and does not break callers. Confirm this production change was intended in a tests-only PR.

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