mirror of
https://github.com/hanzoai/chat.git
synced 2026-09-30 20:51:17 +00:00
The second half of the same uncommitted 2026-07-27 session. It lets the playwright suite run against fakes instead of a real deployment: an in-process fake model (setup/fake-model.js), fake MCP servers over stdio and HTTP, a tool-approval hook, mock users and fixtures, and specs/mock/ that exercise them. playwright.config.mock.ts wires it together and is the entry point. .generated/chat.e2e.yaml is committed deliberately despite the directory name: it is a credential-free TEMPLATE that playwright.config.mock.ts copies to an ignored runtime path, and the fake model overrides the endpoint every run, so the placeholder baseURL in it is never contacted. It is input to the harness, not output of it — the directory is misleadingly named, which is worth knowing before someone adds it to .gitignore and breaks the mock run. packages/api/src/agents/testHook.ts rides along: it is test-only infrastructure from the same work. Nothing in tracked source imports it (the reference in packages/api/dist/ is from a stale build, and the tracked agents/index.ts does not export it), so it is inert until something opts in. Not run here: this checkout cannot execute the suite — @hanzochat/data-provider is not linked into node_modules, which already breaks tracked tests. Committed so the work stops living only on one disk; whoever has a complete workspace can exercise it. Co-authored-by: Hanzo Dev <[email protected]>
92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { MongoClient } from 'mongodb';
|
|
import type { Db } from 'mongodb';
|
|
|
|
const DEFAULT_MONGO_URI = 'mongodb://127.0.0.1:27017/Chat-e2e';
|
|
|
|
/**
|
|
* e2e/setup/start-server.js writes the active Mongo URI (memory-Mongo port included)
|
|
* here on boot. It honors `E2E_RUNTIME_ENV_PATH`, so resolve the same override the
|
|
* server used before falling back to the default location.
|
|
*/
|
|
function getRuntimeEnvPath(): string {
|
|
return (
|
|
process.env.E2E_RUNTIME_ENV_PATH ?? path.resolve(__dirname, '../.test-results/runtime-env.json')
|
|
);
|
|
}
|
|
|
|
function getMongoUri(): string {
|
|
try {
|
|
const env = JSON.parse(fs.readFileSync(getRuntimeEnvPath(), 'utf8')) as { MONGO_URI?: string };
|
|
if (env.MONGO_URI) {
|
|
return env.MONGO_URI;
|
|
}
|
|
} catch {
|
|
/* fall through to env/default */
|
|
}
|
|
return process.env.MONGO_URI ?? DEFAULT_MONGO_URI;
|
|
}
|
|
|
|
async function resolveUserId(db: Db, userEmail: string): Promise<string> {
|
|
const user = await db.collection('users').findOne({ email: userEmail });
|
|
if (!user) {
|
|
throw new Error(`E2E seed: user "${userEmail}" not found`);
|
|
}
|
|
return user._id.toString();
|
|
}
|
|
|
|
/** Connect to the e2e MongoDB, run `fn`, and always close the client. */
|
|
export async function withMongo<T>(fn: (db: Db) => Promise<T>): Promise<T> {
|
|
const client = new MongoClient(getMongoUri());
|
|
await client.connect();
|
|
try {
|
|
return await fn(client.db());
|
|
} finally {
|
|
await client.close();
|
|
}
|
|
}
|
|
|
|
export interface SeedConvo {
|
|
conversationId: string;
|
|
title: string;
|
|
/** Drives the sidebar date group ("Today", "Previous 7 days", ...). */
|
|
updatedAt: Date;
|
|
}
|
|
|
|
/**
|
|
* Inserts conversation documents directly (bypassing mongoose timestamps) so their
|
|
* `updatedAt` can be backdated into specific sidebar date groups.
|
|
*/
|
|
export async function seedConversations(userEmail: string, convos: SeedConvo[]): Promise<void> {
|
|
await withMongo(async (db) => {
|
|
const userId = await resolveUserId(db, userEmail);
|
|
const docs = convos.map((convo) => ({
|
|
conversationId: convo.conversationId,
|
|
title: convo.title,
|
|
user: userId,
|
|
endpoint: 'openAI',
|
|
isArchived: false,
|
|
createdAt: convo.updatedAt,
|
|
updatedAt: convo.updatedAt,
|
|
__v: 0,
|
|
}));
|
|
await db.collection('conversations').insertMany(docs);
|
|
});
|
|
}
|
|
|
|
export async function deleteConversations(conversationIds: string[]): Promise<void> {
|
|
await withMongo(async (db) => {
|
|
await db.collection('conversations').deleteMany({ conversationId: { $in: conversationIds } });
|
|
});
|
|
}
|
|
|
|
/** Clears every conversation for the user so the seeded date groups are not pushed
|
|
* below the virtualized viewport by rows left behind by other specs. */
|
|
export async function clearUserConversations(userEmail: string): Promise<void> {
|
|
await withMongo(async (db) => {
|
|
const userId = await resolveUserId(db, userEmail);
|
|
await db.collection('conversations').deleteMany({ user: userId });
|
|
});
|
|
}
|