Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,0BAA0B,EAC1B,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC"}

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,aAAa;AACb,yCAYqB;AAXnB,gHAAA,mBAAmB,OAAA;AACnB,6GAAA,gBAAgB,OAAA;AAChB,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AACd,uHAAA,0BAA0B,OAAA;AAC1B,2GAAA,cAAc,OAAA;AAQhB,mBAAmB;AACnB,iDAQyB;AAPvB,yGAAA,QAAQ,OAAA;AACR,+GAAA,cAAc,OAAA;AACd,gHAAA,eAAe,OAAA;AACf,oHAAA,mBAAmB,OAAA;AAMrB,cAAc;AACd,2CAUsB;AATpB,gHAAA,kBAAkB,OAAA;AAClB,oHAAA,sBAAsB,OAAA;AACtB,8GAAA,gBAAgB,OAAA;AAChB,mHAAA,qBAAqB,OAAA;AACrB,gHAAA,kBAAkB,OAAA"}

View File

@@ -0,0 +1,44 @@
/**
* Mock Module Index
*
* Central exports for all RuvBot test mocks
*/
// WASM Mocks
export {
MockWasmVectorIndex,
MockWasmEmbedder,
MockWasmRouter,
mockWasmLoader,
createMockRuVectorBindings,
resetWasmMocks,
type WasmVectorIndex,
type WasmEmbedder,
type WasmRouter,
type SearchResult,
type RouteResult
} from './wasm.mock';
// PostgreSQL Mocks
export {
MockPool,
createMockPool,
mockPoolFactory,
queryBuilderHelpers,
type QueryResult,
type PoolClient,
type PoolConfig
} from './postgres.mock';
// Slack Mocks
export {
MockSlackWebClient,
MockSlackEventsHandler,
MockSlackBoltApp,
createMockSlackClient,
createMockSlackApp,
type SlackMessage,
type SlackResponse,
type SlackUser,
type SlackChannel
} from './slack.mock';

View File

@@ -0,0 +1,106 @@
/**
* PostgreSQL Mock Module
*
* Mock implementations for Postgres database operations
* Supports transaction testing and query validation
*/
export interface QueryResult<T = unknown> {
rows: T[];
rowCount: number;
command: string;
fields: FieldInfo[];
}
export interface FieldInfo {
name: string;
dataTypeID: number;
}
export interface PoolConfig {
host: string;
port: number;
database: string;
user: string;
password: string;
max?: number;
idleTimeoutMillis?: number;
}
export interface PoolClient {
query<T = unknown>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
release(): void;
}
interface MockDataStore {
agents: Map<string, unknown>;
sessions: Map<string, unknown>;
memories: Map<string, unknown>;
skills: Map<string, unknown>;
tenants: Map<string, unknown>;
tasks: Map<string, unknown>;
}
/**
* Mock PostgreSQL Pool
*/
export declare class MockPool {
private config;
private connected;
private dataStore;
private queryLog;
private transactionActive;
constructor(config: PoolConfig);
connect(): Promise<PoolClient>;
query<T = unknown>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
end(): Promise<void>;
isConnected(): boolean;
getQueryLog(): Array<{
text: string;
values?: unknown[];
timestamp: Date;
}>;
clearQueryLog(): void;
seedData(table: keyof MockDataStore, data: Array<{
id: string;
[key: string]: unknown;
}>): void;
getData(table: keyof MockDataStore): unknown[];
private createClient;
private logQuery;
private executeQuery;
private handleSelect;
private handleInsert;
private handleUpdate;
private handleDelete;
private extractTableName;
private createResult;
}
/**
* Create a mock pool instance
*/
export declare function createMockPool(config?: Partial<PoolConfig>): MockPool;
/**
* Mock Pool factory for dependency injection
*/
export declare const mockPoolFactory: {
create: import("vitest").Mock<[config: PoolConfig], MockPool>;
createClient: import("vitest").Mock<[config: PoolConfig], Promise<PoolClient>>;
};
/**
* Postgres query builder mock helpers
*/
export declare const queryBuilderHelpers: {
expectQuery: (pool: MockPool, pattern: RegExp) => boolean;
expectQueryCount: (pool: MockPool, pattern: RegExp) => number;
expectTransaction: (pool: MockPool) => boolean;
};
declare const _default: {
MockPool: typeof MockPool;
createMockPool: typeof createMockPool;
mockPoolFactory: {
create: import("vitest").Mock<[config: PoolConfig], MockPool>;
createClient: import("vitest").Mock<[config: PoolConfig], Promise<PoolClient>>;
};
queryBuilderHelpers: {
expectQuery: (pool: MockPool, pattern: RegExp) => boolean;
expectQueryCount: (pool: MockPool, pattern: RegExp) => number;
expectTransaction: (pool: MockPool) => boolean;
};
};
export default _default;
//# sourceMappingURL=postgres.mock.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"postgres.mock.d.ts","sourceRoot":"","sources":["postgres.mock.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,260 @@
"use strict";
/**
* PostgreSQL Mock Module
*
* Mock implementations for Postgres database operations
* Supports transaction testing and query validation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.queryBuilderHelpers = exports.mockPoolFactory = exports.MockPool = void 0;
exports.createMockPool = createMockPool;
const vitest_1 = require("vitest");
/**
* Mock PostgreSQL Pool
*/
class MockPool {
constructor(config) {
this.config = config;
this.connected = false;
this.dataStore = {
agents: new Map(),
sessions: new Map(),
memories: new Map(),
skills: new Map(),
tenants: new Map(),
tasks: new Map()
};
this.queryLog = [];
this.transactionActive = false;
}
async connect() {
this.connected = true;
return this.createClient();
}
async query(text, values) {
this.logQuery(text, values);
return this.executeQuery(text, values);
}
async end() {
this.connected = false;
this.dataStore = {
agents: new Map(),
sessions: new Map(),
memories: new Map(),
skills: new Map(),
tenants: new Map(),
tasks: new Map()
};
}
isConnected() {
return this.connected;
}
getQueryLog() {
return [...this.queryLog];
}
clearQueryLog() {
this.queryLog = [];
}
// Seed data for testing
seedData(table, data) {
for (const row of data) {
this.dataStore[table].set(row.id, row);
}
}
getData(table) {
return Array.from(this.dataStore[table].values());
}
createClient() {
return {
query: async (text, values) => {
return this.executeQuery(text, values);
},
release: () => {
// No-op for mock
}
};
}
logQuery(text, values) {
this.queryLog.push({ text, values, timestamp: new Date() });
}
async executeQuery(text, values) {
const normalizedQuery = text.trim().toUpperCase();
// Handle transaction commands
if (normalizedQuery === 'BEGIN') {
this.transactionActive = true;
return this.createResult([], 'BEGIN');
}
if (normalizedQuery === 'COMMIT') {
this.transactionActive = false;
return this.createResult([], 'COMMIT');
}
if (normalizedQuery === 'ROLLBACK') {
this.transactionActive = false;
return this.createResult([], 'ROLLBACK');
}
// Parse and execute query
if (normalizedQuery.startsWith('SELECT')) {
return this.handleSelect(text, values);
}
if (normalizedQuery.startsWith('INSERT')) {
return this.handleInsert(text, values);
}
if (normalizedQuery.startsWith('UPDATE')) {
return this.handleUpdate(text, values);
}
if (normalizedQuery.startsWith('DELETE')) {
return this.handleDelete(text, values);
}
// Default: return empty result
return this.createResult([], 'UNKNOWN');
}
handleSelect(text, values) {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName];
if (!store) {
return this.createResult([], 'SELECT');
}
// Simple ID-based lookup
const idMatch = text.match(/WHERE\s+id\s*=\s*\$1/i);
if (idMatch && values?.[0]) {
const row = store.get(values[0]);
return this.createResult(row ? [row] : [], 'SELECT');
}
// Tenant-based lookup
const tenantMatch = text.match(/WHERE\s+tenant_id\s*=\s*\$1/i);
if (tenantMatch && values?.[0]) {
const rows = Array.from(store.values())
.filter((row) => row.tenantId === values[0] || row.tenant_id === values[0]);
return this.createResult(rows, 'SELECT');
}
// Return all rows
return this.createResult(Array.from(store.values()), 'SELECT');
}
handleInsert(text, values) {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName];
if (!store || !values) {
return this.createResult([], 'INSERT', 0);
}
// Extract column names from query
const columnsMatch = text.match(/\(([^)]+)\)/);
if (!columnsMatch) {
return this.createResult([], 'INSERT', 0);
}
const columns = columnsMatch[1].split(',').map(c => c.trim());
const row = {};
columns.forEach((col, idx) => {
row[col] = values[idx];
});
const id = row.id || `generated-${Date.now()}`;
row.id = id;
store.set(id, row);
// Check for RETURNING clause
if (text.includes('RETURNING')) {
return this.createResult([row], 'INSERT', 1);
}
return this.createResult([], 'INSERT', 1);
}
handleUpdate(text, values) {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName];
if (!store || !values) {
return this.createResult([], 'UPDATE', 0);
}
// Simple ID-based update
const idMatch = text.match(/WHERE\s+id\s*=\s*\$(\d+)/i);
if (idMatch) {
const idParamIndex = parseInt(idMatch[1]) - 1;
const id = values[idParamIndex];
const row = store.get(id);
if (row) {
// Update would happen here in real implementation
return this.createResult([], 'UPDATE', 1);
}
}
return this.createResult([], 'UPDATE', 0);
}
handleDelete(text, values) {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName];
if (!store || !values) {
return this.createResult([], 'DELETE', 0);
}
// Simple ID-based delete
const idMatch = text.match(/WHERE\s+id\s*=\s*\$1/i);
if (idMatch && values[0]) {
const deleted = store.delete(values[0]);
return this.createResult([], 'DELETE', deleted ? 1 : 0);
}
return this.createResult([], 'DELETE', 0);
}
extractTableName(query) {
const fromMatch = query.match(/FROM\s+(\w+)/i);
if (fromMatch)
return fromMatch[1].toLowerCase();
const intoMatch = query.match(/INTO\s+(\w+)/i);
if (intoMatch)
return intoMatch[1].toLowerCase();
const updateMatch = query.match(/UPDATE\s+(\w+)/i);
if (updateMatch)
return updateMatch[1].toLowerCase();
return 'unknown';
}
createResult(rows, command, rowCount) {
return {
rows,
rowCount: rowCount ?? rows.length,
command,
fields: []
};
}
}
exports.MockPool = MockPool;
/**
* Create a mock pool instance
*/
function createMockPool(config) {
return new MockPool({
host: 'localhost',
port: 5432,
database: 'ruvbot_test',
user: 'test',
password: 'test',
...config
});
}
/**
* Mock Pool factory for dependency injection
*/
exports.mockPoolFactory = {
create: vitest_1.vi.fn((config) => createMockPool(config)),
createClient: vitest_1.vi.fn(async (config) => {
const pool = createMockPool(config);
return pool.connect();
})
};
/**
* Postgres query builder mock helpers
*/
exports.queryBuilderHelpers = {
expectQuery: (pool, pattern) => {
return pool.getQueryLog().some(q => pattern.test(q.text));
},
expectQueryCount: (pool, pattern) => {
return pool.getQueryLog().filter(q => pattern.test(q.text)).length;
},
expectTransaction: (pool) => {
const log = pool.getQueryLog();
const hasBegin = log.some(q => q.text.toUpperCase().includes('BEGIN'));
const hasCommitOrRollback = log.some(q => q.text.toUpperCase().includes('COMMIT') ||
q.text.toUpperCase().includes('ROLLBACK'));
return hasBegin && hasCommitOrRollback;
}
};
exports.default = {
MockPool,
createMockPool,
mockPoolFactory: exports.mockPoolFactory,
queryBuilderHelpers: exports.queryBuilderHelpers
};
//# sourceMappingURL=postgres.mock.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,344 @@
/**
* PostgreSQL Mock Module
*
* Mock implementations for Postgres database operations
* Supports transaction testing and query validation
*/
import { vi } from 'vitest';
// Types
export interface QueryResult<T = unknown> {
rows: T[];
rowCount: number;
command: string;
fields: FieldInfo[];
}
export interface FieldInfo {
name: string;
dataTypeID: number;
}
export interface PoolConfig {
host: string;
port: number;
database: string;
user: string;
password: string;
max?: number;
idleTimeoutMillis?: number;
}
export interface PoolClient {
query<T = unknown>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
release(): void;
}
// In-memory data store for mock
interface MockDataStore {
agents: Map<string, unknown>;
sessions: Map<string, unknown>;
memories: Map<string, unknown>;
skills: Map<string, unknown>;
tenants: Map<string, unknown>;
tasks: Map<string, unknown>;
}
/**
* Mock PostgreSQL Pool
*/
export class MockPool {
private connected: boolean = false;
private dataStore: MockDataStore = {
agents: new Map(),
sessions: new Map(),
memories: new Map(),
skills: new Map(),
tenants: new Map(),
tasks: new Map()
};
private queryLog: Array<{ text: string; values?: unknown[]; timestamp: Date }> = [];
private transactionActive: boolean = false;
constructor(private config: PoolConfig) {}
async connect(): Promise<PoolClient> {
this.connected = true;
return this.createClient();
}
async query<T = unknown>(text: string, values?: unknown[]): Promise<QueryResult<T>> {
this.logQuery(text, values);
return this.executeQuery<T>(text, values);
}
async end(): Promise<void> {
this.connected = false;
this.dataStore = {
agents: new Map(),
sessions: new Map(),
memories: new Map(),
skills: new Map(),
tenants: new Map(),
tasks: new Map()
};
}
isConnected(): boolean {
return this.connected;
}
getQueryLog(): Array<{ text: string; values?: unknown[]; timestamp: Date }> {
return [...this.queryLog];
}
clearQueryLog(): void {
this.queryLog = [];
}
// Seed data for testing
seedData(table: keyof MockDataStore, data: Array<{ id: string; [key: string]: unknown }>): void {
for (const row of data) {
this.dataStore[table].set(row.id, row);
}
}
getData(table: keyof MockDataStore): unknown[] {
return Array.from(this.dataStore[table].values());
}
private createClient(): PoolClient {
return {
query: async <T = unknown>(text: string, values?: unknown[]): Promise<QueryResult<T>> => {
return this.executeQuery<T>(text, values);
},
release: () => {
// No-op for mock
}
};
}
private logQuery(text: string, values?: unknown[]): void {
this.queryLog.push({ text, values, timestamp: new Date() });
}
private async executeQuery<T>(text: string, values?: unknown[]): Promise<QueryResult<T>> {
const normalizedQuery = text.trim().toUpperCase();
// Handle transaction commands
if (normalizedQuery === 'BEGIN') {
this.transactionActive = true;
return this.createResult<T>([], 'BEGIN');
}
if (normalizedQuery === 'COMMIT') {
this.transactionActive = false;
return this.createResult<T>([], 'COMMIT');
}
if (normalizedQuery === 'ROLLBACK') {
this.transactionActive = false;
return this.createResult<T>([], 'ROLLBACK');
}
// Parse and execute query
if (normalizedQuery.startsWith('SELECT')) {
return this.handleSelect<T>(text, values);
}
if (normalizedQuery.startsWith('INSERT')) {
return this.handleInsert<T>(text, values);
}
if (normalizedQuery.startsWith('UPDATE')) {
return this.handleUpdate<T>(text, values);
}
if (normalizedQuery.startsWith('DELETE')) {
return this.handleDelete<T>(text, values);
}
// Default: return empty result
return this.createResult<T>([], 'UNKNOWN');
}
private handleSelect<T>(text: string, values?: unknown[]): QueryResult<T> {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName as keyof MockDataStore];
if (!store) {
return this.createResult<T>([], 'SELECT');
}
// Simple ID-based lookup
const idMatch = text.match(/WHERE\s+id\s*=\s*\$1/i);
if (idMatch && values?.[0]) {
const row = store.get(values[0] as string);
return this.createResult<T>(row ? [row as T] : [], 'SELECT');
}
// Tenant-based lookup
const tenantMatch = text.match(/WHERE\s+tenant_id\s*=\s*\$1/i);
if (tenantMatch && values?.[0]) {
const rows = Array.from(store.values())
.filter((row: any) => row.tenantId === values[0] || row.tenant_id === values[0]);
return this.createResult<T>(rows as T[], 'SELECT');
}
// Return all rows
return this.createResult<T>(Array.from(store.values()) as T[], 'SELECT');
}
private handleInsert<T>(text: string, values?: unknown[]): QueryResult<T> {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName as keyof MockDataStore];
if (!store || !values) {
return this.createResult<T>([], 'INSERT', 0);
}
// Extract column names from query
const columnsMatch = text.match(/\(([^)]+)\)/);
if (!columnsMatch) {
return this.createResult<T>([], 'INSERT', 0);
}
const columns = columnsMatch[1].split(',').map(c => c.trim());
const row: Record<string, unknown> = {};
columns.forEach((col, idx) => {
row[col] = values[idx];
});
const id = row.id as string || `generated-${Date.now()}`;
row.id = id;
store.set(id, row);
// Check for RETURNING clause
if (text.includes('RETURNING')) {
return this.createResult<T>([row as T], 'INSERT', 1);
}
return this.createResult<T>([], 'INSERT', 1);
}
private handleUpdate<T>(text: string, values?: unknown[]): QueryResult<T> {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName as keyof MockDataStore];
if (!store || !values) {
return this.createResult<T>([], 'UPDATE', 0);
}
// Simple ID-based update
const idMatch = text.match(/WHERE\s+id\s*=\s*\$(\d+)/i);
if (idMatch) {
const idParamIndex = parseInt(idMatch[1]) - 1;
const id = values[idParamIndex] as string;
const row = store.get(id);
if (row) {
// Update would happen here in real implementation
return this.createResult<T>([], 'UPDATE', 1);
}
}
return this.createResult<T>([], 'UPDATE', 0);
}
private handleDelete<T>(text: string, values?: unknown[]): QueryResult<T> {
const tableName = this.extractTableName(text);
const store = this.dataStore[tableName as keyof MockDataStore];
if (!store || !values) {
return this.createResult<T>([], 'DELETE', 0);
}
// Simple ID-based delete
const idMatch = text.match(/WHERE\s+id\s*=\s*\$1/i);
if (idMatch && values[0]) {
const deleted = store.delete(values[0] as string);
return this.createResult<T>([], 'DELETE', deleted ? 1 : 0);
}
return this.createResult<T>([], 'DELETE', 0);
}
private extractTableName(query: string): string {
const fromMatch = query.match(/FROM\s+(\w+)/i);
if (fromMatch) return fromMatch[1].toLowerCase();
const intoMatch = query.match(/INTO\s+(\w+)/i);
if (intoMatch) return intoMatch[1].toLowerCase();
const updateMatch = query.match(/UPDATE\s+(\w+)/i);
if (updateMatch) return updateMatch[1].toLowerCase();
return 'unknown';
}
private createResult<T>(rows: T[], command: string, rowCount?: number): QueryResult<T> {
return {
rows,
rowCount: rowCount ?? rows.length,
command,
fields: []
};
}
}
/**
* Create a mock pool instance
*/
export function createMockPool(config?: Partial<PoolConfig>): MockPool {
return new MockPool({
host: 'localhost',
port: 5432,
database: 'ruvbot_test',
user: 'test',
password: 'test',
...config
});
}
/**
* Mock Pool factory for dependency injection
*/
export const mockPoolFactory = {
create: vi.fn((config: PoolConfig) => createMockPool(config)),
createClient: vi.fn(async (config: PoolConfig) => {
const pool = createMockPool(config);
return pool.connect();
})
};
/**
* Postgres query builder mock helpers
*/
export const queryBuilderHelpers = {
expectQuery: (pool: MockPool, pattern: RegExp): boolean => {
return pool.getQueryLog().some(q => pattern.test(q.text));
},
expectQueryCount: (pool: MockPool, pattern: RegExp): number => {
return pool.getQueryLog().filter(q => pattern.test(q.text)).length;
},
expectTransaction: (pool: MockPool): boolean => {
const log = pool.getQueryLog();
const hasBegin = log.some(q => q.text.toUpperCase().includes('BEGIN'));
const hasCommitOrRollback = log.some(q =>
q.text.toUpperCase().includes('COMMIT') ||
q.text.toUpperCase().includes('ROLLBACK')
);
return hasBegin && hasCommitOrRollback;
}
};
export default {
MockPool,
createMockPool,
mockPoolFactory,
queryBuilderHelpers
};

View File

@@ -0,0 +1,207 @@
/**
* Slack API Mock Module
*
* Mock implementations for Slack Web API and Events API
*/
export interface SlackMessage {
channel: string;
text: string;
thread_ts?: string;
blocks?: unknown[];
attachments?: unknown[];
metadata?: Record<string, unknown>;
}
export interface SlackResponse {
ok: boolean;
error?: string;
ts?: string;
channel?: string;
message?: Record<string, unknown>;
}
export interface SlackUser {
id: string;
name: string;
real_name: string;
is_bot: boolean;
team_id: string;
}
export interface SlackChannel {
id: string;
name: string;
is_private: boolean;
is_member: boolean;
team_id: string;
}
/**
* Mock Slack Web Client
*/
export declare class MockSlackWebClient {
private messageLog;
private _reactionsData;
private _filesData;
private _usersData;
private _channelsData;
constructor();
chat: {
postMessage: import("vitest").Mock<[args: SlackMessage], Promise<SlackResponse>>;
update: import("vitest").Mock<[args: {
channel: string;
ts: string;
text?: string;
blocks?: unknown[];
}], Promise<SlackResponse>>;
delete: import("vitest").Mock<[args: {
channel: string;
ts: string;
}], Promise<SlackResponse>>;
postEphemeral: import("vitest").Mock<[args: SlackMessage & {
user: string;
}], Promise<SlackResponse>>;
};
conversations: {
info: import("vitest").Mock<[args: {
channel: string;
}], Promise<{
ok: boolean;
channel?: SlackChannel;
}>>;
members: import("vitest").Mock<[args: {
channel: string;
}], Promise<{
ok: boolean;
members: string[];
}>>;
history: import("vitest").Mock<[args: {
channel: string;
limit?: number;
}], Promise<{
ok: boolean;
messages: unknown[];
}>>;
replies: import("vitest").Mock<[args: {
channel: string;
ts: string;
}], Promise<{
ok: boolean;
messages: unknown[];
}>>;
join: import("vitest").Mock<[args: {
channel: string;
}], Promise<SlackResponse>>;
leave: import("vitest").Mock<[args: {
channel: string;
}], Promise<SlackResponse>>;
};
users: {
info: import("vitest").Mock<[args: {
user: string;
}], Promise<{
ok: boolean;
user?: SlackUser;
}>>;
list: import("vitest").Mock<[], Promise<{
ok: boolean;
members: SlackUser[];
}>>;
};
reactions: {
add: import("vitest").Mock<[args: {
channel: string;
timestamp: string;
name: string;
}], Promise<SlackResponse>>;
remove: import("vitest").Mock<[args: {
channel: string;
timestamp: string;
name: string;
}], Promise<SlackResponse>>;
get: import("vitest").Mock<[args: {
channel: string;
timestamp: string;
}], Promise<{
ok: boolean;
message: {
reactions: unknown[];
};
}>>;
};
files: {
upload: import("vitest").Mock<[args: {
channels: string;
content: string;
filename: string;
}], Promise<{
ok: boolean;
file: unknown;
}>>;
delete: import("vitest").Mock<[args: {
file: string;
}], Promise<SlackResponse>>;
};
auth: {
test: import("vitest").Mock<[], Promise<{
ok: boolean;
user_id: string;
team_id: string;
bot_id: string;
}>>;
};
getMessageLog(): SlackMessage[];
clearMessageLog(): void;
getReactions(channel: string, timestamp: string): string[];
addUser(user: SlackUser): void;
addChannel(channel: SlackChannel): void;
reset(): void;
private seedDefaultData;
}
/**
* Mock Slack Events Handler
*/
export declare class MockSlackEventsHandler {
private eventHandlers;
private processedEvents;
on(eventType: string, handler: (event: unknown) => void): void;
off(eventType: string, handler: (event: unknown) => void): void;
emit(eventType: string, event: unknown): Promise<void>;
getProcessedEvents(): unknown[];
clearProcessedEvents(): void;
reset(): void;
}
/**
* Mock Slack Bolt App
*/
export declare class MockSlackBoltApp {
client: MockSlackWebClient;
private eventsHandler;
private messageHandlers;
private actionHandlers;
private commandHandlers;
constructor();
message(pattern: RegExp | string, handler: Function): void;
action(actionId: string | RegExp, handler: Function): void;
command(command: string, handler: Function): void;
event(eventType: string, handler: Function): void;
processMessage(message: {
text: string;
channel: string;
user: string;
ts: string;
thread_ts?: string;
}): Promise<void>;
processAction(actionId: string, payload: unknown): Promise<void>;
processCommand(command: string, payload: unknown): Promise<void>;
start(port?: number): Promise<void>;
stop(): Promise<void>;
reset(): void;
}
export declare function createMockSlackClient(): MockSlackWebClient;
export declare function createMockSlackApp(): MockSlackBoltApp;
declare const _default: {
MockSlackWebClient: typeof MockSlackWebClient;
MockSlackEventsHandler: typeof MockSlackEventsHandler;
MockSlackBoltApp: typeof MockSlackBoltApp;
createMockSlackClient: typeof createMockSlackClient;
createMockSlackApp: typeof createMockSlackApp;
};
export default _default;
//# sourceMappingURL=slack.mock.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"slack.mock.d.ts","sourceRoot":"","sources":["slack.mock.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,347 @@
"use strict";
/**
* Slack API Mock Module
*
* Mock implementations for Slack Web API and Events API
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockSlackBoltApp = exports.MockSlackEventsHandler = exports.MockSlackWebClient = void 0;
exports.createMockSlackClient = createMockSlackClient;
exports.createMockSlackApp = createMockSlackApp;
const vitest_1 = require("vitest");
/**
* Mock Slack Web Client
*/
class MockSlackWebClient {
constructor() {
this.messageLog = [];
this._reactionsData = new Map();
this._filesData = new Map();
// User and channel data
this._usersData = new Map();
this._channelsData = new Map();
// Chat API
this.chat = {
postMessage: vitest_1.vi.fn(async (args) => {
this.messageLog.push(args);
const ts = `${Date.now()}.${Math.random().toString().slice(2, 8)}`;
return {
ok: true,
ts,
channel: args.channel,
message: {
text: args.text,
ts,
user: 'U_BOT',
type: 'message'
}
};
}),
update: vitest_1.vi.fn(async (args) => {
return {
ok: true,
ts: args.ts,
channel: args.channel
};
}),
delete: vitest_1.vi.fn(async (args) => {
return {
ok: true,
ts: args.ts,
channel: args.channel
};
}),
postEphemeral: vitest_1.vi.fn(async (args) => {
this.messageLog.push(args);
return {
ok: true,
message_ts: `${Date.now()}.${Math.random().toString().slice(2, 8)}`
};
})
};
// Conversations API
this.conversations = {
info: vitest_1.vi.fn(async (args) => {
const channel = this._channelsData.get(args.channel);
return {
ok: !!channel,
channel
};
}),
members: vitest_1.vi.fn(async (args) => {
return {
ok: true,
members: ['U12345678', 'U87654321', 'U_BOT']
};
}),
history: vitest_1.vi.fn(async (args) => {
return {
ok: true,
messages: this.messageLog
.filter(m => m.channel === args.channel)
.slice(0, args.limit || 100)
};
}),
replies: vitest_1.vi.fn(async (args) => {
return {
ok: true,
messages: this.messageLog
.filter(m => m.channel === args.channel && m.thread_ts === args.ts)
};
}),
join: vitest_1.vi.fn(async (args) => {
return { ok: true, channel: args.channel };
}),
leave: vitest_1.vi.fn(async (args) => {
return { ok: true };
})
};
// Users API
this.users = {
info: vitest_1.vi.fn(async (args) => {
const user = this._usersData.get(args.user);
return {
ok: !!user,
user
};
}),
list: vitest_1.vi.fn(async () => {
return {
ok: true,
members: Array.from(this._usersData.values())
};
})
};
// Reactions API
this.reactions = {
add: vitest_1.vi.fn(async (args) => {
const key = `${args.channel}:${args.timestamp}`;
const existing = this._reactionsData.get(key) || [];
this._reactionsData.set(key, [...existing, args.name]);
return { ok: true };
}),
remove: vitest_1.vi.fn(async (args) => {
const key = `${args.channel}:${args.timestamp}`;
const existing = this._reactionsData.get(key) || [];
this._reactionsData.set(key, existing.filter(r => r !== args.name));
return { ok: true };
}),
get: vitest_1.vi.fn(async (args) => {
const key = `${args.channel}:${args.timestamp}`;
const reactions = this._reactionsData.get(key) || [];
return {
ok: true,
message: {
reactions: reactions.map(name => ({ name, count: 1, users: ['U12345678'] }))
}
};
})
};
// Files API
this.files = {
upload: vitest_1.vi.fn(async (args) => {
const fileId = `F${Date.now()}`;
const file = { id: fileId, name: args.filename, content: args.content };
this._filesData.set(fileId, file);
return { ok: true, file };
}),
delete: vitest_1.vi.fn(async (args) => {
this._filesData.delete(args.file);
return { ok: true };
})
};
// Auth API
this.auth = {
test: vitest_1.vi.fn(async () => {
return {
ok: true,
user_id: 'U_BOT',
team_id: 'T12345678',
bot_id: 'B12345678'
};
})
};
// Seed default test data
this.seedDefaultData();
}
// Test helpers
getMessageLog() {
return [...this.messageLog];
}
clearMessageLog() {
this.messageLog = [];
}
getReactions(channel, timestamp) {
return this._reactionsData.get(`${channel}:${timestamp}`) || [];
}
addUser(user) {
this._usersData.set(user.id, user);
}
addChannel(channel) {
this._channelsData.set(channel.id, channel);
}
reset() {
this.messageLog = [];
this._reactionsData.clear();
this._filesData.clear();
this.seedDefaultData();
// Reset all mocks
vitest_1.vi.clearAllMocks();
}
seedDefaultData() {
// Default users
this._usersData.set('U12345678', {
id: 'U12345678',
name: 'testuser',
real_name: 'Test User',
is_bot: false,
team_id: 'T12345678'
});
this._usersData.set('U_BOT', {
id: 'U_BOT',
name: 'ruvbot',
real_name: 'RuvBot',
is_bot: true,
team_id: 'T12345678'
});
// Default channels
this._channelsData.set('C12345678', {
id: 'C12345678',
name: 'general',
is_private: false,
is_member: true,
team_id: 'T12345678'
});
}
}
exports.MockSlackWebClient = MockSlackWebClient;
/**
* Mock Slack Events Handler
*/
class MockSlackEventsHandler {
constructor() {
this.eventHandlers = new Map();
this.processedEvents = [];
}
on(eventType, handler) {
const handlers = this.eventHandlers.get(eventType) || [];
handlers.push(handler);
this.eventHandlers.set(eventType, handlers);
}
off(eventType, handler) {
const handlers = this.eventHandlers.get(eventType) || [];
this.eventHandlers.set(eventType, handlers.filter(h => h !== handler));
}
async emit(eventType, event) {
const handlers = this.eventHandlers.get(eventType) || [];
this.processedEvents.push({ type: eventType, event, timestamp: new Date() });
for (const handler of handlers) {
await handler(event);
}
}
getProcessedEvents() {
return [...this.processedEvents];
}
clearProcessedEvents() {
this.processedEvents = [];
}
reset() {
this.eventHandlers.clear();
this.processedEvents = [];
}
}
exports.MockSlackEventsHandler = MockSlackEventsHandler;
/**
* Mock Slack Bolt App
*/
class MockSlackBoltApp {
constructor() {
this.messageHandlers = [];
this.actionHandlers = new Map();
this.commandHandlers = new Map();
this.client = new MockSlackWebClient();
this.eventsHandler = new MockSlackEventsHandler();
}
message(pattern, handler) {
this.messageHandlers.push({ pattern, handler });
}
action(actionId, handler) {
this.actionHandlers.set(actionId.toString(), handler);
}
command(command, handler) {
this.commandHandlers.set(command, handler);
}
event(eventType, handler) {
this.eventsHandler.on(eventType, handler);
}
async processMessage(message) {
for (const { pattern, handler } of this.messageHandlers) {
const matches = typeof pattern === 'string'
? message.text.includes(pattern)
: pattern.test(message.text);
if (matches) {
const context = {
say: vitest_1.vi.fn(this.client.chat.postMessage),
client: this.client,
message,
event: message
};
await handler(context);
}
}
}
async processAction(actionId, payload) {
const handler = this.actionHandlers.get(actionId);
if (handler) {
const context = {
ack: vitest_1.vi.fn(async () => { }),
respond: vitest_1.vi.fn(async () => { }),
client: this.client,
body: payload,
action: { action_id: actionId }
};
await handler(context);
}
}
async processCommand(command, payload) {
const handler = this.commandHandlers.get(command);
if (handler) {
const context = {
ack: vitest_1.vi.fn(async () => { }),
respond: vitest_1.vi.fn(async () => { }),
client: this.client,
command: payload
};
await handler(context);
}
}
async start(port) {
// No-op for mock
}
async stop() {
// No-op for mock
}
reset() {
this.client.reset();
this.eventsHandler.reset();
this.messageHandlers = [];
this.actionHandlers.clear();
this.commandHandlers.clear();
}
}
exports.MockSlackBoltApp = MockSlackBoltApp;
// Factory functions
function createMockSlackClient() {
return new MockSlackWebClient();
}
function createMockSlackApp() {
return new MockSlackBoltApp();
}
exports.default = {
MockSlackWebClient,
MockSlackEventsHandler,
MockSlackBoltApp,
createMockSlackClient,
createMockSlackApp
};
//# sourceMappingURL=slack.mock.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,428 @@
/**
* Slack API Mock Module
*
* Mock implementations for Slack Web API and Events API
*/
import { vi } from 'vitest';
// Types
export interface SlackMessage {
channel: string;
text: string;
thread_ts?: string;
blocks?: unknown[];
attachments?: unknown[];
metadata?: Record<string, unknown>;
}
export interface SlackResponse {
ok: boolean;
error?: string;
ts?: string;
channel?: string;
message?: Record<string, unknown>;
}
export interface SlackUser {
id: string;
name: string;
real_name: string;
is_bot: boolean;
team_id: string;
}
export interface SlackChannel {
id: string;
name: string;
is_private: boolean;
is_member: boolean;
team_id: string;
}
/**
* Mock Slack Web Client
*/
export class MockSlackWebClient {
private messageLog: SlackMessage[] = [];
private _reactionsData: Map<string, string[]> = new Map();
private _filesData: Map<string, unknown> = new Map();
// User and channel data
private _usersData: Map<string, SlackUser> = new Map();
private _channelsData: Map<string, SlackChannel> = new Map();
constructor() {
// Seed default test data
this.seedDefaultData();
}
// Chat API
chat = {
postMessage: vi.fn(async (args: SlackMessage): Promise<SlackResponse> => {
this.messageLog.push(args);
const ts = `${Date.now()}.${Math.random().toString().slice(2, 8)}`;
return {
ok: true,
ts,
channel: args.channel,
message: {
text: args.text,
ts,
user: 'U_BOT',
type: 'message'
}
};
}),
update: vi.fn(async (args: { channel: string; ts: string; text?: string; blocks?: unknown[] }): Promise<SlackResponse> => {
return {
ok: true,
ts: args.ts,
channel: args.channel
};
}),
delete: vi.fn(async (args: { channel: string; ts: string }): Promise<SlackResponse> => {
return {
ok: true,
ts: args.ts,
channel: args.channel
};
}),
postEphemeral: vi.fn(async (args: SlackMessage & { user: string }): Promise<SlackResponse> => {
this.messageLog.push(args);
return {
ok: true,
message_ts: `${Date.now()}.${Math.random().toString().slice(2, 8)}`
} as SlackResponse;
})
};
// Conversations API
conversations = {
info: vi.fn(async (args: { channel: string }): Promise<{ ok: boolean; channel?: SlackChannel }> => {
const channel = this._channelsData.get(args.channel);
return {
ok: !!channel,
channel
};
}),
members: vi.fn(async (args: { channel: string }): Promise<{ ok: boolean; members: string[] }> => {
return {
ok: true,
members: ['U12345678', 'U87654321', 'U_BOT']
};
}),
history: vi.fn(async (args: { channel: string; limit?: number }): Promise<{ ok: boolean; messages: unknown[] }> => {
return {
ok: true,
messages: this.messageLog
.filter(m => m.channel === args.channel)
.slice(0, args.limit || 100)
};
}),
replies: vi.fn(async (args: { channel: string; ts: string }): Promise<{ ok: boolean; messages: unknown[] }> => {
return {
ok: true,
messages: this.messageLog
.filter(m => m.channel === args.channel && m.thread_ts === args.ts)
};
}),
join: vi.fn(async (args: { channel: string }): Promise<SlackResponse> => {
return { ok: true, channel: args.channel };
}),
leave: vi.fn(async (args: { channel: string }): Promise<SlackResponse> => {
return { ok: true };
})
};
// Users API
users = {
info: vi.fn(async (args: { user: string }): Promise<{ ok: boolean; user?: SlackUser }> => {
const user = this._usersData.get(args.user);
return {
ok: !!user,
user
};
}),
list: vi.fn(async (): Promise<{ ok: boolean; members: SlackUser[] }> => {
return {
ok: true,
members: Array.from(this._usersData.values())
};
})
};
// Reactions API
reactions = {
add: vi.fn(async (args: { channel: string; timestamp: string; name: string }): Promise<SlackResponse> => {
const key = `${args.channel}:${args.timestamp}`;
const existing = this._reactionsData.get(key) || [];
this._reactionsData.set(key, [...existing, args.name]);
return { ok: true };
}),
remove: vi.fn(async (args: { channel: string; timestamp: string; name: string }): Promise<SlackResponse> => {
const key = `${args.channel}:${args.timestamp}`;
const existing = this._reactionsData.get(key) || [];
this._reactionsData.set(key, existing.filter(r => r !== args.name));
return { ok: true };
}),
get: vi.fn(async (args: { channel: string; timestamp: string }): Promise<{ ok: boolean; message: { reactions: unknown[] } }> => {
const key = `${args.channel}:${args.timestamp}`;
const reactions = this._reactionsData.get(key) || [];
return {
ok: true,
message: {
reactions: reactions.map(name => ({ name, count: 1, users: ['U12345678'] }))
}
};
})
};
// Files API
files = {
upload: vi.fn(async (args: { channels: string; content: string; filename: string }): Promise<{ ok: boolean; file: unknown }> => {
const fileId = `F${Date.now()}`;
const file = { id: fileId, name: args.filename, content: args.content };
this._filesData.set(fileId, file);
return { ok: true, file };
}),
delete: vi.fn(async (args: { file: string }): Promise<SlackResponse> => {
this._filesData.delete(args.file);
return { ok: true };
})
};
// Auth API
auth = {
test: vi.fn(async (): Promise<{ ok: boolean; user_id: string; team_id: string; bot_id: string }> => {
return {
ok: true,
user_id: 'U_BOT',
team_id: 'T12345678',
bot_id: 'B12345678'
};
})
};
// Test helpers
getMessageLog(): SlackMessage[] {
return [...this.messageLog];
}
clearMessageLog(): void {
this.messageLog = [];
}
getReactions(channel: string, timestamp: string): string[] {
return this._reactionsData.get(`${channel}:${timestamp}`) || [];
}
addUser(user: SlackUser): void {
this._usersData.set(user.id, user);
}
addChannel(channel: SlackChannel): void {
this._channelsData.set(channel.id, channel);
}
reset(): void {
this.messageLog = [];
this._reactionsData.clear();
this._filesData.clear();
this.seedDefaultData();
// Reset all mocks
vi.clearAllMocks();
}
private seedDefaultData(): void {
// Default users
this._usersData.set('U12345678', {
id: 'U12345678',
name: 'testuser',
real_name: 'Test User',
is_bot: false,
team_id: 'T12345678'
});
this._usersData.set('U_BOT', {
id: 'U_BOT',
name: 'ruvbot',
real_name: 'RuvBot',
is_bot: true,
team_id: 'T12345678'
});
// Default channels
this._channelsData.set('C12345678', {
id: 'C12345678',
name: 'general',
is_private: false,
is_member: true,
team_id: 'T12345678'
});
}
}
/**
* Mock Slack Events Handler
*/
export class MockSlackEventsHandler {
private eventHandlers: Map<string, Array<(event: unknown) => void>> = new Map();
private processedEvents: unknown[] = [];
on(eventType: string, handler: (event: unknown) => void): void {
const handlers = this.eventHandlers.get(eventType) || [];
handlers.push(handler);
this.eventHandlers.set(eventType, handlers);
}
off(eventType: string, handler: (event: unknown) => void): void {
const handlers = this.eventHandlers.get(eventType) || [];
this.eventHandlers.set(eventType, handlers.filter(h => h !== handler));
}
async emit(eventType: string, event: unknown): Promise<void> {
const handlers = this.eventHandlers.get(eventType) || [];
this.processedEvents.push({ type: eventType, event, timestamp: new Date() });
for (const handler of handlers) {
await handler(event);
}
}
getProcessedEvents(): unknown[] {
return [...this.processedEvents];
}
clearProcessedEvents(): void {
this.processedEvents = [];
}
reset(): void {
this.eventHandlers.clear();
this.processedEvents = [];
}
}
/**
* Mock Slack Bolt App
*/
export class MockSlackBoltApp {
client: MockSlackWebClient;
private eventsHandler: MockSlackEventsHandler;
private messageHandlers: Array<{ pattern: RegExp | string; handler: Function }> = [];
private actionHandlers: Map<string, Function> = new Map();
private commandHandlers: Map<string, Function> = new Map();
constructor() {
this.client = new MockSlackWebClient();
this.eventsHandler = new MockSlackEventsHandler();
}
message(pattern: RegExp | string, handler: Function): void {
this.messageHandlers.push({ pattern, handler });
}
action(actionId: string | RegExp, handler: Function): void {
this.actionHandlers.set(actionId.toString(), handler);
}
command(command: string, handler: Function): void {
this.commandHandlers.set(command, handler);
}
event(eventType: string, handler: Function): void {
this.eventsHandler.on(eventType, handler as (event: unknown) => void);
}
async processMessage(message: { text: string; channel: string; user: string; ts: string; thread_ts?: string }): Promise<void> {
for (const { pattern, handler } of this.messageHandlers) {
const matches = typeof pattern === 'string'
? message.text.includes(pattern)
: pattern.test(message.text);
if (matches) {
const context = {
say: vi.fn(this.client.chat.postMessage),
client: this.client,
message,
event: message
};
await handler(context);
}
}
}
async processAction(actionId: string, payload: unknown): Promise<void> {
const handler = this.actionHandlers.get(actionId);
if (handler) {
const context = {
ack: vi.fn(async () => {}),
respond: vi.fn(async () => {}),
client: this.client,
body: payload,
action: { action_id: actionId }
};
await handler(context);
}
}
async processCommand(command: string, payload: unknown): Promise<void> {
const handler = this.commandHandlers.get(command);
if (handler) {
const context = {
ack: vi.fn(async () => {}),
respond: vi.fn(async () => {}),
client: this.client,
command: payload
};
await handler(context);
}
}
async start(port?: number): Promise<void> {
// No-op for mock
}
async stop(): Promise<void> {
// No-op for mock
}
reset(): void {
this.client.reset();
this.eventsHandler.reset();
this.messageHandlers = [];
this.actionHandlers.clear();
this.commandHandlers.clear();
}
}
// Factory functions
export function createMockSlackClient(): MockSlackWebClient {
return new MockSlackWebClient();
}
export function createMockSlackApp(): MockSlackBoltApp {
return new MockSlackBoltApp();
}
export default {
MockSlackWebClient,
MockSlackEventsHandler,
MockSlackBoltApp,
createMockSlackClient,
createMockSlackApp
};

View File

@@ -0,0 +1,118 @@
/**
* WASM Mock Module
*
* Mock implementations for RuVector WASM bindings
* Used to test code that depends on WASM modules without loading actual binaries
*/
export interface WasmVectorIndex {
add(id: string, vector: Float32Array): void;
search(query: Float32Array, topK: number): SearchResult[];
delete(id: string): boolean;
size(): number;
clear(): void;
}
export interface SearchResult {
id: string;
score: number;
distance: number;
}
export interface WasmEmbedder {
embed(text: string): Float32Array;
embedBatch(texts: string[]): Float32Array[];
dimension(): number;
}
export interface WasmRouter {
route(input: string, context?: Record<string, unknown>): RouteResult;
addRoute(pattern: string, handler: string): void;
removeRoute(pattern: string): boolean;
}
export interface RouteResult {
handler: string;
confidence: number;
metadata: Record<string, unknown>;
}
/**
* Mock WASM Vector Index
*/
export declare class MockWasmVectorIndex implements WasmVectorIndex {
private vectors;
private dimension;
constructor(dimension?: number);
add(id: string, vector: Float32Array): void;
search(query: Float32Array, topK: number): SearchResult[];
delete(id: string): boolean;
size(): number;
clear(): void;
private cosineSimilarity;
}
/**
* Mock WASM Embedder
*/
export declare class MockWasmEmbedder implements WasmEmbedder {
private dim;
private cache;
constructor(dimension?: number);
embed(text: string): Float32Array;
embedBatch(texts: string[]): Float32Array[];
dimension(): number;
private hashCode;
}
/**
* Mock WASM Router
*/
export declare class MockWasmRouter implements WasmRouter {
private routes;
route(input: string, context?: Record<string, unknown>): RouteResult;
addRoute(pattern: string, handler: string): void;
removeRoute(pattern: string): boolean;
}
/**
* Mock WASM Module Loader
*/
export declare const mockWasmLoader: {
loadVectorIndex: import("vitest").Mock<[dimension?: number | undefined], Promise<WasmVectorIndex>>;
loadEmbedder: import("vitest").Mock<[dimension?: number | undefined], Promise<WasmEmbedder>>;
loadRouter: import("vitest").Mock<[], Promise<WasmRouter>>;
isWasmSupported: import("vitest").Mock<[], boolean>;
getWasmMemory: import("vitest").Mock<[], {
used: number;
total: number;
}>;
};
/**
* Create mock WASM bindings for RuVector
*/
export declare function createMockRuVectorBindings(): {
vectorIndex: MockWasmVectorIndex;
embedder: MockWasmEmbedder;
router: MockWasmRouter;
search(query: string, topK?: number): Promise<SearchResult[]>;
index(id: string, text: string): Promise<void>;
batchIndex(items: Array<{
id: string;
text: string;
}>): Promise<void>;
};
/**
* Reset all WASM mocks
*/
export declare function resetWasmMocks(): void;
declare const _default: {
MockWasmVectorIndex: typeof MockWasmVectorIndex;
MockWasmEmbedder: typeof MockWasmEmbedder;
MockWasmRouter: typeof MockWasmRouter;
mockWasmLoader: {
loadVectorIndex: import("vitest").Mock<[dimension?: number | undefined], Promise<WasmVectorIndex>>;
loadEmbedder: import("vitest").Mock<[dimension?: number | undefined], Promise<WasmEmbedder>>;
loadRouter: import("vitest").Mock<[], Promise<WasmRouter>>;
isWasmSupported: import("vitest").Mock<[], boolean>;
getWasmMemory: import("vitest").Mock<[], {
used: number;
total: number;
}>;
};
createMockRuVectorBindings: typeof createMockRuVectorBindings;
resetWasmMocks: typeof resetWasmMocks;
};
export default _default;
//# sourceMappingURL=wasm.mock.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"wasm.mock.d.ts","sourceRoot":"","sources":["wasm.mock.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,212 @@
"use strict";
/**
* WASM Mock Module
*
* Mock implementations for RuVector WASM bindings
* Used to test code that depends on WASM modules without loading actual binaries
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.mockWasmLoader = exports.MockWasmRouter = exports.MockWasmEmbedder = exports.MockWasmVectorIndex = void 0;
exports.createMockRuVectorBindings = createMockRuVectorBindings;
exports.resetWasmMocks = resetWasmMocks;
const vitest_1 = require("vitest");
// Mock implementations
/**
* Mock WASM Vector Index
*/
class MockWasmVectorIndex {
constructor(dimension = 384) {
this.vectors = new Map();
this.dimension = dimension;
}
add(id, vector) {
if (vector.length !== this.dimension) {
throw new Error(`Vector dimension mismatch: expected ${this.dimension}, got ${vector.length}`);
}
this.vectors.set(id, vector);
}
search(query, topK) {
if (query.length !== this.dimension) {
throw new Error(`Query dimension mismatch: expected ${this.dimension}, got ${query.length}`);
}
const results = [];
for (const [id, vector] of this.vectors) {
const distance = this.cosineSimilarity(query, vector);
results.push({
id,
score: distance,
distance: 1 - distance
});
}
return results
.sort((a, b) => b.score - a.score)
.slice(0, topK);
}
delete(id) {
return this.vectors.delete(id);
}
size() {
return this.vectors.size;
}
clear() {
this.vectors.clear();
}
cosineSimilarity(a, b) {
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
exports.MockWasmVectorIndex = MockWasmVectorIndex;
/**
* Mock WASM Embedder
*/
class MockWasmEmbedder {
constructor(dimension = 384) {
this.cache = new Map();
this.dim = dimension;
}
embed(text) {
// Check cache first
if (this.cache.has(text)) {
return this.cache.get(text);
}
// Generate deterministic pseudo-random embedding based on text hash
const embedding = new Float32Array(this.dim);
let hash = this.hashCode(text);
for (let i = 0; i < this.dim; i++) {
hash = ((hash * 1103515245) + 12345) & 0x7fffffff;
embedding[i] = (hash / 0x7fffffff) * 2 - 1;
}
// Normalize the embedding
const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
for (let i = 0; i < this.dim; i++) {
embedding[i] /= norm;
}
this.cache.set(text, embedding);
return embedding;
}
embedBatch(texts) {
return texts.map(text => this.embed(text));
}
dimension() {
return this.dim;
}
hashCode(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash);
}
}
exports.MockWasmEmbedder = MockWasmEmbedder;
/**
* Mock WASM Router
*/
class MockWasmRouter {
constructor() {
this.routes = new Map();
}
route(input, context) {
for (const [key, route] of this.routes) {
if (route.pattern.test(input)) {
return {
handler: route.handler,
confidence: 0.95,
metadata: { matchedPattern: key, context }
};
}
}
// Default fallback
return {
handler: 'default',
confidence: 0.5,
metadata: { fallback: true, context }
};
}
addRoute(pattern, handler) {
this.routes.set(pattern, {
pattern: new RegExp(pattern, 'i'),
handler
});
}
removeRoute(pattern) {
return this.routes.delete(pattern);
}
}
exports.MockWasmRouter = MockWasmRouter;
/**
* Mock WASM Module Loader
*/
exports.mockWasmLoader = {
loadVectorIndex: vitest_1.vi.fn(async (dimension) => {
return new MockWasmVectorIndex(dimension);
}),
loadEmbedder: vitest_1.vi.fn(async (dimension) => {
return new MockWasmEmbedder(dimension);
}),
loadRouter: vitest_1.vi.fn(async () => {
return new MockWasmRouter();
}),
isWasmSupported: vitest_1.vi.fn(() => true),
getWasmMemory: vitest_1.vi.fn(() => ({
used: 1024 * 1024 * 50, // 50MB
total: 1024 * 1024 * 256 // 256MB
}))
};
/**
* Create mock WASM bindings for RuVector
*/
function createMockRuVectorBindings() {
const vectorIndex = new MockWasmVectorIndex(384);
const embedder = new MockWasmEmbedder(384);
const router = new MockWasmRouter();
return {
vectorIndex,
embedder,
router,
// Convenience methods
async search(query, topK = 10) {
const embedding = embedder.embed(query);
return vectorIndex.search(embedding, topK);
},
async index(id, text) {
const embedding = embedder.embed(text);
vectorIndex.add(id, embedding);
},
async batchIndex(items) {
for (const item of items) {
const embedding = embedder.embed(item.text);
vectorIndex.add(item.id, embedding);
}
}
};
}
/**
* Reset all WASM mocks
*/
function resetWasmMocks() {
vitest_1.vi.clearAllMocks();
exports.mockWasmLoader.loadVectorIndex.mockClear();
exports.mockWasmLoader.loadEmbedder.mockClear();
exports.mockWasmLoader.loadRouter.mockClear();
}
// Default export for easy mocking
exports.default = {
MockWasmVectorIndex,
MockWasmEmbedder,
MockWasmRouter,
mockWasmLoader: exports.mockWasmLoader,
createMockRuVectorBindings,
resetWasmMocks
};
//# sourceMappingURL=wasm.mock.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,278 @@
/**
* WASM Mock Module
*
* Mock implementations for RuVector WASM bindings
* Used to test code that depends on WASM modules without loading actual binaries
*/
import { vi } from 'vitest';
// Types for WASM interfaces
export interface WasmVectorIndex {
add(id: string, vector: Float32Array): void;
search(query: Float32Array, topK: number): SearchResult[];
delete(id: string): boolean;
size(): number;
clear(): void;
}
export interface SearchResult {
id: string;
score: number;
distance: number;
}
export interface WasmEmbedder {
embed(text: string): Float32Array;
embedBatch(texts: string[]): Float32Array[];
dimension(): number;
}
export interface WasmRouter {
route(input: string, context?: Record<string, unknown>): RouteResult;
addRoute(pattern: string, handler: string): void;
removeRoute(pattern: string): boolean;
}
export interface RouteResult {
handler: string;
confidence: number;
metadata: Record<string, unknown>;
}
// Mock implementations
/**
* Mock WASM Vector Index
*/
export class MockWasmVectorIndex implements WasmVectorIndex {
private vectors: Map<string, Float32Array> = new Map();
private dimension: number;
constructor(dimension: number = 384) {
this.dimension = dimension;
}
add(id: string, vector: Float32Array): void {
if (vector.length !== this.dimension) {
throw new Error(`Vector dimension mismatch: expected ${this.dimension}, got ${vector.length}`);
}
this.vectors.set(id, vector);
}
search(query: Float32Array, topK: number): SearchResult[] {
if (query.length !== this.dimension) {
throw new Error(`Query dimension mismatch: expected ${this.dimension}, got ${query.length}`);
}
const results: SearchResult[] = [];
for (const [id, vector] of this.vectors) {
const distance = this.cosineSimilarity(query, vector);
results.push({
id,
score: distance,
distance: 1 - distance
});
}
return results
.sort((a, b) => b.score - a.score)
.slice(0, topK);
}
delete(id: string): boolean {
return this.vectors.delete(id);
}
size(): number {
return this.vectors.size;
}
clear(): void {
this.vectors.clear();
}
private cosineSimilarity(a: Float32Array, b: Float32Array): number {
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
/**
* Mock WASM Embedder
*/
export class MockWasmEmbedder implements WasmEmbedder {
private dim: number;
private cache: Map<string, Float32Array> = new Map();
constructor(dimension: number = 384) {
this.dim = dimension;
}
embed(text: string): Float32Array {
// Check cache first
if (this.cache.has(text)) {
return this.cache.get(text)!;
}
// Generate deterministic pseudo-random embedding based on text hash
const embedding = new Float32Array(this.dim);
let hash = this.hashCode(text);
for (let i = 0; i < this.dim; i++) {
hash = ((hash * 1103515245) + 12345) & 0x7fffffff;
embedding[i] = (hash / 0x7fffffff) * 2 - 1;
}
// Normalize the embedding
const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
for (let i = 0; i < this.dim; i++) {
embedding[i] /= norm;
}
this.cache.set(text, embedding);
return embedding;
}
embedBatch(texts: string[]): Float32Array[] {
return texts.map(text => this.embed(text));
}
dimension(): number {
return this.dim;
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash);
}
}
/**
* Mock WASM Router
*/
export class MockWasmRouter implements WasmRouter {
private routes: Map<string, { pattern: RegExp; handler: string }> = new Map();
route(input: string, context?: Record<string, unknown>): RouteResult {
for (const [key, route] of this.routes) {
if (route.pattern.test(input)) {
return {
handler: route.handler,
confidence: 0.95,
metadata: { matchedPattern: key, context }
};
}
}
// Default fallback
return {
handler: 'default',
confidence: 0.5,
metadata: { fallback: true, context }
};
}
addRoute(pattern: string, handler: string): void {
this.routes.set(pattern, {
pattern: new RegExp(pattern, 'i'),
handler
});
}
removeRoute(pattern: string): boolean {
return this.routes.delete(pattern);
}
}
/**
* Mock WASM Module Loader
*/
export const mockWasmLoader = {
loadVectorIndex: vi.fn(async (dimension?: number): Promise<WasmVectorIndex> => {
return new MockWasmVectorIndex(dimension);
}),
loadEmbedder: vi.fn(async (dimension?: number): Promise<WasmEmbedder> => {
return new MockWasmEmbedder(dimension);
}),
loadRouter: vi.fn(async (): Promise<WasmRouter> => {
return new MockWasmRouter();
}),
isWasmSupported: vi.fn((): boolean => true),
getWasmMemory: vi.fn((): { used: number; total: number } => ({
used: 1024 * 1024 * 50, // 50MB
total: 1024 * 1024 * 256 // 256MB
}))
};
/**
* Create mock WASM bindings for RuVector
*/
export function createMockRuVectorBindings() {
const vectorIndex = new MockWasmVectorIndex(384);
const embedder = new MockWasmEmbedder(384);
const router = new MockWasmRouter();
return {
vectorIndex,
embedder,
router,
// Convenience methods
async search(query: string, topK: number = 10): Promise<SearchResult[]> {
const embedding = embedder.embed(query);
return vectorIndex.search(embedding, topK);
},
async index(id: string, text: string): Promise<void> {
const embedding = embedder.embed(text);
vectorIndex.add(id, embedding);
},
async batchIndex(items: Array<{ id: string; text: string }>): Promise<void> {
for (const item of items) {
const embedding = embedder.embed(item.text);
vectorIndex.add(item.id, embedding);
}
}
};
}
/**
* Reset all WASM mocks
*/
export function resetWasmMocks(): void {
vi.clearAllMocks();
mockWasmLoader.loadVectorIndex.mockClear();
mockWasmLoader.loadEmbedder.mockClear();
mockWasmLoader.loadRouter.mockClear();
}
// Default export for easy mocking
export default {
MockWasmVectorIndex,
MockWasmEmbedder,
MockWasmRouter,
mockWasmLoader,
createMockRuVectorBindings,
resetWasmMocks
};