Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 135 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# 🔑 KeyCV

This repository houses the backend service for the KeyCV application, designed to automate and improve parts of the job application process. The frontend (if in a separate repository) would consume this backend's APIs.
This repository houses the backend service for the KeyCV application,
designed to automate and improve parts of the job application process.
The frontend (if in a separate repository) would consume this backend's APIs.

## Backend Service Overview

The backend service is built with Node.js, Express, and TypeScript. Its primary responsibilities include processing job application data and providing API endpoints for the frontend.
The backend service is built with Node.js, Express, and TypeScript. Its primary responsibilities
include processing job application data and providing API endpoints for the frontend.

## Tech Stack (Backend)

Expand Down Expand Up @@ -44,7 +47,8 @@ npm install

### 4. Environment Variables

Environment variables are crucial for configuring our application. We use a `.env.example` file as a template to define all necessary variables.
Environment variables are crucial for configuring our application.
We use a `.env.example` file as a template to define all necessary variables.

For local development, create a `.env.local` file in the `backend/` directory:

Expand All @@ -55,7 +59,8 @@ cp .env.example .env.local
Then, open `.env.local` and fill in the required environment variables specific to your local setup.

> 🚨 **Warning:** </br>
> **Ensure this file is never committed to Git, as it contains sensitive information.** (It is already ignored by `.gitignore`). On Render, add these variables via the dashboard.
> **Ensure this file is never committed to Git, as it contains sensitive information.**
(It is already ignored by `.gitignore`). On Render, add these variables via the dashboard.

### 5. Run the Development Server

Expand All @@ -65,7 +70,9 @@ To start the backend server in development mode with live reloading:
npm run dev
```

The `npm run dev` command uses `nodemon` to watch for changes in your source files (`src/`). It will automatically recompile your TypeScript code and restart the server, loading environment variables from `.env.local`.
The `npm run dev` command uses `nodemon` to watch for changes in your source files (`src/`). It will
automatically recompile your TypeScript code and restart the server, loading environment variables
from `.env.local`.

> **Note:**</br>
> The server will typically run on `http://localhost:3000`.
Expand All @@ -92,12 +99,126 @@ npm start

These scripts are run from within the `backend/` directory.

- `npm run dev`: Starts the backend server in development mode with live reloading. It automatically recompiles TypeScript changes and loads environment variables from `.env.local`.
- `npm run build`: Compiles TypeScript to JavaScript for the backend, creating production-ready files in the `dist/` directory.
- `npm run dev`: Starts the backend server in development mode with live reloading.
It automatically recompiles TypeScript changes and loads environment variables from `.env.local`.
- `npm run build`: Compiles TypeScript to JavaScript for the backend, creating production-ready
files in the `dist/` directory.

- `npm start`: Starts the compiled production backend server.
- `npm run test`: Runs tests (currently no tests configured for the backend).
- `npm test`: Runs the complete test suite using Vitest.
- `npm run test:watch`: Runs tests in watch mode for development.
- `npm run test:ui`: Opens the Vitest UI for interactive testing.
- `npm run test:coverage`: Generates test coverage reports.

For contribution guidelines, including our Git branching model and commit message conventions,
please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) guide.

## Testing

We use **Vitest** as our testing framework, providing a fast and modern testing experience
with TypeScript support.

### Test Structure

Our test suite is organized into three main categories:

1. **Unit Tests** (`src/**/*.test.ts`)
- Service layer tests (LLM service, business logic)
- Utility function tests
- Individual component testing

2. **Integration Tests** (`src/controllers/*.test.ts`)
- API endpoint testing
- Controller logic with mocked services
- Request/response validation

3. **Basic Tests** (`src/__tests__/*.test.ts`)
- Smoke tests
- Environment validation
- Configuration checks

### Running Tests

```bash
# Run all tests once
npm test

# Run tests in watch mode (re-runs on file changes)
npm run test:watch

# Open interactive UI for test exploration
npm run test:ui

# Generate coverage report
npm run test:coverage
```

### Test Configuration

- **Framework:** Vitest v4.0.15
- **Environment:** Node.js
- **Coverage Tool:** v8
- **Mocking:** vi (Vitest's built-in mocking utilities)
- **Globals:** Enabled (no need to import describe, it, expect)

For contribution guidelines, including our Git branching model and commit message conventions, please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) guide.
### Coverage Thresholds

We maintain minimum coverage thresholds:

- **Lines:** 70%
- **Functions:** 70%
- **Branches:** 70%
- **Statements:** 70%

### Test Results

![Test Results](./docs/vitest_passed.png)

> All 28 tests passing across 3 test suites*

### Writing Tests

When adding new features, please include corresponding tests:

```typescript
import { describe, it, expect, beforeEach, vi } from "vitest";

describe("FeatureName", () => {
beforeEach(() => {
// Setup code
});

it("should do something expected", () => {
// Arrange
const input = "test";

// Act
const result = yourFunction(input);

// Assert
expect(result).toBe("expected");
});
});
```

### Mocking External Dependencies

We mock external services (like the Anthropic API) to ensure fast, reliable tests:

```typescript
// Mock the Anthropic SDK
const { mockCreate } = vi.hoisted(() => ({
mockCreate: vi.fn(),
}));

vi.mock("@anthropic-ai/sdk", () => ({
default: vi.fn(function() {
return {
messages: { create: mockCreate },
};
}),
}));
```

## Deployment

Expand All @@ -117,10 +238,12 @@ You can access the live application after Render finishes provisioning and expos

## Project Documentation

All detailed project documentation has been moved to the `docs/` directory to keep the root directory clean.
All detailed project documentation has been moved to the `docs/` directory keeping root directory clean.

- **[Project Guidelines](docs/GUIDELINES.md):** High-level project goals, tech stack, and project management approach.
- **[Deployment Guide](docs/deployment.md):** Detailed instructions for deploying the backend service on Render.
- **[Project Guidelines](docs/GUIDELINES.md):** High-level project goals, tech stack, and project
management approach.
- **[Deployment Guide](docs/deployment.md):** Detailed instructions for deploying the backend service
at Render.
- **[Project Plan Gantt Chart](docs/gantt.html):** A visual representation of our project plan.
- **[AI Toolkit](docs/AI_Toolkit.md):** Suggestions for leveraging AI to enhance the application's capabilities.

Expand Down
2 changes: 1 addition & 1 deletion backend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default [
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
project: "./tsconfig.eslint.json",
},
globals: {
console: "readonly",
Expand Down
13 changes: 13 additions & 0 deletions backend/src/__tests__/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="vitest/globals" />
import { describe, it, expect } from "vitest";

describe("Basic Test", () => {
it("should pass a simple test", () => {
expect(1 + 1).toBe(2);
});

it("should handle async tests", async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
});
30 changes: 30 additions & 0 deletions backend/src/__tests__/fixtures/resumes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export const SAMPLE_RESUME_TEXT = `
John Doe
Software Engineer

EXPERIENCE
Senior Developer at TechCorp (2020-2024)
- Built scalable microservices with Node.js and TypeScript
- Led team of 5 developers
- Improved performance by 40%

SKILLS
JavaScript, React, Node.js, PostgreSQL
`.trim();

export const SAMPLE_JOB_DESCRIPTION = `
Senior Full-Stack Engineer

Requirements:
- 5+ years experience with TypeScript
- React and Node.js expertise
- PostgreSQL or similar database
- Leadership experience preferred
`.trim();

export const createMockPdfBuffer = (): Buffer => {
// Minimal PDF with text
return Buffer.from(
"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\n2 0 obj\n<<\n/Type /Pages\n/Kids [3 0 R]\n/Count 1\n>>\nendobj\n3 0 obj\n<<\n/Type /Page\n/Parent 2 0 R\n/Contents 4 0 R\n>>\nendobj\n4 0 obj\n<<\n/Length 44\n>>\nstream\nBT\n/F1 12 Tf\n100 700 Td\n(Test Resume) Tj\nET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\n0000000115 00000 n\n0000000214 00000 n\ntrailer\n<<\n/Size 5\n/Root 1 0 R\n>>\nstartxref\n338\n%%EOF",
);
};
24 changes: 24 additions & 0 deletions backend/src/__tests__/mocks/anthropic.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { vi } from "vitest";
import type Anthropic from "@anthropic-ai/sdk";

export const createMockAnthropicResponse = (jsonContent: unknown) => ({
content: [
{
type: "text" as const,
text: JSON.stringify(jsonContent),
},
],
});

export const createMockAnthropicClient = () => {
const mockCreate = vi.fn();

return {
client: {
messages: {
create: mockCreate,
},
} as unknown as Anthropic,
mockCreate,
};
};
30 changes: 30 additions & 0 deletions backend/src/__tests__/mocks/express.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { vi } from "vitest";
import type { Request, Response } from "express";

export const createMockRequest = (
overrides: Partial<Request> = {},
): Partial<Request> => ({
body: {},
params: {},
query: {},
headers: {},
...overrides,
});

export const createMockResponse = (): {
res: Partial<Response>;
statusMock: ReturnType<typeof vi.fn>;
jsonMock: ReturnType<typeof vi.fn>;
} => {
const jsonMock = vi.fn();
const statusMock = vi.fn().mockReturnValue({ json: jsonMock });

const res: Partial<Response> = {
status: statusMock,
json: jsonMock,
send: vi.fn(),
sendStatus: vi.fn(),
};

return { res, statusMock, jsonMock };
};
18 changes: 18 additions & 0 deletions backend/src/__tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { beforeAll, afterAll, afterEach, vi } from "vitest";

// Set test environment variables
beforeAll(() => {
process.env.NODE_ENV = "test";
process.env.ANTHROPIC_API_KEY = "test-api-key-" + Math.random();
});

// Clean up after each test
afterEach(() => {
vi.clearAllMocks();
vi.restoreAllMocks();
});

// Final cleanup
afterAll(() => {
delete process.env.ANTHROPIC_API_KEY;
});
Loading