mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-04 08:30:39 +00:00
test: add tests for services in CoreModule
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { AnomalyService, DENY_SERVICE_INSTRUCTION } from './AnomalyService';
|
||||
|
||||
describe('AnomalyService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'anomaly': AnomalyService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const anomalyService = testKernel.services!.get('anomaly') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(anomalyService).toBeInstanceOf(AnomalyService);
|
||||
});
|
||||
|
||||
it('should have types object', () => {
|
||||
expect(anomalyService.types).toBeDefined();
|
||||
expect(typeof anomalyService.types).toBe('object');
|
||||
});
|
||||
|
||||
it('should register a type with handler', () => {
|
||||
const handler = vi.fn();
|
||||
anomalyService.register('test-type', { handler });
|
||||
|
||||
expect(anomalyService.types['test-type']).toBeDefined();
|
||||
expect(anomalyService.types['test-type'].handler).toBe(handler);
|
||||
});
|
||||
|
||||
it('should register a type with threshold', () => {
|
||||
anomalyService.register('threshold-type', { high: 100 });
|
||||
|
||||
expect(anomalyService.types['threshold-type']).toBeDefined();
|
||||
expect(anomalyService.types['threshold-type'].handler).toBeDefined();
|
||||
expect(typeof anomalyService.types['threshold-type'].handler).toBe('function');
|
||||
});
|
||||
|
||||
it('should call handler when noting anomaly', async () => {
|
||||
const handler = vi.fn().mockReturnValue('result');
|
||||
anomalyService.register('callable-type', { handler });
|
||||
|
||||
const data = { test: 'data' };
|
||||
const result = await anomalyService.note('callable-type', data);
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(data);
|
||||
expect(result).toBe('result');
|
||||
});
|
||||
|
||||
it('should return undefined for unregistered type', async () => {
|
||||
const result = await anomalyService.note('non-existent-type', {});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should trigger threshold handler when value exceeds high', async () => {
|
||||
anomalyService.register('high-threshold', { high: 50 });
|
||||
|
||||
const result = await anomalyService.note('high-threshold', { value: 75 });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toBeInstanceOf(Set);
|
||||
expect(result.has(DENY_SERVICE_INSTRUCTION)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not trigger threshold handler when value is below high', async () => {
|
||||
anomalyService.register('low-threshold', { high: 100 });
|
||||
|
||||
const result = await anomalyService.note('low-threshold', { value: 50 });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle multiple type registrations', () => {
|
||||
anomalyService.register('type1', { handler: () => {} });
|
||||
anomalyService.register('type2', { high: 100 });
|
||||
anomalyService.register('type3', { handler: () => {} });
|
||||
|
||||
expect(anomalyService.types['type1']).toBeDefined();
|
||||
expect(anomalyService.types['type2']).toBeDefined();
|
||||
expect(anomalyService.types['type3']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should store config in type instance', () => {
|
||||
const config = { high: 200, custom: 'value' };
|
||||
anomalyService.register('config-type', config);
|
||||
|
||||
expect(anomalyService.types['config-type'].config).toBe(config);
|
||||
});
|
||||
|
||||
it('should handle exact threshold value', async () => {
|
||||
anomalyService.register('exact-threshold', { high: 100 });
|
||||
|
||||
const result = await anomalyService.note('exact-threshold', { value: 100 });
|
||||
|
||||
// Threshold uses > not >=, so equal should not trigger
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle value just over threshold', async () => {
|
||||
anomalyService.register('just-over', { high: 100 });
|
||||
|
||||
const result = await anomalyService.note('just-over', { value: 100.1 });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toBeInstanceOf(Set);
|
||||
expect(result.has(DENY_SERVICE_INSTRUCTION)).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow custom handler to return any value', async () => {
|
||||
const customResult = { custom: 'result', data: [1, 2, 3] };
|
||||
anomalyService.register('custom-return', {
|
||||
handler: () => customResult
|
||||
});
|
||||
|
||||
const result = await anomalyService.note('custom-return', {});
|
||||
|
||||
expect(result).toBe(customResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DENY_SERVICE_INSTRUCTION', () => {
|
||||
it('should be a symbol', () => {
|
||||
expect(typeof DENY_SERVICE_INSTRUCTION).toBe('symbol');
|
||||
});
|
||||
|
||||
it('should be unique', () => {
|
||||
const anotherSymbol = Symbol('DENY_SERVICE_INSTRUCTION');
|
||||
expect(DENY_SERVICE_INSTRUCTION).not.toBe(anotherSymbol);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ClientOperationService } from './ClientOperationService';
|
||||
|
||||
describe('ClientOperationService', async () => {
|
||||
// ClientOperationService doesn't extend BaseService, so we can't use init
|
||||
// We need to create it directly
|
||||
const services = { _instances: {} };
|
||||
const clientOperationService = new ClientOperationService({ services });
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(clientOperationService).toBeDefined();
|
||||
expect(clientOperationService.operations_).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have operations array', () => {
|
||||
expect(clientOperationService.operations_).toBeDefined();
|
||||
expect(Array.isArray(clientOperationService.operations_)).toBe(true);
|
||||
});
|
||||
|
||||
it('should create operation with default parameters', async () => {
|
||||
const tracker = await clientOperationService.add_operation({});
|
||||
|
||||
expect(tracker).toBeDefined();
|
||||
expect(tracker.name).toBe('untitled');
|
||||
expect(Array.isArray(tracker.tags)).toBe(true);
|
||||
expect(tracker.tags.length).toBe(0);
|
||||
expect(tracker.frame).toBe(null);
|
||||
expect(tracker.metadata).toBeDefined();
|
||||
expect(typeof tracker.metadata).toBe('object');
|
||||
expect(Array.isArray(tracker.objects)).toBe(true);
|
||||
});
|
||||
|
||||
it('should create operation with name', async () => {
|
||||
const tracker = await clientOperationService.add_operation({
|
||||
name: 'test-operation',
|
||||
});
|
||||
|
||||
expect(tracker.name).toBe('test-operation');
|
||||
});
|
||||
|
||||
it('should create operation with tags', async () => {
|
||||
const tags = ['tag1', 'tag2', 'tag3'];
|
||||
const tracker = await clientOperationService.add_operation({
|
||||
tags,
|
||||
});
|
||||
|
||||
expect(tracker.tags).toEqual(tags);
|
||||
});
|
||||
|
||||
it('should create operation with frame', async () => {
|
||||
const frame = { type: 'test-frame' };
|
||||
const tracker = await clientOperationService.add_operation({
|
||||
frame,
|
||||
});
|
||||
|
||||
expect(tracker.frame).toBe(frame);
|
||||
});
|
||||
|
||||
it('should create operation with metadata', async () => {
|
||||
const metadata = { key1: 'value1', key2: 'value2' };
|
||||
const tracker = await clientOperationService.add_operation({
|
||||
metadata,
|
||||
});
|
||||
|
||||
expect(tracker.metadata).toEqual(metadata);
|
||||
});
|
||||
|
||||
it('should create operation with objects', async () => {
|
||||
const objects = [{ id: 1 }, { id: 2 }];
|
||||
const tracker = await clientOperationService.add_operation({
|
||||
objects,
|
||||
});
|
||||
|
||||
expect(tracker.objects).toEqual(objects);
|
||||
});
|
||||
|
||||
it('should create operation with all parameters', async () => {
|
||||
const params = {
|
||||
name: 'full-operation',
|
||||
tags: ['full', 'test'],
|
||||
frame: { type: 'frame' },
|
||||
metadata: { meta: 'data' },
|
||||
objects: [{ obj: 1 }],
|
||||
};
|
||||
|
||||
const tracker = await clientOperationService.add_operation(params);
|
||||
|
||||
expect(tracker.name).toBe(params.name);
|
||||
expect(tracker.tags).toEqual(params.tags);
|
||||
expect(tracker.frame).toBe(params.frame);
|
||||
expect(tracker.metadata).toEqual(params.metadata);
|
||||
expect(tracker.objects).toEqual(params.objects);
|
||||
});
|
||||
|
||||
it('should create multiple operations', async () => {
|
||||
const tracker1 = await clientOperationService.add_operation({ name: 'op1' });
|
||||
const tracker2 = await clientOperationService.add_operation({ name: 'op2' });
|
||||
const tracker3 = await clientOperationService.add_operation({ name: 'op3' });
|
||||
|
||||
expect(tracker1.name).toBe('op1');
|
||||
expect(tracker2.name).toBe('op2');
|
||||
expect(tracker3.name).toBe('op3');
|
||||
});
|
||||
|
||||
it('should have ckey method', () => {
|
||||
expect(clientOperationService.ckey).toBeDefined();
|
||||
expect(typeof clientOperationService.ckey).toBe('function');
|
||||
});
|
||||
|
||||
it('should generate context key with ckey', () => {
|
||||
const key = clientOperationService.ckey('test-key');
|
||||
|
||||
expect(key).toBeDefined();
|
||||
expect(typeof key).toBe('string');
|
||||
expect(key).toContain('test-key');
|
||||
});
|
||||
|
||||
it('should generate different keys for different inputs', () => {
|
||||
const key1 = clientOperationService.ckey('key1');
|
||||
const key2 = clientOperationService.ckey('key2');
|
||||
|
||||
expect(key1).not.toBe(key2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { CommandService } from './CommandService';
|
||||
|
||||
describe('CommandService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
commands: CommandService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const commandService = testKernel.services!.get('commands') as CommandService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(commandService).toBeInstanceOf(CommandService);
|
||||
});
|
||||
|
||||
it('should have help command registered by default', () => {
|
||||
expect(commandService.commandNames).toContain('help');
|
||||
});
|
||||
|
||||
it('should register commands', () => {
|
||||
commandService.registerCommands('test-service', [
|
||||
{
|
||||
id: 'test-cmd',
|
||||
description: 'A test command',
|
||||
handler: async () => {},
|
||||
},
|
||||
]);
|
||||
expect(commandService.commandNames).toContain('test-service:test-cmd');
|
||||
});
|
||||
|
||||
it('should execute registered commands', async () => {
|
||||
let executed = false;
|
||||
commandService.registerCommands('exec-test', [
|
||||
{
|
||||
id: 'exec-cmd',
|
||||
description: 'Execute test',
|
||||
handler: async () => { executed = true; },
|
||||
},
|
||||
]);
|
||||
|
||||
const mockLog = { error: vi.fn(), log: vi.fn() };
|
||||
await commandService.executeCommand(['exec-test:exec-cmd'], mockLog);
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass arguments to command handler', async () => {
|
||||
let receivedArgs: string[] = [];
|
||||
commandService.registerCommands('args-test', [
|
||||
{
|
||||
id: 'args-cmd',
|
||||
description: 'Args test',
|
||||
handler: async (args) => { receivedArgs = args; },
|
||||
},
|
||||
]);
|
||||
|
||||
const mockLog = { error: vi.fn(), log: vi.fn() };
|
||||
await commandService.executeCommand(['args-test:args-cmd', 'arg1', 'arg2'], mockLog);
|
||||
expect(receivedArgs).toEqual(['arg1', 'arg2']);
|
||||
});
|
||||
|
||||
it('should handle unknown commands', async () => {
|
||||
const mockLog = { error: vi.fn(), log: vi.fn() };
|
||||
await commandService.executeCommand(['unknown-command'], mockLog);
|
||||
expect(mockLog.error).toHaveBeenCalledWith('unknown command: unknown-command');
|
||||
});
|
||||
|
||||
it('should execute raw commands', async () => {
|
||||
let executed = false;
|
||||
commandService.registerCommands('raw-test', [
|
||||
{
|
||||
id: 'raw-cmd',
|
||||
description: 'Raw test',
|
||||
handler: async () => { executed = true; },
|
||||
},
|
||||
]);
|
||||
|
||||
const mockLog = { error: vi.fn(), log: vi.fn() };
|
||||
await commandService.executeRawCommand('raw-test:raw-cmd', mockLog);
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should get command by id', () => {
|
||||
commandService.registerCommands('get-test', [
|
||||
{
|
||||
id: 'get-cmd',
|
||||
description: 'Get test',
|
||||
handler: async () => {},
|
||||
},
|
||||
]);
|
||||
|
||||
const cmd = commandService.getCommand('get-test:get-cmd');
|
||||
expect(cmd).toBeDefined();
|
||||
expect(cmd?.id).toBe('get-test:get-cmd');
|
||||
});
|
||||
|
||||
it('should execute help command', async () => {
|
||||
const mockLog = { error: vi.fn(), log: vi.fn() };
|
||||
await commandService.executeCommand(['help'], mockLog);
|
||||
expect(mockLog.log).toHaveBeenCalledWith('available commands:');
|
||||
});
|
||||
|
||||
it('should support command completers', () => {
|
||||
commandService.registerCommands('complete-test', [
|
||||
{
|
||||
id: 'complete-cmd',
|
||||
description: 'Complete test',
|
||||
handler: async () => {},
|
||||
completer: (args) => ['option1', 'option2'],
|
||||
},
|
||||
]);
|
||||
|
||||
const cmd = commandService.getCommand('complete-test:complete-cmd');
|
||||
const completions = cmd?.completeArgument([]);
|
||||
expect(completions).toEqual(['option1', 'option2']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import * as config from '../config';
|
||||
import { CommentService } from './CommentService';
|
||||
|
||||
describe('CommentService', async () => {
|
||||
config.load_config({
|
||||
'services': {
|
||||
'database': {
|
||||
path: ':memory:',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'comment': CommentService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
testCore: true,
|
||||
});
|
||||
|
||||
const commentService = testKernel.services!.get('comment') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(commentService).toBeInstanceOf(CommentService);
|
||||
});
|
||||
|
||||
it('should have db connection after init', () => {
|
||||
expect(commentService.db).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have uuidv4 module', () => {
|
||||
expect(commentService.modules).toBeDefined();
|
||||
expect(commentService.modules.uuidv4).toBeDefined();
|
||||
expect(typeof commentService.modules.uuidv4).toBe('function');
|
||||
});
|
||||
|
||||
it('should have create_comment_ method', () => {
|
||||
expect(commentService.create_comment_).toBeDefined();
|
||||
expect(typeof commentService.create_comment_).toBe('function');
|
||||
});
|
||||
|
||||
it('should have attach_comment_to_fsentry method', () => {
|
||||
expect(commentService.attach_comment_to_fsentry).toBeDefined();
|
||||
expect(typeof commentService.attach_comment_to_fsentry).toBe('function');
|
||||
});
|
||||
|
||||
it('should have get_comments_for_fsentry method', () => {
|
||||
expect(commentService.get_comments_for_fsentry).toBeDefined();
|
||||
expect(typeof commentService.get_comments_for_fsentry).toBe('function');
|
||||
});
|
||||
|
||||
it('should generate UUID for comments', () => {
|
||||
const uuid1 = commentService.modules.uuidv4();
|
||||
const uuid2 = commentService.modules.uuidv4();
|
||||
|
||||
expect(uuid1).toBeDefined();
|
||||
expect(uuid2).toBeDefined();
|
||||
expect(typeof uuid1).toBe('string');
|
||||
expect(typeof uuid2).toBe('string');
|
||||
expect(uuid1).not.toBe(uuid2);
|
||||
});
|
||||
|
||||
it('should validate UUID format', () => {
|
||||
const uuid = commentService.modules.uuidv4();
|
||||
|
||||
// UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
expect(uuid).toMatch(uuidRegex);
|
||||
});
|
||||
|
||||
it('should create comment with text', async () => {
|
||||
const mockReq = {
|
||||
body: { text: 'Test comment text' },
|
||||
user: { id: 1 },
|
||||
};
|
||||
const mockRes = {};
|
||||
|
||||
// Mock database write
|
||||
const originalWrite = commentService.db.write.bind(commentService.db);
|
||||
commentService.db.write = vi.fn().mockResolvedValue({ insertId: 123 });
|
||||
|
||||
try {
|
||||
const result = await commentService.create_comment_({
|
||||
req: mockReq,
|
||||
res: mockRes
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.id).toBe(123);
|
||||
expect(result.uid).toBeDefined();
|
||||
expect(typeof result.uid).toBe('string');
|
||||
|
||||
// Verify database write was called with correct parameters
|
||||
expect(commentService.db.write).toHaveBeenCalledWith(
|
||||
expect.stringContaining('INSERT INTO `user_comments`'),
|
||||
expect.arrayContaining([
|
||||
expect.any(String), // UUID
|
||||
1, // user_id
|
||||
'{}', // metadata
|
||||
'Test comment text',
|
||||
])
|
||||
);
|
||||
} finally {
|
||||
commentService.db.write = originalWrite;
|
||||
}
|
||||
});
|
||||
|
||||
it('should attach comment to fsentry', async () => {
|
||||
const mockNode = {
|
||||
get: vi.fn().mockResolvedValue(456), // mysql-id
|
||||
};
|
||||
const comment = {
|
||||
id: 123,
|
||||
uid: 'comment-uuid',
|
||||
};
|
||||
|
||||
const originalWrite = commentService.db.write.bind(commentService.db);
|
||||
commentService.db.write = vi.fn().mockResolvedValue({});
|
||||
|
||||
try {
|
||||
await commentService.attach_comment_to_fsentry({
|
||||
node: mockNode,
|
||||
comment: comment,
|
||||
});
|
||||
|
||||
expect(commentService.db.write).toHaveBeenCalledWith(
|
||||
expect.stringContaining('INSERT INTO `user_fsentry_comments`'),
|
||||
expect.arrayContaining([123, 456])
|
||||
);
|
||||
|
||||
expect(mockNode.get).toHaveBeenCalledWith('mysql-id');
|
||||
} finally {
|
||||
commentService.db.write = originalWrite;
|
||||
}
|
||||
});
|
||||
|
||||
it('should call database to get comments for fsentry', async () => {
|
||||
const mockNode = {
|
||||
get: vi.fn().mockResolvedValue(789),
|
||||
};
|
||||
|
||||
const originalRead = commentService.db.read.bind(commentService.db);
|
||||
commentService.db.read = vi.fn().mockResolvedValue([]);
|
||||
|
||||
try {
|
||||
// Note: This test only verifies the database call structure
|
||||
// Full integration tests would require proper user service setup
|
||||
await commentService.get_comments_for_fsentry({
|
||||
node: mockNode,
|
||||
});
|
||||
|
||||
expect(commentService.db.read).toHaveBeenCalledWith(
|
||||
expect.stringContaining('SELECT * FROM `user_comments`'),
|
||||
expect.arrayContaining([789])
|
||||
);
|
||||
|
||||
expect(mockNode.get).toHaveBeenCalledWith('mysql-id');
|
||||
} finally {
|
||||
commentService.db.read = originalRead;
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle multiple comment attachments', async () => {
|
||||
const mockNode = {
|
||||
get: vi.fn().mockResolvedValue(999),
|
||||
};
|
||||
|
||||
const comments = [
|
||||
{ id: 1, uid: 'uuid-1' },
|
||||
{ id: 2, uid: 'uuid-2' },
|
||||
{ id: 3, uid: 'uuid-3' },
|
||||
];
|
||||
|
||||
const originalWrite = commentService.db.write.bind(commentService.db);
|
||||
commentService.db.write = vi.fn().mockResolvedValue({});
|
||||
|
||||
try {
|
||||
for (const comment of comments) {
|
||||
await commentService.attach_comment_to_fsentry({
|
||||
node: mockNode,
|
||||
comment,
|
||||
});
|
||||
}
|
||||
|
||||
expect(commentService.db.write).toHaveBeenCalledTimes(3);
|
||||
} finally {
|
||||
commentService.db.write = originalWrite;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import * as config from '../config';
|
||||
import { ConfigurableCountingService } from './ConfigurableCountingService';
|
||||
|
||||
describe('ConfigurableCountingService', async () => {
|
||||
config.load_config({
|
||||
'services': {
|
||||
'database': {
|
||||
path: ':memory:',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'counting': ConfigurableCountingService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
testCore: true,
|
||||
});
|
||||
|
||||
const countingService = testKernel.services!.get('counting') as ConfigurableCountingService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(countingService).toBeInstanceOf(ConfigurableCountingService);
|
||||
});
|
||||
|
||||
it('should have counting types defined', () => {
|
||||
expect(ConfigurableCountingService.counting_types).toBeDefined();
|
||||
expect(ConfigurableCountingService.counting_types.gpt).toBeDefined();
|
||||
expect(ConfigurableCountingService.counting_types.dalle).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have sql columns defined', () => {
|
||||
expect(ConfigurableCountingService.sql_columns).toBeDefined();
|
||||
expect(ConfigurableCountingService.sql_columns.uint).toBeDefined();
|
||||
expect(ConfigurableCountingService.sql_columns.uint.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should validate GPT counting type structure', () => {
|
||||
const gptType = ConfigurableCountingService.counting_types.gpt;
|
||||
expect(gptType.category).toBeDefined();
|
||||
expect(gptType.values).toBeDefined();
|
||||
expect(gptType.category.length).toBeGreaterThan(0);
|
||||
expect(gptType.values.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should validate DALL-E counting type structure', () => {
|
||||
const dalleType = ConfigurableCountingService.counting_types.dalle;
|
||||
expect(dalleType.category).toBeDefined();
|
||||
expect(dalleType.category.length).toBeGreaterThan(0);
|
||||
expect(dalleType.category.some(c => c.name === 'model')).toBe(true);
|
||||
expect(dalleType.category.some(c => c.name === 'quality')).toBe(true);
|
||||
expect(dalleType.category.some(c => c.name === 'resolution')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have gpt token value definitions', () => {
|
||||
const gptType = ConfigurableCountingService.counting_types.gpt;
|
||||
expect(gptType.values.some(v => v.name === 'input_tokens')).toBe(true);
|
||||
expect(gptType.values.some(v => v.name === 'output_tokens')).toBe(true);
|
||||
expect(gptType.values.every(v => v.type === 'uint')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have available sql columns for uint type', () => {
|
||||
const columns = ConfigurableCountingService.sql_columns.uint;
|
||||
expect(columns).toBeDefined();
|
||||
expect(Array.isArray(columns)).toBe(true);
|
||||
expect(columns.length).toBe(3);
|
||||
expect(columns.every(col => typeof col === 'string')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have model category for gpt', () => {
|
||||
const gptType = ConfigurableCountingService.counting_types.gpt;
|
||||
const modelCategory = gptType.category.find(c => c.name === 'model');
|
||||
expect(modelCategory).toBeDefined();
|
||||
expect(modelCategory!.type).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { ContextInitService } from './ContextInitService';
|
||||
|
||||
describe('ContextInitService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'context-init': ContextInitService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const contextInitService = testKernel.services!.get('context-init') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(contextInitService).toBeInstanceOf(ContextInitService);
|
||||
});
|
||||
|
||||
it('should have middleware instance', () => {
|
||||
expect(contextInitService.mw).toBeDefined();
|
||||
expect(contextInitService.mw.value_initializers_).toBeDefined();
|
||||
expect(Array.isArray(contextInitService.mw.value_initializers_)).toBe(true);
|
||||
});
|
||||
|
||||
it('should register a value initializer', () => {
|
||||
const initialLength = contextInitService.mw.value_initializers_.length;
|
||||
|
||||
contextInitService.register_value('test-key', 'test-value');
|
||||
|
||||
expect(contextInitService.mw.value_initializers_.length).toBe(initialLength + 1);
|
||||
});
|
||||
|
||||
it('should store key-value pair in initializer', () => {
|
||||
const service = testKernel.services!.get('context-init') as any;
|
||||
|
||||
service.register_value('stored-key', 'stored-value');
|
||||
|
||||
const lastInitializer = service.mw.value_initializers_[service.mw.value_initializers_.length - 1];
|
||||
expect(lastInitializer.key).toBe('stored-key');
|
||||
expect(lastInitializer.value).toBe('stored-value');
|
||||
});
|
||||
|
||||
it('should register async factory', () => {
|
||||
const service = testKernel.services!.get('context-init') as any;
|
||||
const initialLength = service.mw.value_initializers_.length;
|
||||
|
||||
const factory = async () => 'async-value';
|
||||
service.register_async_factory('async-key', factory);
|
||||
|
||||
expect(service.mw.value_initializers_.length).toBe(initialLength + 1);
|
||||
});
|
||||
|
||||
it('should store async factory in initializer', () => {
|
||||
const service = testKernel.services!.get('context-init') as any;
|
||||
|
||||
const factory = async () => 'factory-result';
|
||||
service.register_async_factory('factory-key', factory);
|
||||
|
||||
const lastInitializer = service.mw.value_initializers_[service.mw.value_initializers_.length - 1];
|
||||
expect(lastInitializer.key).toBe('factory-key');
|
||||
expect(lastInitializer.async_factory).toBe(factory);
|
||||
});
|
||||
|
||||
it('should handle multiple value registrations', () => {
|
||||
const service = testKernel.services!.get('context-init') as any;
|
||||
|
||||
service.register_value('key1', 'value1');
|
||||
service.register_value('key2', 'value2');
|
||||
service.register_value('key3', 'value3');
|
||||
|
||||
const keys = service.mw.value_initializers_.map((init: any) => init.key);
|
||||
expect(keys).toContain('key1');
|
||||
expect(keys).toContain('key2');
|
||||
expect(keys).toContain('key3');
|
||||
});
|
||||
|
||||
it('should have install method on middleware', () => {
|
||||
expect(contextInitService.mw.install).toBeDefined();
|
||||
expect(typeof contextInitService.mw.install).toBe('function');
|
||||
});
|
||||
|
||||
it('should have run method on middleware', () => {
|
||||
expect(contextInitService.mw.run).toBeDefined();
|
||||
expect(typeof contextInitService.mw.run).toBe('function');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { DetailProviderService } from './DetailProviderService';
|
||||
|
||||
describe('DetailProviderService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'detail-provider': DetailProviderService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const detailProviderService = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(detailProviderService).toBeInstanceOf(DetailProviderService);
|
||||
});
|
||||
|
||||
it('should have empty providers array initially', () => {
|
||||
expect(detailProviderService.providers_).toBeDefined();
|
||||
expect(Array.isArray(detailProviderService.providers_)).toBe(true);
|
||||
});
|
||||
|
||||
it('should register a provider', () => {
|
||||
const initialLength = detailProviderService.providers_.length;
|
||||
const provider = async (context: any, out: any) => {
|
||||
out.test = 'value';
|
||||
};
|
||||
|
||||
detailProviderService.register_provider(provider);
|
||||
|
||||
expect(detailProviderService.providers_.length).toBe(initialLength + 1);
|
||||
});
|
||||
|
||||
it('should get details with single provider', async () => {
|
||||
const service = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.name = context.input;
|
||||
});
|
||||
|
||||
const result = await service.get_details({ input: 'test-name' });
|
||||
|
||||
expect(result.name).toBe('test-name');
|
||||
});
|
||||
|
||||
it('should get details with multiple providers', async () => {
|
||||
const service = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.field1 = 'value1';
|
||||
});
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.field2 = 'value2';
|
||||
});
|
||||
|
||||
const result = await service.get_details({});
|
||||
|
||||
expect(result.field1).toBe('value1');
|
||||
expect(result.field2).toBe('value2');
|
||||
});
|
||||
|
||||
it('should allow providers to modify existing output', async () => {
|
||||
const service = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.counter = 1;
|
||||
});
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.counter = out.counter + 1;
|
||||
});
|
||||
|
||||
const result = await service.get_details({});
|
||||
|
||||
expect(result.counter).toBe(2);
|
||||
});
|
||||
|
||||
it('should use provided output object', async () => {
|
||||
const service = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
out.added = true;
|
||||
});
|
||||
|
||||
const existingOut = { existing: 'value' };
|
||||
const result = await service.get_details({}, existingOut);
|
||||
|
||||
expect(result.existing).toBe('value');
|
||||
expect(result.added).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle async providers', async () => {
|
||||
const service = testKernel.services!.get('detail-provider') as any;
|
||||
|
||||
service.register_provider(async (context: any, out: any) => {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
out.async = true;
|
||||
});
|
||||
|
||||
const result = await service.get_details({});
|
||||
|
||||
expect(result.async).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { EventService } from './EventService';
|
||||
|
||||
describe('EventService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'event-test': EventService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const eventService = testKernel.services!.get('event-test') as EventService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(eventService).toBeInstanceOf(EventService);
|
||||
});
|
||||
|
||||
it('should emit and receive events', async () => {
|
||||
let received = false;
|
||||
eventService.on('test.event', () => {
|
||||
received = true;
|
||||
});
|
||||
|
||||
await eventService.emit('test.event', {});
|
||||
expect(received).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass data to event listeners', async () => {
|
||||
let receivedData: any = null;
|
||||
eventService.on('data.event', (key, data) => {
|
||||
receivedData = data;
|
||||
});
|
||||
|
||||
await eventService.emit('data.event', { value: 42 });
|
||||
expect(receivedData).toEqual({ value: 42 });
|
||||
});
|
||||
|
||||
it('should support wildcard listeners', async () => {
|
||||
const received: string[] = [];
|
||||
eventService.on('wild.*', (key) => {
|
||||
received.push(key);
|
||||
});
|
||||
|
||||
await eventService.emit('wild.test1', {});
|
||||
await eventService.emit('wild.test2', {});
|
||||
|
||||
expect(received).toContain('wild.test1');
|
||||
expect(received).toContain('wild.test2');
|
||||
});
|
||||
|
||||
it('should support multiple listeners on same event', async () => {
|
||||
let count = 0;
|
||||
eventService.on('multi.event', () => { count++; });
|
||||
eventService.on('multi.event', () => { count++; });
|
||||
|
||||
await eventService.emit('multi.event', {});
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
it('should detach listeners', async () => {
|
||||
let count = 0;
|
||||
const det = eventService.on('detach.event', () => { count++; });
|
||||
|
||||
await eventService.emit('detach.event', {});
|
||||
expect(count).toBe(1);
|
||||
|
||||
det.detach();
|
||||
await eventService.emit('detach.event', {});
|
||||
expect(count).toBe(1); // Should still be 1
|
||||
});
|
||||
|
||||
it('should support global listeners', async () => {
|
||||
let globalReceived = false;
|
||||
eventService.on_all(() => {
|
||||
globalReceived = true;
|
||||
});
|
||||
|
||||
await eventService.emit('any.event', {});
|
||||
expect(globalReceived).toBe(true);
|
||||
});
|
||||
|
||||
it('should create scoped event bus', () => {
|
||||
const scoped = eventService.get_scoped('test.scope');
|
||||
expect(scoped).toBeDefined();
|
||||
expect(scoped.scope).toBe('test.scope');
|
||||
});
|
||||
|
||||
it('should emit events through scoped bus', async () => {
|
||||
let received = false;
|
||||
eventService.on('scope.test.event', () => {
|
||||
received = true;
|
||||
});
|
||||
|
||||
const scoped = eventService.get_scoped('scope.test');
|
||||
await scoped.emit('event', {});
|
||||
expect(received).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { FeatureFlagService } from './FeatureFlagService';
|
||||
|
||||
describe('FeatureFlagService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'feature-flag': FeatureFlagService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
testCore: true,
|
||||
});
|
||||
|
||||
const featureFlagService = testKernel.services!.get('feature-flag') as FeatureFlagService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(featureFlagService).toBeInstanceOf(FeatureFlagService);
|
||||
});
|
||||
|
||||
it('should register feature flags', () => {
|
||||
featureFlagService.register('test-flag', true);
|
||||
expect(featureFlagService.known_flags.has('test-flag')).toBe(true);
|
||||
});
|
||||
|
||||
it('should register config flags', () => {
|
||||
featureFlagService.register('config-flag', { $: 'config-flag', value: true });
|
||||
expect(featureFlagService.known_flags.get('config-flag')).toEqual({ $: 'config-flag', value: true });
|
||||
});
|
||||
|
||||
it('should check config flags', async () => {
|
||||
featureFlagService.register('enabled-flag', { $: 'config-flag', value: true });
|
||||
const result = await featureFlagService.check('enabled-flag');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should check disabled config flags', async () => {
|
||||
featureFlagService.register('disabled-flag', { $: 'config-flag', value: false });
|
||||
const result = await featureFlagService.check('disabled-flag');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should register function flags', () => {
|
||||
featureFlagService.register('fn-flag', {
|
||||
$: 'function-flag',
|
||||
fn: async () => true,
|
||||
});
|
||||
expect(featureFlagService.known_flags.has('fn-flag')).toBe(true);
|
||||
});
|
||||
|
||||
it('should check function flags', async () => {
|
||||
featureFlagService.register('dynamic-flag', {
|
||||
$: 'function-flag',
|
||||
fn: async ({ actor }) => actor?.type?.user?.username === 'test',
|
||||
});
|
||||
|
||||
const result = await featureFlagService.check({ actor: { type: { user: { username: 'test' } } } }, 'dynamic-flag');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should support function flags with different conditions', async () => {
|
||||
featureFlagService.register('conditional-flag', {
|
||||
$: 'function-flag',
|
||||
fn: async ({ actor }) => actor?.type?.user?.username !== 'test',
|
||||
});
|
||||
|
||||
const result = await featureFlagService.check({ actor: { type: { user: { username: 'other' } } } }, 'conditional-flag');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should manage multiple flags', () => {
|
||||
featureFlagService.register('multi-flag-1', { $: 'config-flag', value: true });
|
||||
featureFlagService.register('multi-flag-2', { $: 'config-flag', value: false });
|
||||
featureFlagService.register('multi-flag-3', {
|
||||
$: 'function-flag',
|
||||
fn: async () => true,
|
||||
});
|
||||
|
||||
expect(featureFlagService.known_flags.has('multi-flag-1')).toBe(true);
|
||||
expect(featureFlagService.known_flags.has('multi-flag-2')).toBe(true);
|
||||
expect(featureFlagService.known_flags.has('multi-flag-3')).toBe(true);
|
||||
expect(featureFlagService.known_flags.size).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { HelloWorldService } from './HelloWorldService';
|
||||
|
||||
describe('HelloWorldService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'hello-world': HelloWorldService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const helloWorldService = testKernel.services!.get('hello-world') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(helloWorldService).toBeInstanceOf(HelloWorldService);
|
||||
});
|
||||
|
||||
it('should return version', () => {
|
||||
const version = helloWorldService.as('version').get_version();
|
||||
expect(version).toBe('v1.0.0');
|
||||
});
|
||||
|
||||
it('should greet without subject', async () => {
|
||||
const greeting = await helloWorldService.as('hello-world').greet({});
|
||||
expect(greeting).toBe('Hello, World!');
|
||||
});
|
||||
|
||||
it('should greet with subject', async () => {
|
||||
const greeting = await helloWorldService.as('hello-world').greet({ subject: 'Alice' });
|
||||
expect(greeting).toBe('Hello, Alice!');
|
||||
});
|
||||
|
||||
it('should greet with different subjects', async () => {
|
||||
const greeting1 = await helloWorldService.as('hello-world').greet({ subject: 'Bob' });
|
||||
const greeting2 = await helloWorldService.as('hello-world').greet({ subject: 'Charlie' });
|
||||
|
||||
expect(greeting1).toBe('Hello, Bob!');
|
||||
expect(greeting2).toBe('Hello, Charlie!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { HostnameService } from './HostnameService';
|
||||
|
||||
describe('HostnameService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
hostname: HostnameService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const hostnameService = testKernel.services!.get('hostname') as HostnameService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(hostnameService).toBeInstanceOf(HostnameService);
|
||||
});
|
||||
|
||||
it('should have entries object', () => {
|
||||
expect(hostnameService.entries).toBeDefined();
|
||||
expect(typeof hostnameService.entries).toBe('object');
|
||||
});
|
||||
|
||||
it('should have entries as empty object by default', () => {
|
||||
expect(hostnameService.entries).toBeDefined();
|
||||
expect(typeof hostnameService.entries).toBe('object');
|
||||
});
|
||||
|
||||
it('should have get_broadcast_addresses method', () => {
|
||||
expect(typeof hostnameService.get_broadcast_addresses).toBe('function');
|
||||
});
|
||||
|
||||
it('should allow manual entry registration', () => {
|
||||
hostnameService.entries['manual.test.com'] = { scope: 'test' };
|
||||
expect(hostnameService.entries['manual.test.com']).toBeDefined();
|
||||
expect(hostnameService.entries['manual.test.com'].scope).toBe('test');
|
||||
});
|
||||
|
||||
it('should maintain multiple entries', () => {
|
||||
hostnameService.entries['first.test.com'] = { scope: 'web' };
|
||||
hostnameService.entries['second.test.com'] = { scope: 'api' };
|
||||
|
||||
expect(hostnameService.entries['first.test.com'].scope).toBe('web');
|
||||
expect(hostnameService.entries['second.test.com'].scope).toBe('api');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { LockService } from './LockService';
|
||||
|
||||
describe('LockService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
lock: LockService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
testCore: true,
|
||||
});
|
||||
|
||||
const lockService = testKernel.services!.get('lock') as LockService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(lockService).toBeInstanceOf(LockService);
|
||||
});
|
||||
|
||||
it('should acquire and release a lock', async () => {
|
||||
let executed = false;
|
||||
await lockService.lock('test-lock', async () => {
|
||||
executed = true;
|
||||
});
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should execute callback within lock', async () => {
|
||||
const result = await lockService.lock('test-lock-2', async () => {
|
||||
return 'success';
|
||||
});
|
||||
expect(result).toBe('success');
|
||||
});
|
||||
|
||||
it('should handle multiple sequential locks', async () => {
|
||||
const results: number[] = [];
|
||||
|
||||
await lockService.lock('seq-lock', async () => {
|
||||
results.push(1);
|
||||
});
|
||||
|
||||
await lockService.lock('seq-lock', async () => {
|
||||
results.push(2);
|
||||
});
|
||||
|
||||
expect(results).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('should handle locks with options', async () => {
|
||||
let executed = false;
|
||||
await lockService.lock('opt-lock', { timeout: 5000 }, async () => {
|
||||
executed = true;
|
||||
});
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should support array of lock names', async () => {
|
||||
let executed = false;
|
||||
await lockService.lock(['lock-a', 'lock-b'], async () => {
|
||||
executed = true;
|
||||
});
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should maintain lock state', async () => {
|
||||
await lockService.lock('state-lock', async () => {
|
||||
expect(lockService.locks['state-lock']).toBeDefined();
|
||||
});
|
||||
// Lock should still exist after release
|
||||
expect(lockService.locks['state-lock']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle errors within lock callback', async () => {
|
||||
await expect(
|
||||
lockService.lock('error-lock', async () => {
|
||||
throw new Error('Test error');
|
||||
})
|
||||
).rejects.toThrow('Test error');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Readable } from 'stream';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import MemoryStorageService from './MemoryStorageService';
|
||||
|
||||
describe('MemoryStorageService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'memory-storage': MemoryStorageService,
|
||||
},
|
||||
initLevelString: 'construct',
|
||||
});
|
||||
|
||||
const memoryStorage = testKernel.services!.get('memory-storage') as MemoryStorageService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(memoryStorage).toBeInstanceOf(MemoryStorageService);
|
||||
});
|
||||
|
||||
it('should create read stream from memory file', async () => {
|
||||
const mockFile = {
|
||||
content: Buffer.from('test content'),
|
||||
};
|
||||
|
||||
const stream = await memoryStorage.create_read_stream('test-uuid', {
|
||||
memory_file: mockFile,
|
||||
});
|
||||
|
||||
expect(stream).toBeInstanceOf(Readable);
|
||||
});
|
||||
|
||||
it('should read content from stream', async () => {
|
||||
const testContent = 'Hello, World!';
|
||||
const mockFile = {
|
||||
content: Buffer.from(testContent),
|
||||
};
|
||||
|
||||
const stream = await memoryStorage.create_read_stream('test-uuid', {
|
||||
memory_file: mockFile,
|
||||
}) as Readable;
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
const result = Buffer.concat(chunks).toString();
|
||||
expect(result).toBe(testContent);
|
||||
});
|
||||
|
||||
it('should throw error when memory_file is not provided', async () => {
|
||||
await expect(
|
||||
memoryStorage.create_read_stream('test-uuid', {})
|
||||
).rejects.toThrow('MemoryStorageService.create_read_stream: memory_file is required');
|
||||
});
|
||||
|
||||
it('should handle empty content', async () => {
|
||||
const mockFile = {
|
||||
content: Buffer.from(''),
|
||||
};
|
||||
|
||||
const stream = await memoryStorage.create_read_stream('test-uuid', {
|
||||
memory_file: mockFile,
|
||||
}) as Readable;
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
const result = Buffer.concat(chunks).toString();
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should handle binary content', async () => {
|
||||
const binaryData = Buffer.from([0x00, 0x01, 0x02, 0xFF]);
|
||||
const mockFile = {
|
||||
content: binaryData,
|
||||
};
|
||||
|
||||
const stream = await memoryStorage.create_read_stream('test-uuid', {
|
||||
memory_file: mockFile,
|
||||
}) as Readable;
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
const result = Buffer.concat(chunks);
|
||||
expect(result).toEqual(binaryData);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import * as config from '../config';
|
||||
import { NotificationService, UserIDNotifSelector, UsernameNotifSelector } from './NotificationService';
|
||||
import { ScriptService } from './ScriptService';
|
||||
|
||||
describe('NotificationService', async () => {
|
||||
config.load_config({
|
||||
'services': {
|
||||
'database': {
|
||||
path: ':memory:',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'script': ScriptService,
|
||||
'notification': NotificationService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
testCore: true,
|
||||
});
|
||||
|
||||
const notificationService = testKernel.services!.get('notification') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(notificationService).toBeInstanceOf(NotificationService);
|
||||
});
|
||||
|
||||
it('should have db connection after init', () => {
|
||||
expect(notificationService.db).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have notifs_pending_write object', () => {
|
||||
expect(notificationService.notifs_pending_write).toBeDefined();
|
||||
expect(typeof notificationService.notifs_pending_write).toBe('object');
|
||||
});
|
||||
|
||||
it('should have merged_on_user_connected_ object', () => {
|
||||
expect(notificationService.merged_on_user_connected_).toBeDefined();
|
||||
expect(typeof notificationService.merged_on_user_connected_).toBe('object');
|
||||
});
|
||||
|
||||
it('should have on_user_connected method', () => {
|
||||
expect(notificationService.on_user_connected).toBeDefined();
|
||||
expect(typeof notificationService.on_user_connected).toBe('function');
|
||||
});
|
||||
|
||||
it('should have do_on_user_connected method', () => {
|
||||
expect(notificationService.do_on_user_connected).toBeDefined();
|
||||
expect(typeof notificationService.do_on_user_connected).toBe('function');
|
||||
});
|
||||
|
||||
it('should have on_sent_to_user method', () => {
|
||||
expect(notificationService.on_sent_to_user).toBeDefined();
|
||||
expect(typeof notificationService.on_sent_to_user).toBe('function');
|
||||
});
|
||||
|
||||
it('should have notify method', () => {
|
||||
expect(notificationService.notify).toBeDefined();
|
||||
expect(typeof notificationService.notify).toBe('function');
|
||||
});
|
||||
|
||||
it('should schedule do_on_user_connected on user connected', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const user = { uuid: 'test-uuid-123', id: 1 };
|
||||
|
||||
await notificationService.on_user_connected({ user });
|
||||
|
||||
expect(notificationService.merged_on_user_connected_[user.uuid]).toBeDefined();
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should clear previous timeout on repeated user connected', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const user = { uuid: 'test-uuid-456', id: 2 };
|
||||
|
||||
await notificationService.on_user_connected({ user });
|
||||
const firstTimeout = notificationService.merged_on_user_connected_[user.uuid];
|
||||
|
||||
await notificationService.on_user_connected({ user });
|
||||
const secondTimeout = notificationService.merged_on_user_connected_[user.uuid];
|
||||
|
||||
expect(firstTimeout).toBeDefined();
|
||||
expect(secondTimeout).toBeDefined();
|
||||
// The timeout should have been replaced
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should handle notify with user ID selector', async () => {
|
||||
const userId = 123;
|
||||
const selector = UserIDNotifSelector(userId);
|
||||
|
||||
const result = await selector(notificationService);
|
||||
|
||||
expect(result).toEqual([userId]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UsernameNotifSelector', () => {
|
||||
it('should create a selector function', () => {
|
||||
const selector = UsernameNotifSelector('testuser');
|
||||
|
||||
expect(selector).toBeDefined();
|
||||
expect(typeof selector).toBe('function');
|
||||
});
|
||||
|
||||
it('should return function that fetches user by username', async () => {
|
||||
const mockGetUserService = {
|
||||
get_user: vi.fn().mockResolvedValue({ id: 42, username: 'testuser' }),
|
||||
};
|
||||
|
||||
const mockService = {
|
||||
services: {
|
||||
get: vi.fn().mockReturnValue(mockGetUserService),
|
||||
},
|
||||
};
|
||||
|
||||
const selector = UsernameNotifSelector('testuser');
|
||||
const result = await selector(mockService as any);
|
||||
|
||||
expect(mockService.services.get).toHaveBeenCalledWith('get-user');
|
||||
expect(mockGetUserService.get_user).toHaveBeenCalledWith({ username: 'testuser' });
|
||||
expect(result).toEqual([42]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UserIDNotifSelector', () => {
|
||||
it('should create a selector function', () => {
|
||||
const selector = UserIDNotifSelector(123);
|
||||
|
||||
expect(selector).toBeDefined();
|
||||
expect(typeof selector).toBe('function');
|
||||
});
|
||||
|
||||
it('should return array with user ID', async () => {
|
||||
const userId = 456;
|
||||
const selector = UserIDNotifSelector(userId);
|
||||
|
||||
const result = await selector(null as any);
|
||||
|
||||
expect(result).toEqual([userId]);
|
||||
});
|
||||
|
||||
it('should work with different user IDs', async () => {
|
||||
const selector1 = UserIDNotifSelector(100);
|
||||
const selector2 = UserIDNotifSelector(200);
|
||||
const selector3 = UserIDNotifSelector(300);
|
||||
|
||||
const result1 = await selector1(null as any);
|
||||
const result2 = await selector2(null as any);
|
||||
const result3 = await selector3(null as any);
|
||||
|
||||
expect(result1).toEqual([100]);
|
||||
expect(result2).toEqual([200]);
|
||||
expect(result3).toEqual([300]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { PuterVersionService } from './PuterVersionService';
|
||||
|
||||
describe('PuterVersionService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'puter-version': PuterVersionService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const versionService = testKernel.services!.get('puter-version') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(versionService).toBeInstanceOf(PuterVersionService);
|
||||
});
|
||||
|
||||
it('should have boot_time set after init', () => {
|
||||
expect(versionService.boot_time).toBeDefined();
|
||||
expect(typeof versionService.boot_time).toBe('number');
|
||||
expect(versionService.boot_time).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should return version info', () => {
|
||||
const versionInfo = versionService.get_version();
|
||||
|
||||
expect(versionInfo).toBeDefined();
|
||||
expect(versionInfo).toHaveProperty('version');
|
||||
expect(versionInfo).toHaveProperty('environment');
|
||||
expect(versionInfo).toHaveProperty('location');
|
||||
expect(versionInfo).toHaveProperty('deploy_timestamp');
|
||||
});
|
||||
|
||||
it('should have valid version string', () => {
|
||||
const versionInfo = versionService.get_version();
|
||||
|
||||
expect(typeof versionInfo.version).toBe('string');
|
||||
expect(versionInfo.version).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have deploy_timestamp matching boot_time', () => {
|
||||
const versionInfo = versionService.get_version();
|
||||
|
||||
expect(versionInfo.deploy_timestamp).toBe(versionService.boot_time);
|
||||
});
|
||||
|
||||
it('should have environment from config', () => {
|
||||
const versionInfo = versionService.get_version();
|
||||
|
||||
// Environment might be undefined in test context
|
||||
expect(versionInfo).toHaveProperty('environment');
|
||||
});
|
||||
|
||||
it('should have location from config', () => {
|
||||
const versionInfo = versionService.get_version();
|
||||
|
||||
// Location might be undefined in test context
|
||||
expect(versionInfo).toHaveProperty('location');
|
||||
});
|
||||
|
||||
it('should return consistent version info on multiple calls', () => {
|
||||
const versionInfo1 = versionService.get_version();
|
||||
const versionInfo2 = versionService.get_version();
|
||||
|
||||
expect(versionInfo1.version).toBe(versionInfo2.version);
|
||||
expect(versionInfo1.deploy_timestamp).toBe(versionInfo2.deploy_timestamp);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { RegistryService } from './RegistryService';
|
||||
|
||||
describe('RegistryService', async () => {
|
||||
// Initialize globalThis.kv for testing
|
||||
beforeAll(() => {
|
||||
if (!globalThis.kv) {
|
||||
globalThis.kv = new Map();
|
||||
globalThis.kv.set = function(key, value) {
|
||||
return Map.prototype.set.call(this, key, value);
|
||||
};
|
||||
globalThis.kv.get = function(key) {
|
||||
return Map.prototype.get.call(this, key);
|
||||
};
|
||||
globalThis.kv.exists = function(key) {
|
||||
return this.has(key);
|
||||
};
|
||||
globalThis.kv.del = function(key) {
|
||||
return this.delete(key);
|
||||
};
|
||||
globalThis.kv.keys = function(pattern) {
|
||||
const prefix = pattern.replace('*', '');
|
||||
return Array.from(this.keys()).filter(k => k.startsWith(prefix));
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
registry: RegistryService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const registryService = testKernel.services!.get('registry') as RegistryService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(registryService).toBeInstanceOf(RegistryService);
|
||||
});
|
||||
|
||||
it('should register a collection', () => {
|
||||
const collection = registryService.register_collection('test-collection');
|
||||
expect(collection).toBeDefined();
|
||||
});
|
||||
|
||||
it('should retrieve registered collection', () => {
|
||||
registryService.register_collection('retrieve-collection');
|
||||
const collection = registryService.get('retrieve-collection');
|
||||
expect(collection).toBeDefined();
|
||||
});
|
||||
|
||||
it('should throw error when registering duplicate collection', () => {
|
||||
registryService.register_collection('duplicate-collection');
|
||||
expect(() => {
|
||||
registryService.register_collection('duplicate-collection');
|
||||
}).toThrow('collection duplicate-collection already exists');
|
||||
});
|
||||
|
||||
it('should throw error when getting non-existent collection', () => {
|
||||
expect(() => {
|
||||
registryService.get('non-existent-collection');
|
||||
}).toThrow('collection non-existent-collection does not exist');
|
||||
});
|
||||
|
||||
it('should allow setting values in collection', () => {
|
||||
const collection = registryService.register_collection('value-collection');
|
||||
collection.set('key1', 'value1');
|
||||
expect(collection.get('key1')).toBe('value1');
|
||||
});
|
||||
|
||||
it('should allow checking existence in collection', () => {
|
||||
const collection = registryService.register_collection('exists-collection');
|
||||
collection.set('existing-key', 'value');
|
||||
expect(collection.exists('existing-key')).toBe(true);
|
||||
expect(collection.exists('non-existing-key')).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow deleting from collection', () => {
|
||||
const collection = registryService.register_collection('delete-collection');
|
||||
collection.set('delete-key', 'value');
|
||||
expect(collection.exists('delete-key')).toBe(true);
|
||||
collection.del('delete-key');
|
||||
expect(collection.exists('delete-key')).toBe(false);
|
||||
});
|
||||
|
||||
it('should support multiple independent collections', () => {
|
||||
const collection1 = registryService.register_collection('coll1');
|
||||
const collection2 = registryService.register_collection('coll2');
|
||||
|
||||
collection1.set('key', 'value1');
|
||||
collection2.set('key', 'value2');
|
||||
|
||||
expect(collection1.get('key')).toBe('value1');
|
||||
expect(collection2.get('key')).toBe('value2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { BackendScript, ScriptService } from './ScriptService';
|
||||
|
||||
describe('ScriptService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'script': ScriptService,
|
||||
},
|
||||
initLevelString: 'construct',
|
||||
});
|
||||
|
||||
const scriptService = testKernel.services!.get('script') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(scriptService).toBeInstanceOf(ScriptService);
|
||||
});
|
||||
|
||||
it('should have empty scripts array initially', () => {
|
||||
expect(scriptService.scripts).toBeDefined();
|
||||
expect(Array.isArray(scriptService.scripts)).toBe(true);
|
||||
});
|
||||
|
||||
it('should register a script', () => {
|
||||
const initialLength = scriptService.scripts.length;
|
||||
const scriptFn = async (ctx: any, args: any[]) => {
|
||||
return 'result';
|
||||
};
|
||||
|
||||
scriptService.register('test-script', scriptFn);
|
||||
|
||||
expect(scriptService.scripts.length).toBe(initialLength + 1);
|
||||
});
|
||||
|
||||
it('should create BackendScript instance on registration', () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
const scriptFn = async (ctx: any, args: any[]) => {};
|
||||
|
||||
service.register('backend-script', scriptFn);
|
||||
|
||||
const lastScript = service.scripts[service.scripts.length - 1];
|
||||
expect(lastScript).toBeInstanceOf(BackendScript);
|
||||
expect(lastScript.name).toBe('backend-script');
|
||||
});
|
||||
|
||||
it('should store script function', () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
const scriptFn = async (ctx: any, args: any[]) => 'my-result';
|
||||
|
||||
service.register('fn-script', scriptFn);
|
||||
|
||||
const lastScript = service.scripts[service.scripts.length - 1];
|
||||
expect(lastScript.fn).toBe(scriptFn);
|
||||
});
|
||||
|
||||
it('should execute registered script', async () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
let executed = false;
|
||||
|
||||
const scriptFn = async (ctx: any, args: any[]) => {
|
||||
executed = true;
|
||||
return 'executed';
|
||||
};
|
||||
|
||||
service.register('exec-script', scriptFn);
|
||||
const script = service.scripts[service.scripts.length - 1];
|
||||
|
||||
const result = await script.run({}, []);
|
||||
|
||||
expect(executed).toBe(true);
|
||||
expect(result).toBe('executed');
|
||||
});
|
||||
|
||||
it('should pass context to script', async () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
let receivedCtx: any = null;
|
||||
|
||||
const scriptFn = async (ctx: any, args: any[]) => {
|
||||
receivedCtx = ctx;
|
||||
};
|
||||
|
||||
service.register('ctx-script', scriptFn);
|
||||
const script = service.scripts[service.scripts.length - 1];
|
||||
|
||||
const testCtx = { test: 'context' };
|
||||
await script.run(testCtx, []);
|
||||
|
||||
expect(receivedCtx).toBe(testCtx);
|
||||
});
|
||||
|
||||
it('should pass arguments to script', async () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
let receivedArgs: any[] = [];
|
||||
|
||||
const scriptFn = async (ctx: any, args: any[]) => {
|
||||
receivedArgs = args;
|
||||
};
|
||||
|
||||
service.register('args-script', scriptFn);
|
||||
const script = service.scripts[service.scripts.length - 1];
|
||||
|
||||
const testArgs = ['arg1', 'arg2', 'arg3'];
|
||||
await script.run({}, testArgs);
|
||||
|
||||
expect(receivedArgs).toEqual(testArgs);
|
||||
});
|
||||
|
||||
it('should handle multiple script registrations', () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
|
||||
service.register('script1', async () => {});
|
||||
service.register('script2', async () => {});
|
||||
service.register('script3', async () => {});
|
||||
|
||||
const scriptNames = service.scripts.map((s: any) => s.name);
|
||||
expect(scriptNames).toContain('script1');
|
||||
expect(scriptNames).toContain('script2');
|
||||
expect(scriptNames).toContain('script3');
|
||||
});
|
||||
|
||||
it('should allow scripts to return values', async () => {
|
||||
const service = testKernel.services!.get('script') as any;
|
||||
|
||||
service.register('return-script', async (ctx: any, args: any[]) => {
|
||||
return { success: true, data: args[0] };
|
||||
});
|
||||
|
||||
const script = service.scripts[service.scripts.length - 1];
|
||||
const result = await script.run({}, ['test-data']);
|
||||
|
||||
expect(result).toEqual({ success: true, data: 'test-data' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('BackendScript', () => {
|
||||
it('should create script with name and function', () => {
|
||||
const fn = async () => {};
|
||||
const script = new BackendScript('test', fn);
|
||||
|
||||
expect(script.name).toBe('test');
|
||||
expect(script.fn).toBe(fn);
|
||||
});
|
||||
|
||||
it('should execute script function', async () => {
|
||||
let executed = false;
|
||||
const fn = async () => { executed = true; };
|
||||
const script = new BackendScript('exec', fn);
|
||||
|
||||
await script.run({}, []);
|
||||
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass parameters to function', async () => {
|
||||
let receivedCtx: any = null;
|
||||
let receivedArgs: any = null;
|
||||
|
||||
const fn = async (ctx: any, args: any) => {
|
||||
receivedCtx = ctx;
|
||||
receivedArgs = args;
|
||||
};
|
||||
|
||||
const script = new BackendScript('params', fn);
|
||||
const ctx = { test: true };
|
||||
const args = ['a', 'b'];
|
||||
|
||||
await script.run(ctx, args);
|
||||
|
||||
expect(receivedCtx).toBe(ctx);
|
||||
expect(receivedArgs).toBe(args);
|
||||
});
|
||||
|
||||
it('should return function result', async () => {
|
||||
const fn = async () => 'result-value';
|
||||
const script = new BackendScript('return', fn);
|
||||
|
||||
const result = await script.run({}, []);
|
||||
|
||||
expect(result).toBe('result-value');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { ShutdownService } from './ShutdownService';
|
||||
|
||||
describe('ShutdownService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
shutdown: ShutdownService,
|
||||
},
|
||||
initLevelString: 'construct',
|
||||
});
|
||||
|
||||
const shutdownService = testKernel.services!.get('shutdown') as ShutdownService;
|
||||
|
||||
// Mock the logger for the service
|
||||
shutdownService.log = {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
};
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(shutdownService).toBeInstanceOf(ShutdownService);
|
||||
});
|
||||
|
||||
it('should have shutdown method', () => {
|
||||
expect(typeof shutdownService.shutdown).toBe('function');
|
||||
});
|
||||
|
||||
it('should call process.exit when shutdown is called', () => {
|
||||
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
||||
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation((() => {}) as any);
|
||||
|
||||
shutdownService.shutdown({ reason: 'test shutdown', code: 0 });
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
expect(stdoutSpy).toHaveBeenCalled();
|
||||
|
||||
exitSpy.mockRestore();
|
||||
stdoutSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should use default exit code when not provided', () => {
|
||||
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
||||
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation((() => {}) as any);
|
||||
|
||||
shutdownService.shutdown({ reason: 'test' });
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
|
||||
exitSpy.mockRestore();
|
||||
stdoutSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should use custom exit code when provided', () => {
|
||||
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
||||
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation((() => {}) as any);
|
||||
|
||||
shutdownService.shutdown({ reason: 'error', code: 1 });
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
|
||||
exitSpy.mockRestore();
|
||||
stdoutSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should work without any parameters', () => {
|
||||
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
||||
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation((() => {}) as any);
|
||||
|
||||
shutdownService.shutdown();
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
|
||||
exitSpy.mockRestore();
|
||||
stdoutSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { SystemValidationService } from './SystemValidationService';
|
||||
|
||||
describe('SystemValidationService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
'system-validation': SystemValidationService,
|
||||
},
|
||||
initLevelString: 'init',
|
||||
});
|
||||
|
||||
const systemValidationService = testKernel.services!.get('system-validation') as any;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(systemValidationService).toBeInstanceOf(SystemValidationService);
|
||||
});
|
||||
|
||||
it('should have mark_invalid method', () => {
|
||||
expect(systemValidationService.mark_invalid).toBeDefined();
|
||||
expect(typeof systemValidationService.mark_invalid).toBe('function');
|
||||
});
|
||||
|
||||
it('should handle mark_invalid in dev environment', async () => {
|
||||
// Set up dev environment
|
||||
const originalEnv = systemValidationService.global_config?.env;
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = 'dev';
|
||||
}
|
||||
|
||||
// Mock the error service
|
||||
const mockReport = vi.fn();
|
||||
systemValidationService.errors = {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
// Mock dev-console service
|
||||
const mockTurnOn = vi.fn();
|
||||
const mockAddWidget = vi.fn();
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: mockTurnOn,
|
||||
add_widget: mockAddWidget,
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
await systemValidationService.mark_invalid('test message', new Error('test error'));
|
||||
|
||||
// Verify error was reported
|
||||
expect(mockReport).toHaveBeenCalledWith('INVALID SYSTEM STATE', expect.objectContaining({
|
||||
message: 'test message',
|
||||
trace: true,
|
||||
alarm: true,
|
||||
}));
|
||||
|
||||
// Verify dev console was called
|
||||
expect(mockTurnOn).toHaveBeenCalled();
|
||||
expect(mockAddWidget).toHaveBeenCalled();
|
||||
} finally {
|
||||
// Restore original environment
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
|
||||
it('should create source error if not provided', async () => {
|
||||
const originalEnv = systemValidationService.global_config?.env;
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = 'dev';
|
||||
}
|
||||
|
||||
const mockReport = vi.fn();
|
||||
systemValidationService.errors = {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: vi.fn(),
|
||||
add_widget: vi.fn(),
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
await systemValidationService.mark_invalid('test without source');
|
||||
|
||||
expect(mockReport).toHaveBeenCalledWith('INVALID SYSTEM STATE', expect.objectContaining({
|
||||
source: expect.any(Error),
|
||||
}));
|
||||
} finally {
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
|
||||
it('should report with correct parameters', async () => {
|
||||
const originalEnv = systemValidationService.global_config?.env;
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = 'dev';
|
||||
}
|
||||
|
||||
const mockReport = vi.fn();
|
||||
systemValidationService.errors = {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: vi.fn(),
|
||||
add_widget: vi.fn(),
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
const testError = new Error('specific error');
|
||||
await systemValidationService.mark_invalid('specific message', testError);
|
||||
|
||||
expect(mockReport).toHaveBeenCalledWith('INVALID SYSTEM STATE', {
|
||||
source: testError,
|
||||
message: 'specific message',
|
||||
trace: true,
|
||||
alarm: true,
|
||||
});
|
||||
} finally {
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import { TraceService } from './TraceService';
|
||||
|
||||
describe('TraceService', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
trace: TraceService,
|
||||
},
|
||||
initLevelString: 'construct',
|
||||
});
|
||||
|
||||
const traceService = testKernel.services!.get('trace') as TraceService;
|
||||
|
||||
it('should be instantiated', () => {
|
||||
expect(traceService).toBeInstanceOf(TraceService);
|
||||
});
|
||||
|
||||
it('should have a tracer', () => {
|
||||
expect(traceService.tracer).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create spans with spanify', async () => {
|
||||
const result = await traceService.spanify('test-span', async ({ span }) => {
|
||||
expect(span).toBeDefined();
|
||||
return 'test-result';
|
||||
});
|
||||
expect(result).toBe('test-result');
|
||||
});
|
||||
|
||||
it('should execute callback within span', async () => {
|
||||
let executed = false;
|
||||
await traceService.spanify('exec-span', async () => {
|
||||
executed = true;
|
||||
});
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle errors in spanify', async () => {
|
||||
await expect(
|
||||
traceService.spanify('error-span', async () => {
|
||||
throw new Error('Test span error');
|
||||
})
|
||||
).rejects.toThrow('Test span error');
|
||||
});
|
||||
|
||||
it('should support options in spanify', async () => {
|
||||
const result = await traceService.spanify('options-span', async ({ span }) => {
|
||||
return 'with-options';
|
||||
}, {
|
||||
attributes: { 'test.attribute': 'value' },
|
||||
});
|
||||
expect(result).toBe('with-options');
|
||||
});
|
||||
|
||||
it('should return values from span callback', async () => {
|
||||
const obj = { value: 42 };
|
||||
const result = await traceService.spanify('return-span', async () => {
|
||||
return obj;
|
||||
});
|
||||
expect(result).toEqual(obj);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user