Skip to content

Commit b03a1db

Browse files
committed
get testing to work kind of
1 parent 42be5f4 commit b03a1db

File tree

18 files changed

+3616
-4
lines changed

18 files changed

+3616
-4
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Supabase Start
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
supabase-start:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: "20"
21+
cache: "yarn"
22+
cache-dependency-path: "yarn.lock"
23+
24+
- name: Enable Corepack
25+
run: corepack enable
26+
27+
- name: Setup Supabase CLI
28+
uses: supabase/setup-cli@v1
29+
with:
30+
version: latest
31+
32+
- name: Start Clickhouse and Minio
33+
run: cd docker && docker compose up minio minio-setup clickhouse -d
34+
35+
- name: Start Supabase
36+
run: echo 'y' | supabase start -x realtime,storage-api,imgproxy,mailpit,edge-runtime,logflare,vector,supavisor
37+
38+
- name: start gateway workers
39+
run: |
40+
cd worker
41+
yarn install
42+
npx wrangler dev --var WORKER_TYPE:HELICONE_API --port 8788 &
43+
npx wrangler dev --var WORKER_TYPE:AI_GATEWAY_API --port 8793 --test-scheduled &
44+
sleep 10
45+
46+
- name: start jawn

e2e/.env.example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# AI Gateway URL (default: http://localhost:8793)
2+
AI_GATEWAY_URL=http://localhost:8793
3+
4+
# Worker API URL (default: http://localhost:8788)
5+
WORKER_API_URL=http://localhost:8788
6+
7+
# Jawn API URL (default: http://localhost:8585)
8+
JAWN_URL=http://localhost:8585
9+
10+
# ClickHouse URL (default: http://localhost:8123)
11+
CLICKHOUSE_URL=http://localhost:8123
12+
13+
# PostgreSQL URL (default: postgresql://postgres:postgres@localhost:54322/postgres)
14+
POSTGRES_URL=postgresql://postgres:postgres@localhost:54322/postgres
15+
16+
# Test timeout in milliseconds (default: 30000)
17+
TEST_TIMEOUT=30000

e2e/README.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Helicone E2E Tests
2+
3+
End-to-end integration tests for the Helicone AI Gateway.
4+
5+
## Overview
6+
7+
This test suite validates the Helicone AI Gateway functionality by making real HTTP requests to the gateway running on port 8793.
8+
9+
## Prerequisites
10+
11+
- Node.js 20+
12+
- Yarn
13+
- Helicone AI Gateway running on port 8793
14+
15+
## Getting Started
16+
17+
### 1. Install Dependencies
18+
19+
```bash
20+
cd e2e
21+
yarn install
22+
```
23+
24+
### 2. Start the Gateway
25+
26+
From the root of the Helicone project:
27+
28+
```bash
29+
cd worker
30+
npx wrangler dev --var WORKER_TYPE:AI_GATEWAY_API --port 8793 --test-scheduled
31+
```
32+
33+
Or use the convenience script:
34+
35+
```bash
36+
./worker/run_all_workers.sh
37+
```
38+
39+
### 3. Run Tests
40+
41+
```bash
42+
# Run all tests
43+
yarn test
44+
45+
# Run gateway tests only
46+
yarn test:gateway
47+
48+
# Run in watch mode
49+
yarn test:watch
50+
51+
# Run with coverage
52+
yarn test:coverage
53+
```
54+
55+
## Configuration
56+
57+
Create a `.env` file in the `e2e` directory to customize settings:
58+
59+
```bash
60+
GATEWAY_URL=http://localhost:8793
61+
```
62+
63+
## Test Structure
64+
65+
```
66+
e2e/
67+
├── lib/ # Shared utilities
68+
│ ├── constants.ts # Test constants and config
69+
│ ├── http-client.ts # HTTP client wrapper
70+
│ └── test-helpers.ts # Common test helpers
71+
├── tests/
72+
│ ├── setup.ts # Jest setup file
73+
│ └── gateway/ # Gateway-specific tests
74+
│ ├── health.test.ts # Health check tests
75+
│ └── chat-completion.test.ts # Chat completion tests
76+
├── jest.config.ts # Jest configuration
77+
├── tsconfig.json # TypeScript configuration
78+
└── package.json
79+
```
80+
81+
## Writing Tests
82+
83+
### Example Test
84+
85+
```typescript
86+
import { gatewayClient } from "../../lib/http-client";
87+
import { GATEWAY_ENDPOINTS } from "../../lib/constants";
88+
import { createChatCompletionRequest } from "../../lib/test-helpers";
89+
90+
describe("My Feature", () => {
91+
it("should work correctly", async () => {
92+
const requestBody = createChatCompletionRequest({
93+
model: "gpt-4o-mini",
94+
messages: [{ role: "user", content: "Hello" }],
95+
});
96+
97+
const response = await gatewayClient.post(
98+
GATEWAY_ENDPOINTS.CHAT_COMPLETIONS,
99+
requestBody
100+
);
101+
102+
expect(response.status).toBe(200);
103+
});
104+
});
105+
```
106+
107+
## Available Test Utilities
108+
109+
### HTTP Client
110+
111+
```typescript
112+
import { gatewayClient } from "../../lib/http-client";
113+
114+
// Make requests
115+
await gatewayClient.post("/endpoint", data);
116+
await gatewayClient.get("/endpoint");
117+
118+
// Custom headers
119+
gatewayClient.setHeaders({ "X-Custom": "value" });
120+
gatewayClient.resetHeaders();
121+
```
122+
123+
### Test Helpers
124+
125+
```typescript
126+
import {
127+
createChatCompletionRequest,
128+
validateChatCompletionResponse,
129+
retry,
130+
sleep,
131+
} from "../../lib/test-helpers";
132+
133+
// Create request body
134+
const request = createChatCompletionRequest({
135+
model: "gpt-4o-mini",
136+
messages: [{ role: "user", content: "Hello" }],
137+
});
138+
139+
// Validate response
140+
validateChatCompletionResponse(response);
141+
142+
// Retry with backoff
143+
await retry(() => apiCall(), { maxAttempts: 3 });
144+
```
145+
146+
## Troubleshooting
147+
148+
### Gateway Not Running
149+
150+
If tests fail with connection errors:
151+
152+
1. Ensure the gateway is running: `npx wrangler dev --var WORKER_TYPE:AI_GATEWAY_API --port 8793`
153+
2. Check the port is correct: `lsof -i :8793`
154+
3. Verify the gateway URL in `.env`
155+
156+
### Timeout Errors
157+
158+
If tests timeout:
159+
160+
1. Increase timeout in `jest.config.ts`: `testTimeout: 60000`
161+
2. Check gateway logs for errors
162+
3. Verify network connectivity
163+
164+
### Authentication Errors
165+
166+
Tests use mock authentication headers. If you see auth errors:
167+
168+
1. Check `lib/constants.ts` for header configuration
169+
2. Verify the gateway is configured for local testing
170+
3. Update test headers as needed
171+
172+
## CI/CD Integration
173+
174+
Add to your CI pipeline:
175+
176+
```yaml
177+
- name: Install E2E dependencies
178+
run: cd e2e && yarn install
179+
180+
- name: Start Gateway
181+
run: |
182+
cd worker
183+
npx wrangler dev --var WORKER_TYPE:AI_GATEWAY_API --port 8793 &
184+
sleep 10
185+
186+
- name: Run E2E tests
187+
run: cd e2e && yarn test
188+
```

e2e/jest.config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Config } from "jest";
2+
3+
const config: Config = {
4+
preset: "ts-jest",
5+
testEnvironment: "node",
6+
roots: ["<rootDir>/tests"],
7+
testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"],
8+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
9+
collectCoverageFrom: [
10+
"tests/**/*.{ts,tsx}",
11+
"lib/**/*.{ts,tsx}",
12+
"!tests/**/*.d.ts",
13+
"!lib/**/*.d.ts",
14+
"!tests/**/*.spec.ts",
15+
"!tests/**/*.test.ts",
16+
],
17+
coverageDirectory: "coverage",
18+
verbose: true,
19+
testTimeout: 30000,
20+
setupFilesAfterEnv: ["<rootDir>/tests/setup.ts"],
21+
transform: {
22+
"^.+\\.ts$": [
23+
"ts-jest",
24+
{
25+
tsconfig: {
26+
esModuleInterop: true,
27+
},
28+
},
29+
],
30+
},
31+
};
32+
33+
export default config;

e2e/lib/constants.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Test constants and configuration
3+
*/
4+
5+
// Service URLs
6+
export const AI_GATEWAY_URL =
7+
process.env.AI_GATEWAY_URL || "http://localhost:8793";
8+
export const WORKER_API_URL =
9+
process.env.WORKER_API_URL || "http://localhost:8788";
10+
export const JAWN_URL = process.env.JAWN_URL || "http://localhost:8585";
11+
export const POSTGRES_URL =
12+
process.env.POSTGRES_URL ||
13+
"postgresql://postgres:postgres@localhost:54322/postgres";
14+
export const CLICKHOUSE_URL =
15+
process.env.CLICKHOUSE_URL || "http://localhost:18123";
16+
17+
// Gateway endpoints
18+
export const GATEWAY_ENDPOINTS = {
19+
CHAT_COMPLETIONS: "/v1/chat/completions",
20+
HEALTHCHECK: "/healthcheck",
21+
} as const;
22+
23+
// Jawn endpoints
24+
export const JAWN_ENDPOINTS = {
25+
HEALTHCHECK: "/healthcheck",
26+
} as const;
27+
28+
export const TEST_ORG_ID = "83635a30-5ba6-41a8-8cc6-fb7df941b24a";
29+
30+
export const TEST_ORG_API_KEY = "sk-helicone-aizk36y-5yue2my-qmy5tza-n7x3aqa";
31+
32+
export const TEST_HEADERS = {
33+
"Content-Type": "application/json",
34+
Authorization: `Bearer ${TEST_ORG_API_KEY}`,
35+
"__helicone-mock-response": "true",
36+
} as const;
37+
38+
export const DEFAULT_TIMEOUT = 30000; // 30 seconds
39+
40+
export const TEST_MESSAGES = {
41+
SIMPLE: [
42+
{
43+
role: "user" as const,
44+
content: "Say hello",
45+
},
46+
],
47+
} as const;

e2e/lib/http-client.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* HTTP client for making requests to the Helicone Gateway
3+
*/
4+
5+
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
6+
import { AI_GATEWAY_URL, TEST_HEADERS, DEFAULT_TIMEOUT } from "./constants";
7+
8+
export class GatewayClient {
9+
private client: AxiosInstance;
10+
11+
constructor(baseURL: string = AI_GATEWAY_URL) {
12+
this.client = axios.create({
13+
baseURL,
14+
timeout: DEFAULT_TIMEOUT,
15+
headers: TEST_HEADERS,
16+
validateStatus: () => true, // Don't throw on any status code
17+
});
18+
}
19+
20+
/**
21+
* Make a POST request to the gateway
22+
*/
23+
async post<T = any>(
24+
endpoint: string,
25+
data: any,
26+
config?: AxiosRequestConfig
27+
): Promise<AxiosResponse<T>> {
28+
return this.client.post<T>(endpoint, data, config);
29+
}
30+
31+
/**
32+
* Make a GET request to the gateway
33+
*/
34+
async get<T = any>(
35+
endpoint: string,
36+
config?: AxiosRequestConfig
37+
): Promise<AxiosResponse<T>> {
38+
return this.client.get<T>(endpoint, config);
39+
}
40+
41+
/**
42+
* Set custom headers for the next request
43+
*/
44+
setHeaders(headers: Record<string, string>): void {
45+
Object.assign(this.client.defaults.headers, headers);
46+
}
47+
48+
/**
49+
* Reset headers to default
50+
*/
51+
resetHeaders(): void {
52+
this.client.defaults.headers = {
53+
...this.client.defaults.headers,
54+
...TEST_HEADERS,
55+
} as any;
56+
}
57+
}
58+
59+
// Export a singleton instance
60+
export const gatewayClient = new GatewayClient();

0 commit comments

Comments
 (0)