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
7 changes: 7 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
evaluator: Detailed evaluation engine
"""

import os
import sys

_PACKAGE_DIR = os.path.abspath(os.path.dirname(__file__))
if _PACKAGE_DIR not in sys.path:
sys.path.insert(0, _PACKAGE_DIR)

from core_directive import (
ActionResult,
CoreDirective,
Expand Down
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
testEnvironment: 'node',
testPathIgnorePatterns: ['/prime-security/'],
modulePathIgnorePatterns: ['/prime-security/']
};
2 changes: 0 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 63 additions & 1 deletion src/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const PORT = process.env.GATEWAY_PORT || 3000;
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const OPENAI_BASE_URL = process.env.OPENAI_BASE_URL || 'https://api.openai.com';
const DEFAULT_MODEL = process.env.DEFAULT_MODEL || 'gpt-4';
const users = [];

// Core Directive - The governing principle for the LLM
const CORE_DIRECTIVE = process.env.CORE_DIRECTIVE || `You are governed by the following core directive:
Expand Down Expand Up @@ -50,6 +51,60 @@ app.get('/v1/models', (req, res) => {
});
});

/**
* Create a new user entry
*/
app.post('/v1/users', (req, res) => {
const name = typeof req.body?.name === 'string' ? req.body.name.trim() : '';
const email = typeof req.body?.email === 'string' ? req.body.email.trim() : '';

if (!name || !email) {
return res.status(400).json({
error: {
message: 'Both name and email are required to create a user',
type: 'validation_error'
}
});
}

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email)) {
return res.status(400).json({
error: {
message: 'A valid email address is required',
type: 'validation_error'
}
});
}

const existing = users.find((user) => user.email.toLowerCase() === email.toLowerCase());
if (existing) {
return res.status(409).json({
error: {
message: 'A user with this email already exists',
type: 'conflict_error'
}
});
}

const user = {
id: `user_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
name,
email,
createdAt: new Date().toISOString()
};

users.push(user);
return res.status(201).json({ user });
});

/**
* List created users
*/
app.get('/v1/users', (req, res) => {
res.json({ users });
});

/**
* Chat completions endpoint (OpenAI-compatible)
* This is the main endpoint that injects the Core Directive
Expand Down Expand Up @@ -230,6 +285,13 @@ function handleStreamingRequest(modifiedRequest, req, res) {
proxyReq.end();
}

/**
* Utility for tests to reset in-memory user data
*/
function clearUsers() {
users.length = 0;
}

// Start the server
if (require.main === module) {
app.listen(PORT, () => {
Expand All @@ -240,4 +302,4 @@ if (require.main === module) {
});
}

module.exports = { app, injectCoreDirective };
module.exports = { app, injectCoreDirective, clearUsers };
40 changes: 39 additions & 1 deletion tests/gateway.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
*/

const request = require('supertest');
const { app, injectCoreDirective } = require('../src/gateway');
const { app, injectCoreDirective, clearUsers } = require('../src/gateway');

describe('LLM Gateway', () => {
beforeEach(() => {
clearUsers();
});

describe('Health Check', () => {
it('should return ok status', async () => {
const response = await request(app).get('/health');
Expand Down Expand Up @@ -145,4 +149,38 @@ describe('LLM Gateway', () => {
}
});
});

describe('Users Endpoint', () => {
it('should create a user with name and email', async () => {
const response = await request(app)
.post('/v1/users')
.send({ name: 'Alice', email: 'alice@example.com' });

expect(response.status).toBe(201);
expect(response.body.user).toHaveProperty('id');
expect(response.body.user.name).toBe('Alice');
expect(response.body.user.email).toBe('alice@example.com');
expect(response.body.user).toHaveProperty('createdAt');
});

it('should validate required fields', async () => {
const response = await request(app)
.post('/v1/users')
.send({ name: 'Missing Email' });

expect(response.status).toBe(400);
expect(response.body.error.message).toContain('required');
});

it('should list created users', async () => {
await request(app)
.post('/v1/users')
.send({ name: 'Carol', email: 'carol@example.com' });

const response = await request(app).get('/v1/users');
expect(response.status).toBe(200);
expect(Array.isArray(response.body.users)).toBe(true);
expect(response.body.users.find(u => u.email === 'carol@example.com')).toBeTruthy();
});
});
});