An intelligent API service that generates and validates marketing content using AI. Built with TypeScript, Express, Supabase, and Google Cloud Vertex AI.
- Getting Started
- Client Application
- API Documentation
- Style Checker
- Tools & Testing
- Bugs & Challenges Encountered
- AI Citations
Before you begin, ensure you have the following installed and configured:
- Node.js (v16 or higher)
- npm (comes with Node.js)
- Supabase Account - For database and authentication
- Google Cloud Platform Account - With Vertex AI API enabled
- Clone the repository:
git clone https://github.com/ldang04/team-ditto.git
cd team-ditto- Install dependencies:
npm install- Create environment file:
Create a .env file in the project root with the following variables:
# Supabase Configuration
SUPABASE_URL=your-supabase-project-url
SUPABASE_SERVICE_KEY=your-supabase-service-role-key
# Google Cloud Platform Configuration
GCP_PROJECT_ID=your-gcp-project-id
VERTEX_MODEL_TEXT=gemini-2.5-flash-lite
# Server Configuration (optional)
PORT=3000- Set up Google Cloud credentials:
Place your GCP service account JSON file in the project root as gcp-service-account.json (this file is gitignored for security).
Alternatively, authenticate using:
gcloud auth application-default login- Set up Supabase database:
Your Supabase database should have the following tables:
clients- Client organizationsapi_keys- API keys for authenticationprojects- Marketing projectsthemes- Brand themescontents- Generated contentembeddings- Vector embeddings for content validation
See the Supabase schema in your project dashboard or contact your team for the schema file.
Compile TypeScript to JavaScript:
npm run buildThis creates compiled JavaScript files in the dist/ directory.
This project uses GitHub Actions for continuous integration. The CI workflow automatically runs on every push and pull request.
The CI workflow (.github/workflows/ci.yml) automatically:
- Checks out your code from the repository
- Sets up Node.js (version 20) with npm caching
- Authenticates to Google Cloud for Vertex AI access
- Installs dependencies using
npm ci(clean install) - Type checks TypeScript code (
npm run typecheck) - Builds the TypeScript code (
npm run build) - Runs ESLint for style checking (
npm run lint) - Runs static analysis for bug detection (
npm run analyze) - Runs unit tests with coverage (
npm run test:unit) - Uploads artifacts (test results, coverage reports, analysis reports)
- Go to your GitHub repository
- Click the "Actions" tab
- Select a workflow run to see detailed results
- Click on individual steps to see logs
- Download artifacts to view test reports, coverage, and static analysis results
To simulate what CI does locally, run these commands in order:
# Install dependencies (clean install, like CI)
npm ci
# Type checking
npm run typecheck
# Build
npm run build
# Style checking
npm run lint
# Static analysis
npm run analyze
# Unit tests with coverage
npm run test:unitAll configuration files required for CI are included in the repository:
.github/workflows/ci.yml- CI workflow definition.github/workflows/deploy.yml- CD workflow for Cloud Run deploymentjest.config.ts- Jest test configurationtsconfig.json- TypeScript configurationeslint.config.mjs- ESLint configurationpackage.json- Dependencies and scripts
The CI workflow requires these secrets to be set in GitHub (Settings → Secrets and variables → Actions):
SUPABASE_URL- Your Supabase project URLSUPABASE_SERVICE_KEY- Your Supabase service role keyGCP_PROJECT_ID- Your Google Cloud project IDGCP_SERVICE_ACCOUNT_KEY- GCP service account JSON key for Vertex AI access
Note: Without these secrets, CI will fail when running tests that require database or AI services.
Also Note: API tests (Postman/Newman) are not run in CI as they require a running server. Run them manually using npm run api:test after starting the server locally.
NOTE: how you can see that the CI does indeed run the static bug analyzer, and the stylechecker (ESLint).
Then you can see with this later check from the CI loop (Testing #57/PR #88) that we did fix all errors/warnings that were generated from the static bug analyzer, and the stylechecker:
npm startThis runs the server using ts-node-dev which automatically reloads on file changes.
npm run build
npm run start:prodThe server will start on http://localhost:3000 (or the PORT specified in your .env file).
curl http://localhost:3000/api/vertex-test# Run all unit tests
npm test
# Run with coverage report
npm run test:unit
# Run with JUnit XML output (for CI/CD)
npm run test:unit:junitNote: The server must be running for API tests to work.
In one terminal:
npm startIn another terminal:
npm run api:testLogging Verification: Our service uses the Winston logger to record both informational and error logs for every API request.
How it works:
- All incoming API requests are logged - calls logger.info() with the request method, URL, and payload.
- Controller-level errors are logged using logger.error() along with the stack trace.
- Winston writes logs to both the console (for live debugging) and to persistent log files under the
logs/directory.
File output (persistent):
logs/combined.log→ contains all info, warn, and error logs.logs/error.log→ contains only error level logs for easier debugging.
API tests trigger logging in the server. To verify logging works:
- Watch the first terminal (where
npm startis running) during API tests - You should see
console.logoutput for errors and debugging - Each endpoint call generates log entries showing the service is functioning
- Every successful endpoint call → recorded in
combined.log. - Every failed or exception-throwing call → recorded in both
combined.loganderror.log.
npm run reports:allThis generates:
- Unit test results
- Coverage reports
- API test results
- Coverage summary
team-ditto/
├── src/ # API service source code
│ ├── controllers/ # Request handlers
│ ├── models/ # Database models
│ ├── routes/ # API routes
│ ├── services/ # Business logic (e.g., EmbeddingService)
│ ├── middleware/ # Authentication middleware
│ ├── config/ # Configuration (Supabase client)
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ ├── app.ts # Express app configuration
│ └── index.ts # Server entry point
├── client/ # Client application (React)
│ ├── src/ # Client source code
│ ├── package.json # Client dependencies
│ └── README.md # Client documentation
├── tests/ # Test files
├── postman/ # Postman collection and environment
├── reports/ # Generated test reports
├── dist/ # Compiled JavaScript (gitignored)
├── coverage/ # Coverage reports (gitignored)
├── __mocks__/ # Jest mocks for testing
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── jest.config.ts # Jest configuration
├── eslint.config.js # ESLint configuration
└── README.md # This file
The client application code is located in the client/ directory of this repository. This is a React-based web application that provides a user interface for interacting with the Ditto Content API.
LinkLaunch is a LinkedIn marketing campaign builder that helps professionals create engaging LinkedIn posts with AI-generated text and images. The client application:
- Provides a user-friendly interface for creating and managing marketing campaigns
- Generates LinkedIn-optimized content using the Ditto Content API
- Validates content against brand guidelines
- Manages drafts and content libraries
- Displays LinkedIn-style previews of generated content
For detailed information about the client's features and functionality, see the client README.
- Node.js (v16 or higher)
- npm or yarn
- Ditto API service running (either locally at
http://localhost:3000or deployed)
- Navigate to the client directory:
cd client- Install dependencies:
npm installDevelopment Mode:
- Start the Ditto API service (from project root):
npm start- Start the client (from
client/directory):
npm run dev- Open
http://localhost:5173in your browser
Production Build:
cd client
npm run buildThe production build will be in the client/dist/ directory.
By default, the client is configured to connect to a locally running API service at http://localhost:3000. The client uses a Vite proxy configuration (in client/vite.config.ts) to forward API requests to the backend.
To connect a client instance to a cloud-deployed API service, modify client/vite.config.ts:
server: {
proxy: {
'/api': {
target: 'https://your-gcp-service-url.run.app',
changeOrigin: true,
},
},
}You can run multiple client instances simultaneously. Each client instance:
- Can run on any local machine (does not need to run in the cloud)
- Must have its own unique API key (obtained via
/api/clients/create) - Stores its API key in browser localStorage
- Can connect to the same API service instance
Example: You can run:
- Client instance 1 on
localhost:5173(API key:key-abc-123) - Client instance 2 on
localhost:5174(API key:key-xyz-789) - Client instance 3 on a different machine (API key:
key-def-456)
All three can connect to the same API service running at localhost:3000 or a cloud deployment.
The Ditto API service is designed to handle multiple client instances running simultaneously. Here's how it works:
- Each client instance gets a unique API key:
- When a client first starts, it calls
/api/clients/create(no authentication required) - The service creates a new client record in the database and generates a unique API key
- The API key is returned to the client and stored in browser localStorage
- API key maps to client_id:
- The service maintains an
api_keystable that maps API keys toclient_id - Every authenticated request includes the API key in the
Authorization: Bearer <api_key>header - The service's authentication middleware looks up the API key to determine the
client_id
- Data isolation by client_id:
- All database tables (projects, themes, contents, etc.) include a
client_idcolumn - Every query filters results by
client_idto ensure data isolation - Client A's projects, themes, and content are completely separate from Client B's data
┌─────────────────┐
│ Client Instance 1 │ (API Key: key-abc-123)
│ localhost:5173 │
└────────┬──────────┘
│
│ Authorization: Bearer key-abc-123
│
▼
┌─────────────────────────────────────┐
│ Ditto API Service │
│ localhost:3000 │
│ │
│ ┌──────────────────────────────┐ │
│ │ Authentication Middleware │ │
│ │ - Validates API key │ │
│ │ - Extracts client_id │ │
│ └──────────────┬─────────────────┘ │
│ │ │
│ ┌──────────────▼─────────────────┐ │
│ │ Database Queries │ │
│ │ - Filter by client_id │ │
│ │ - Data isolation enforced │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
▲
│
│ Authorization: Bearer key-xyz-789
│
┌────────┴──────────┐
│ Client Instance 2 │ (API Key: key-xyz-789)
│ localhost:5174 │
└───────────────────┘
The service distinguishes between clients using a three-layer identification system:
- API Key Authentication:
- Each request includes
Authorization: Bearer <api_key>header - The service validates the API key against the
api_keystable - Invalid or missing API keys return
401 Unauthorizedor403 Forbidden
- Client ID Extraction:
- Valid API keys map to a unique
client_idin the database - The authentication middleware extracts and attaches
client_idto the request object - All subsequent database operations use this
client_id
- Database-Level Isolation:
- Every table includes a
client_idforeign key - All SELECT queries include
WHERE client_id = ?filters - All INSERT operations automatically include the authenticated
client_id - This ensures complete data isolation between clients
Example Flow:
// Client Instance 1 makes request
GET /api/projects
Authorization: Bearer key-abc-123
// Service authentication middleware:
1. Validates key-abc-123 → finds client_id = "client-1"
2. Attaches client_id to request object
// Database query:
SELECT * FROM projects WHERE client_id = 'client-1'
// Returns only Client 1's projects
// Client Instance 2 makes request
GET /api/projects
Authorization: Bearer key-xyz-789
// Service authentication middleware:
1. Validates key-xyz-789 → finds client_id = "client-2"
2. Attaches client_id to request object
// Database query:
SELECT * FROM projects WHERE client_id = 'client-2'
// Returns only Client 2's projects (completely separate data)This architecture ensures that:
- Multiple client instances can run simultaneously without conflicts
- Each client only sees and can only access their own data
- The service can handle unlimited concurrent client connections
- Client instances can run on any machine (local or cloud) and connect to the same service
Most endpoints require API key authentication. Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
How to get an API key:
- First, call
/api/clients/create(no auth required) to create a client - You'll receive an API key in the response - save it immediately!
- Use that API key in the
Authorizationheader for all other endpoints
Status Code Format: HTTP_CODE (description) - Status: for success codes, Errors: for error codes
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/clients/create |
Start here! Create a new client and get an API key Body: {"name": "string"}Returns: {success, data: {client_id, api_key}, message}Status: 201 (created), Errors: 400 (missing name), 500 (server error) |
| GET | /api/vertex-test |
Test Vertex AI connection No parameters required Returns: {success, data: "AI response", message}Status: 200 (success), Errors: 500 (Vertex AI connection failed) |
| Method | Endpoint | Description |
|---|---|---|
| Projects | ||
| POST | /api/projects/create |
Create a new project Required: nameOptional: description, goals, customer_type, theme_idReturns: {success, data: {id, name, description, goals, customer_type, theme_id, created_at, client_id}, message}Status: 201 (created), Errors: 400 (missing name), 401 (no auth), 403 (invalid auth), 500 (server error) |
| GET | /api/projects |
List all projects for authenticated client No parameters required Returns: {success, data: [{project objects}], message}Status: 200 (success), Errors: 401 (no auth), 403 (invalid auth), 500 (server error) |
| PUT | /api/projects/:id |
Update a project Path: :idBody: Any of name, description, goals, customer_type, theme_idReturns: {success, data: {updated project object}, message}Status: 200 (success), Errors: 400 (invalid data), 401 (no auth), 403 (invalid auth), 404 (project not found), 500 (server error) |
| Themes | ||
| POST | /api/themes/create |
Create a new brand theme Required: name, tags[] (array, must have at least one tag)Optional: inspirations[] (array), project_idReturns: {success, data: {id, name, tags, inspirations, created_at, client_id, ...}, message}Status: 201 (created), Errors: 400 (missing name or tags), 401 (no auth), 403 (invalid auth), 500 (server error) |
| GET | /api/themes |
List all themes for authenticated client No parameters required Returns: {success, data: [{theme objects}], message}Status: 200 (success), Errors: 401 (no auth), 403 (invalid auth), 500 (server error) |
| Contents | ||
| GET | /api/contents/:project_id |
List all content for a project Path: :project_idReturns: {success, data: [{id, project_id, media_type, media_url, text_content, created_at}], message}Status: 200 (success), Errors: 401 (no auth), 403 (invalid auth), 404 (project not found), 500 (server error) |
| Text Generation | ||
| POST | /api/text/generate |
Generate text content using AI Required: project_id, promptOptional: style_preferences (default: {}), target_audience (default: "general"), variantCount (default: 3), media_type (default: "text")Returns: {success, data: {variants: [{content_id, generated_content}], project_id, media_type, variant_count, timestamp}, message}Status: 201 (created), Errors: 400 (missing project_id/prompt), 401 (no auth), 403 (invalid auth), 404 (project/theme not found), 500 (AI generation failed) |
| Image Generation | ||
| POST | /api/images/generate |
Generate images using AI with RAG Required: project_id, promptOptional: style_preferences (default: {}), target_audience (default: "general"), variantCount (default: 3, range: 1-10), aspectRatio (default: "1:1"), input_images (array of base64 images), overlay_text (string)Returns: {success, data: {variants: [{content_id, image_url, generated_content}], project_id, variant_count, timestamp}, message}Status: 201 (created), Errors: 400 (missing project_id/prompt), 401 (no auth), 403 (invalid auth), 404 (project/theme not found), 500 (AI generation failed) |
| Content Validation | ||
| POST | /api/validate |
Validate content against brand guidelines Either: content_id OR content + project_idPrerequisites: Project must have a theme linked AND customer_type must be set (not null) Returns: {success, data: {validation: {brand_consistency_score, quality_score, overall_score, passes_validation, strengths, issues, recommendations, summary, similarity_details}}, message}Status: 200 (success), Errors: 400 (missing parameters), 401 (no auth), 403 (invalid auth), 404 (content/project/theme not found), 500 (validation failed) |
| Content Ranking | ||
| POST | /api/rank |
Rank content items by brand alignment and quality Either: project_id OR content_ids[] (array of content IDs)Optional: limit (number)Returns: {success, data: {ranked_content: [{content_id, rank, brand_consistency_score, quality_score, overall_score, recommendation}], summary: {total_ranked, average_scores}}, message}Status: 200 (success), Errors: 400 (missing project_id or content_ids), 401 (no auth), 403 (invalid auth), 404 (project/content not found), 500 (ranking failed) |
- 200: Success (GET, PUT, POST operations completed successfully)
- 201: Created (POST operations that create new resources)
- 400: Bad request (missing required fields, invalid data)
- 401: Unauthorized (missing API key)
- 403: Forbidden (invalid API key)
- 404: Not found (resource doesn't exist)
- 500: Internal server error (server/configuration issue)
IMPORTANT: Some endpoints must be called in a specific order to work correctly:
- Create Client (
POST /api/clients/create) - MUST BE FIRST
- No authentication required
- Returns API key needed for all subsequent calls
- Save the API key immediately - it's only shown once
- Create Theme (
POST /api/themes/create) - BEFORE creating projects
- Required for projects that will use validation or generation
- Must include
nameandtagsarray (non-empty) - Returns
theme_idneeded for project creation
- Create Project (
POST /api/projects/create) - AFTER theme creation
- Must include
theme_id(from step 2) for validation/generation to work - Must include
customer_type(not null) for validation to work - Returns
project_idneeded for content operations
- Generate/Validate/Rank Content - AFTER project setup
- Requires project with
theme_idandcustomer_typeset - Can be called in any order once project is properly configured
/api/text/generateand/api/images/generateare independent - neither requires the other- Both generation endpoints use RAG to retrieve existing project content for context (works with any existing content, or theme-only if none exists)
/api/rankrequires content to exist (either viaproject_idorcontent_ids)
- DO NOT call
/api/validatebefore creating a theme and linking it to a project - DO NOT call
/api/text/generateor/api/images/generatebefore project hastheme_id - DO NOT call
/api/rankwithproject_idbefore any content has been generated for that project - DO NOT call
/api/themes/createwithout providing a non-emptytagsarray
IMPORTANT: The /api/validate endpoint requires specific project setup to work correctly:
- Create a client (get API key)
- Create a theme (with inspirations for brand guidelines)
- Create a project with:
theme_id(link to the theme)customer_type(must not be null - this is critical!)
- Now validation will work
Why this matters: The validation system uses the theme's inspirations and the project's customer_type to assess brand consistency. Without these, the system cannot perform proper validation.
Endpoint: POST /api/validate
Authentication: Required (Bearer token)
Prerequisites: Project must have theme_id AND customer_type (not null)
Request Options:
- Option A:
{"content_id": "existing-content-id"} - Option B:
{"content": "text to validate", "project_id": "project-id"}
Response: Returns brand consistency score, quality score, recommendations, and detailed similarity analysis.
curl -X POST http://localhost:3000/api/clients/create \
-H "Content-Type: application/json" \
-d '{"name": "My Company"}'Response:
{
"success": true,
"data": {
"client_id": "abc-123",
"api_key": "your-api-key-here"
},
"message": "Client created successfully"
}IMPORTANT: Save the API key - it's only shown once!
curl -X POST http://localhost:3000/api/themes/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"name": "Modern Tech",
"tags": ["modern", "professional"],
"inspirations": ["Apple", "Google"]
}'Note: tags is required and must be a non-empty array.
curl -X POST http://localhost:3000/api/projects/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"name": "Summer Campaign",
"description": "Product launch campaign",
"goals": "Increase awareness",
"customer_type": "Tech professionals",
"theme_id": "your-theme-id-from-step-2"
}'curl -X POST http://localhost:3000/api/text/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"project_id": "your-project-id",
"prompt": "Create a product announcement",
"variantCount": 3
}'curl -X POST http://localhost:3000/api/images/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"project_id": "your-project-id",
"prompt": "Professional product image",
"variantCount": 2,
"aspectRatio": "16:9"
}'curl -X POST http://localhost:3000/api/rank \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"project_id": "your-project-id",
"limit": 10
}'curl -X POST http://localhost:3000/api/validate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"content": "Our innovative tech solution provides modern features for professionals",
"project_id": "your-project-id"
}'Note: This will only work if your project has both a theme_id and customer_type set (not null).
Problem: Getting 500 error when calling /api/validate
Solution: Ensure your project has:
theme_idis set (not null)customer_typeis set (not null)
Problem: Getting 404 "Theme not found"
Solution: Link your project to a theme using theme_id
Problem: Getting 400 "Must provide either content_id OR (content + project_id)"
Solution: Provide either:
content_id(to validate existing content)- OR both
contentandproject_id(to validate new text)
This project utilizes ESLint as a code style checker to ensure consistency amongst the Typscript code, and to find any bugs.
The configuration for this service uses:
- ESLint v9 (flat-config format)
- TypeScript-ESLint for TypeScript support
- @eslint/js for base JavaScript rules
- Custom project rules defined in eslint.config.js
To run the style checker, first of course make sure that you have installed all dependencies:
npm install
Then, if you added ESLint later, you can install the required packages throught the following command:
npm install --save-dev eslint typescript-eslint @eslint/js
To run the style checker, use the following command:
npm run lint
To generate an HTML lint report run this command:
npm run lint:report
You can find the generated report in the reports folder in the root level directory, with the HTML file named, eslint-report.html.
The specific rule set that we are using is named in the config file, but to list it here, we use the Base JS recommended rules and recommended Typescript configs. Here is a link that was referred to every now and then when configuring our style rule set. And here is a link for documentation we consulted when reviewing some Typescript rules sets
An example of the report generated is shown below. This screenshot was taken as of 12/12/25. It shows how the current codebase came back clean with our rules implemented with the style checker:

If curious about the documentation that was used to understand the style checker, check out this link!
Before Report Summary:
| Category | Count | Files Affected |
|---|---|---|
| Object Injection | 40 | ImageGenerationController, ContentAnalysisService, EmbeddingService, RAGService, StorageService, ThemeAnalysisService, ragService.unit.test |
| Non-Literal RegExp | 1 | ContentAnalysisService |
| Unused Variables | 3 | contentAnalysisService.unit.test, contentGenerationPipeline.unit.test |
| Total | 42 | 8 files |
Issues Fixed:
-
Non-Literal RegExp (1 issue) - ContentAnalysisService.ts:374
- Problem: Dynamic regex patterns from user input create injection attack surface
- Fix: Added
eslint-disable-next-line security/detect-non-literal-regexpwith input validation
-
Object Injection Vulnerabilities (40 issues) - Multiple files
- Problem: ESLint rule
security/detect-object-injectionflagged legitimate TypeScript property access patterns - Analysis: After review, determined these were false positives because:
- Code uses known object structures, not untrusted user input for property access
- TypeScript's type system provides safety guarantees
- Real object injection requires dynamic property access on untrusted data
- Fix: Removed
security/detect-object-injectionrule from ESLint config
- Problem: ESLint rule
-
Unused Variables (3 issues) - Test files
- Fixed by removing unused imports in test files
After: 0 errors, 0 warnings
- Before Report:
eslint-before-report.txt- 42 issues (3 errors, 39 warnings) - After Report:
eslint-after-report.txt- 0 issues - HTML Report:
reports/static-analysis-report.html
This project includes a comprehensive testing infrastructure with multiple tools for unit testing, API testing, coverage reporting, and CI/CD integration via GitHub Actions.
- Purpose: Core testing framework that handles ALL types of testing
- Usage:
npm test - Output: Test results in terminal with pass/fail status
- What it provides:
- Unit Testing:
describe(),it(),expect()functions - API Testing: Works with supertest for HTTP requests
- Function Testing: Test individual functions and methods
- Coverage Reporting: Built-in coverage measurement
- Mocking: Mock functions, modules, and dependencies
- Purpose: Compiles TypeScript test files without manual compilation
- Usage: Automatically configured in
jest.config.ts - Output: Transparent TypeScript compilation for tests
- What it provides: Seamless TypeScript support in test files
- Purpose: Provides IntelliSense and type checking for Jest functions
- Usage: Automatically loaded
- Output: Autocomplete and type safety for Jest functions
- What it provides: TypeScript support for
describe,it,expect, etc.
- Purpose: Simulates HTTP requests to Express app without starting a real server
- Usage: Import in test files:
import request from 'supertest' - Output: HTTP request/response testing with status codes and body validation
- What it provides:
request(app).get('/endpoint').expect(200)syntax
- Purpose: Provides IntelliSense for supertest functions
- Usage: Automatically loaded
- Output: Autocomplete for
request(),.get(),.post(),.expect() - What it provides: TypeScript support for HTTP testing
- Purpose: Generates XML reports for CI/CD systems (Jenkins, GitHub Actions)
- Usage:
npm run test:unit:junit - Output:
reports/jest-junit.xml- Standard XML format for CI integration - What it provides: Test results, timing, and metadata in XML format
- Purpose: Measures how much of your code was executed by tests (built into Jest)
- Usage:
npm run test:unit(includes coverage) - Output: Coverage metrics and HTML reports
- What it provides: Statement, branch, function, and line coverage percentages
NOTE: You can find the HTML report generated by the command by looking into the coverage folder and finding the index.html file.
Here is a screenshot of the HTML report that is generated when checking branch coverage tests. Notice there is over 80% coverage (Taken 12/12/25):

- Purpose: Maps coverage and stack traces correctly back to TypeScript line numbers
- Usage: Automatically configured
- Output: Better error messages showing TypeScript lines instead of compiled JS
- What it provides: Accurate error reporting and debugging
- Purpose: Command-line runner for Postman collections (runs API tests automatically)
- Usage:
npm run api:test - Output: API test results in terminal with pass/fail status
- What it provides: Automated API endpoint testing without manual Postman usage
- Purpose: Generates beautiful HTML reports for API test runs
- Usage: Automatically configured with newman
- Output:
reports/postman-report.html- Interactive HTML report - What it provides: Visual API test results with detailed request/response information
NOTE: Test Dependencies Are Critical, the Postman tests must run in correct order
# Basic testing
npm test # Run all tests
npm run test:unit # Run tests with coverage
npm run test:unit:junit # Run tests with coverage + JUnit XML
# API testing
npm run api:test # Run Postman collections
# Full reporting
npm run reports:all # Generate all reports (tests + coverage + API)
npm run coverage:summary # Generate coverage summary| Report | Location | Purpose |
|---|---|---|
| HTML Coverage Report | coverage/index.html |
Interactive coverage visualization |
| JUnit XML | reports/jest-junit.xml |
CI/CD integration |
| API Test Report | reports/postman-report.html |
Postman test results |
The following lists some bugs and errors that we ran into. This section serves as:
-
When first getting the API to run, we ran into some trouble. It seemed as though
app.listenwas not being hit and that there was this trend that as one updated theapp.tsfile, the run commands stopped running. With some further investigation, such as guidance from AI tools with prompts asking for help to debug, and documentation reading, we were able to solve this issue. -
There was a time when we had issue with figuring out how to protect our API keys, but that was a quick fix by modifying the
/client/createendpoint. -
During testing, in order to ensure the project was demoable, the
/generateendpoint was hanging and not returning a response code. We found that content was being generated, despite the hanging of the endpoint. We fixed this accordingly and ensure that the endpoint worked (i.e. returning a response) with further testting.
-
Our Postman API tests were failing with 13 errors initially, then we reduced it to 9 errors, and finally down to 2 errors. Here is a breakdown of these error and our debugging of them:
-
/api/generateand/api/validateendpoints were returning 404 errors -
Project creation was failing with 500 errors
-
Tests were failing due to missing or invalid project IDs
-
There were JSON parsing errors in request bodies
-
There were environment variable management issues
We found that the issues were related to database schema requirements, test execution order issue, and missing dependencies. We solved these errors by fixing JSON escaping, fixing environment variables, fixing project creation, creating a dedicated test project, and updating test dependencies.
- JSDoc Documentation: used AI to generate API documentation for the /generate endpoint ; highlighted the code in cursor and asked it to generate a JSDoc description based on the given parameters / outputs
- Content generation prompts: used Cursor in-line prompting to create a prompt template for context-aware prompts that incorporates the project data and user requirements. Prompted along the lines of: "Based on the user-inputted data, generate a context-aware prompt that produces relevant marketing content. Incorporate the exact variables highlighted in the prompt"
- AI integration: integrated with Google Cloud Vertex AI for actual content generation. Used GCP credits supplied in class.
The "lint" lines in the package.json files were created with the help of ChatGPT and documentation reading. The file eslint.config.js, which is where all the ESLint settings are, is also cited in the file and was created with the help and guidance of ChatGPT and online documentation. Prompts given were just that of the nature where we explained what we were hoping to achieve with specific settings and asking how we could go about implementing this.
This sections of the project was done with the help of Cursor. A prompt like "modify this file to support proper theme endpoint injection for correct testing usage," was used to generate guidance and code.
There are some comments that were done with the assistance of Copilot. Prompts were of the nature asking for assistance in commenting and providing descriptions for functions and files.
- Files:
src/controllers/Computation.ts(validate function),src/routes/computationRoutes.ts - Use Case: Used Cursor to add error handling the
/api/validateendpoint that compares generated content against brand themes using cosine similarity calculations that i wrote - What AI Generated:
- Error handling for edge cases (missing params, invalid IDs)
- JSDoc comments for the validate function
- Files:
postman/collection.json,README.md - Use Case: Used Cursor to expand API test coverage from 25 to 34+ tests
- What AI Generated:
- 4 data persistence tests (create → read → update → read)
- 5 multi-client isolation tests with separate API keys
- Collection-level prerequest scripts for logging documentation
- README section explaining how to verify logging during API tests
- Files:
README.md(API Documentation section) - Use Case: Enhanced API documentation clarity
- Prompts: "Clarify that /api/clients/create is a public endpoint used to obtain API keys"
- What AI Generated: "How to get an API key" section with step-by-step instructions
- Files:
client/src/pages/*.tsx,client/src/components/*.tsx,client/src/types/index.ts,client/README.md,README.md(Client Application section) - Use Case: Built the LinkLaunch React client application with Cursor AI assistance (component scaffolding + code-complete)
- What AI Generated: React components for LinkedIn post generation, validation, and preview; content grouping logic; validation UI with metrics; API integration hooks; TypeScript types; formatting client documentation and diagrams (including multi-client architecture)
This project at times utilized the free AI tools that are Cursor (student plan - free) and ChatGPT (free). These tools were consulted when guidance was needed to setup certain endpoints, the testing framework, and the organization of this README (which has already been documented in the section above). Although there are citations within the code that mark when AI was used, we wanted to include this section and overall statement to ensure that we give a proper citation to external artifcial intelligence tools/sources used. All AI-assisted code was reviewed, tested, and validated before committing. GCP credits for Vertex AI were obtained through Columbia University.





