initial
Some checks failed
CI / Type Check & Lint (push) Failing after 5s
CI / Build Test (Current Platform) (push) Failing after 5s
CI / Build All Platforms (push) Successful in 49s

This commit is contained in:
2026-01-30 03:16:57 +00:00
commit daaf6559e3
80 changed files with 14430 additions and 0 deletions

157
test/test.logger.ts Normal file
View File

@@ -0,0 +1,157 @@
import { assert, assertEquals } from 'jsr:@std/assert@^1.0.0';
import { Logger } from '../ts/logger.ts';
// Create a Logger instance for testing
const logger = new Logger();
Deno.test('should create a logger instance', () => {
assert(logger instanceof Logger);
});
Deno.test('should log messages with different log levels', () => {
// We're not testing console output directly, just ensuring no errors
logger.log('Regular log message');
logger.error('Error message');
logger.warn('Warning message');
logger.success('Success message');
// Just assert that the test runs without errors
assert(true);
});
Deno.test('should create a logbox with title, content, and end', () => {
// Just ensuring no errors occur
logger.logBoxTitle('Test Box', 40);
logger.logBoxLine('This is a test line');
logger.logBoxEnd();
// Just assert that the test runs without errors
assert(true);
});
Deno.test('should handle width persistence between logbox calls', () => {
logger.logBoxTitle('Width Test', 45);
// These should use the width from the title
logger.logBoxLine('Line 1');
logger.logBoxLine('Line 2');
logger.logBoxEnd();
let errorThrown = false;
try {
// This should work fine after the reset in logBoxEnd
logger.logBoxTitle('New Box', 30);
logger.logBoxLine('New line');
logger.logBoxEnd();
} catch (_error) {
errorThrown = true;
}
assertEquals(errorThrown, false);
});
Deno.test('should use default width when no width is specified', () => {
// This should automatically use the default width instead of throwing
let errorThrown = false;
try {
logger.logBoxLine('This should use default width');
logger.logBoxEnd();
} catch (_error) {
errorThrown = true;
}
// Verify no error was thrown
assertEquals(errorThrown, false);
});
Deno.test('should create a complete logbox in one call', () => {
// Just ensuring no errors occur
logger.logBox('Complete Box', [
'Line 1',
'Line 2',
'Line 3',
], 40);
// Just assert that the test runs without errors
assert(true);
});
Deno.test('should handle content that exceeds box width', () => {
// Just ensuring no errors occur when content is too long
logger.logBox('Truncation Test', [
'This line is way too long and should be truncated because it exceeds the available space',
], 30);
// Just assert that the test runs without errors
assert(true);
});
Deno.test('should create dividers with custom characters', () => {
// Just ensuring no errors occur
logger.logDivider(30);
logger.logDivider(20, '*');
// Just assert that the test runs without errors
assert(true);
});
Deno.test('should create divider with default width', () => {
// This should use the default width
logger.logDivider(undefined, '-');
// Just assert that the test runs without errors
assert(true);
});
Deno.test('Logger Demo', () => {
console.log('\n=== LOGGER DEMO ===\n');
// Basic logging
logger.log('Regular log message');
logger.error('Error message');
logger.warn('Warning message');
logger.success('Success message');
// Logbox with title, content lines, and end
logger.logBoxTitle('Configuration Loaded', 50);
logger.logBoxLine('SNMP Settings:');
logger.logBoxLine(' Host: 127.0.0.1');
logger.logBoxLine(' Port: 161');
logger.logBoxLine(' Version: 1');
logger.logBoxEnd();
// Complete logbox in one call
logger.logBox('UPS Status', [
'Power Status: onBattery',
'Battery Capacity: 75%',
'Runtime Remaining: 30 minutes',
], 45);
// Logbox with content that's too long for the width
logger.logBox('Truncation Example', [
'This line is short enough to fit within the box width',
'This line is way too long and will be truncated because it exceeds the available space for content within the logbox',
], 40);
// Demonstrating logbox width being remembered
logger.logBoxTitle('Width Persistence Example', 60);
logger.logBoxLine('These lines use the width from the title');
logger.logBoxLine('No need to specify the width again');
logger.logBoxEnd();
// Demonstrating default width
console.log('\nDefault Width Example:');
logger.logBoxLine('This line uses the default width');
logger.logBoxLine('Still using default width');
logger.logBoxEnd();
// Divider example
logger.log('\nDivider example:');
logger.logDivider(30);
logger.logDivider(30, '*');
logger.logDivider(undefined, '=');
assert(true);
});

216
test/test.showcase.ts Normal file
View File

@@ -0,0 +1,216 @@
/**
* Showcase test for ModelGrid CLI outputs
* Demonstrates all the beautiful colored output features
*
* Run with: deno run --allow-all test/showcase.ts
*/
import { type ITableColumn, logger } from '../ts/logger.ts';
import { theme } from '../ts/colors.ts';
console.log('');
console.log('═'.repeat(80));
logger.highlight('MODELGRID CLI OUTPUT SHOWCASE');
logger.dim('Demonstrating beautiful, colored terminal output');
console.log('═'.repeat(80));
console.log('');
// === 1. Basic Logging Methods ===
logger.logBoxTitle('Basic Logging Methods', 60, 'info');
logger.logBoxLine('');
logger.log('Normal log message (default color)');
logger.success('Success message with ✓ symbol');
logger.error('Error message with ✗ symbol');
logger.warn('Warning message with ⚠ symbol');
logger.info('Info message with symbol');
logger.dim('Dim/secondary text for less important info');
logger.highlight('Highlighted/bold text for emphasis');
logger.logBoxLine('');
logger.logBoxEnd();
console.log('');
// === 2. Colored Boxes ===
logger.logBoxTitle('Colored Box Styles', 60);
logger.logBoxLine('');
logger.logBoxLine('Boxes can be styled with different colors:');
logger.logBoxEnd();
console.log('');
logger.logBox(
'Success Box (Green)',
[
'Used for successful operations',
'Container started, model loaded, etc.',
],
60,
'success',
);
console.log('');
logger.logBox(
'Error Box (Red)',
[
'Used for critical errors and failures',
'Configuration errors, GPU detection failures, etc.',
],
60,
'error',
);
console.log('');
logger.logBox(
'Warning Box (Yellow)',
[
'Used for warnings and deprecations',
'Driver updates needed, low VRAM, etc.',
],
60,
'warning',
);
console.log('');
logger.logBox(
'Info Box (Cyan)',
[
'Used for informational messages',
'Version info, model info, etc.',
],
60,
'info',
);
console.log('');
// === 3. GPU Status Table ===
const gpuColumns: ITableColumn[] = [
{ header: 'ID', key: 'id' },
{ header: 'Model', key: 'model' },
{ header: 'VRAM', key: 'vram', align: 'right' },
{
header: 'Status',
key: 'status',
color: (v) => {
if (v.includes('Ready')) return theme.success(v);
if (v.includes('Busy')) return theme.warning(v);
return theme.dim(v);
},
},
{ header: 'Utilization', key: 'utilization', align: 'right' },
];
const gpuData = [
{
id: 'gpu-0',
model: 'NVIDIA RTX 4090',
vram: '24 GB',
status: 'Ready',
utilization: '15%',
},
{
id: 'gpu-1',
model: 'NVIDIA RTX 4090',
vram: '24 GB',
status: 'Busy',
utilization: '92%',
},
{
id: 'gpu-2',
model: 'AMD RX 7900 XTX',
vram: '24 GB',
status: 'Ready',
utilization: '0%',
},
];
logger.logTable(gpuColumns, gpuData, 'GPU Devices');
console.log('');
// === 4. Container Table ===
const containerColumns: ITableColumn[] = [
{ header: 'ID', key: 'id' },
{ header: 'Type', key: 'type' },
{ header: 'Status', key: 'status' },
{ header: 'GPU', key: 'gpu' },
{ header: 'Models', key: 'models', align: 'right' },
];
const containerData = [
{ id: 'ollama-1', type: 'ollama', status: 'Running', gpu: 'gpu-0', models: '3' },
{ id: 'vllm-1', type: 'vllm', status: 'Running', gpu: 'gpu-1', models: '1' },
];
logger.logTable(containerColumns, containerData, 'AI Containers');
console.log('');
// === 5. Service Status Example ===
logger.logBoxTitle('Service Status', 70, 'success');
logger.logBoxLine('');
logger.logBoxLine(`Status: ${theme.statusActive('Active (Running)')}`);
logger.logBoxLine(`Enabled: ${theme.success('Yes')}`);
logger.logBoxLine(`Uptime: 2 days, 5 hours, 23 minutes`);
logger.logBoxLine(`PID: ${theme.dim('12345')}`);
logger.logBoxLine(`Memory: ${theme.dim('245.2 MB')}`);
logger.logBoxLine('');
logger.logBoxEnd();
console.log('');
// === 6. Configuration Example ===
logger.logBoxTitle('Configuration', 70);
logger.logBoxLine('');
logger.logBoxLine(`GPUs Detected: ${theme.highlight('3')}`);
logger.logBoxLine(`Containers: ${theme.highlight('2')}`);
logger.logBoxLine(`API Port: ${theme.dim('8080')}`);
logger.logBoxLine(`Config File: ${theme.path('/etc/modelgrid/config.json')}`);
logger.logBoxLine('');
logger.logBoxEnd();
console.log('');
// === 7. Model List Example ===
const modelColumns: ITableColumn[] = [
{ header: 'Model', key: 'name' },
{ header: 'Container', key: 'container' },
{ header: 'Size', key: 'size', align: 'right' },
{ header: 'Status', key: 'status' },
];
const modelData = [
{ name: 'llama3:8b', container: 'ollama-1', size: '4.7 GB', status: 'Loaded' },
{ name: 'mistral:7b', container: 'ollama-1', size: '4.1 GB', status: 'Loaded' },
{ name: 'llama3:70b', container: 'vllm-1', size: '40 GB', status: 'Loaded' },
];
logger.logTable(modelColumns, modelData, 'Loaded Models');
console.log('');
// === 8. Error Example ===
logger.logBoxTitle('Error Example', 70, 'error');
logger.logBoxLine('');
logger.logBoxLine(`✗ Failed to start container vllm-2`);
logger.logBoxLine('');
logger.logBoxLine('Possible causes:');
logger.logBoxLine(` ${theme.dim('• Insufficient VRAM on assigned GPU')}`);
logger.logBoxLine(` ${theme.dim('• Docker daemon not running')}`);
logger.logBoxLine(` ${theme.dim('• NVIDIA container toolkit not installed')}`);
logger.logBoxLine('');
logger.logBoxLine(`Try: ${theme.command('modelgrid gpu status')}`);
logger.logBoxLine('');
logger.logBoxEnd();
console.log('');
// === Final Summary ===
console.log('═'.repeat(80));
logger.success('CLI Output Showcase Complete!');
logger.dim('All color and formatting features demonstrated');
console.log('═'.repeat(80));
console.log('');

323
test/test.ts Normal file
View File

@@ -0,0 +1,323 @@
import { assert, assertEquals, assertExists } from 'jsr:@std/assert@^1.0.0';
import { shortId } from '../ts/helpers/shortid.ts';
// =============================================================================
// UNIT TESTS - ModelGrid Core Components
// =============================================================================
// -----------------------------------------------------------------------------
// shortId() Tests
// -----------------------------------------------------------------------------
Deno.test('shortId: generates 6-character string', () => {
const id = shortId();
assertEquals(id.length, 6);
});
Deno.test('shortId: contains only alphanumeric characters', () => {
const id = shortId();
const alphanumericRegex = /^[a-zA-Z0-9]+$/;
assert(alphanumericRegex.test(id), `ID "${id}" contains non-alphanumeric characters`);
});
Deno.test('shortId: generates unique IDs', () => {
const ids = new Set<string>();
const count = 100;
for (let i = 0; i < count; i++) {
ids.add(shortId());
}
// All IDs should be unique (statistically extremely likely for 100 IDs)
assertEquals(ids.size, count, 'Generated IDs should be unique');
});
// -----------------------------------------------------------------------------
// Interface Type Tests
// -----------------------------------------------------------------------------
Deno.test('IModelGridConfig: valid config structure', () => {
const config = {
version: '1.0',
api: {
port: 8080,
host: '0.0.0.0',
apiKeys: ['test-key'],
},
docker: {
networkName: 'modelgrid',
runtime: 'docker' as const,
},
gpus: {
autoDetect: true,
assignments: {},
},
containers: [],
models: {
greenlistUrl: 'https://example.com/greenlit.json',
autoPull: true,
defaultContainer: 'ollama',
autoLoad: [],
},
checkInterval: 30000,
};
assertExists(config.version);
assertExists(config.api);
assertExists(config.docker);
assertExists(config.gpus);
assertExists(config.containers);
assertExists(config.models);
assertEquals(config.api.port, 8080);
assertEquals(config.docker.runtime, 'docker');
});
Deno.test('IGpuInfo: valid GPU info structure', () => {
const gpu = {
id: 'gpu-0',
vendor: 'nvidia' as const,
model: 'RTX 4090',
vram: 24576,
driverVersion: '535.154.05',
cudaVersion: '12.2',
pciSlot: '0000:01:00.0',
};
assertExists(gpu.id);
assertExists(gpu.vendor);
assertExists(gpu.model);
assert(gpu.vram > 0, 'VRAM should be positive');
assert(['nvidia', 'amd', 'intel'].includes(gpu.vendor), 'Vendor should be valid');
});
Deno.test('IContainerConfig: valid container config structure', () => {
const container = {
id: 'ollama-1',
type: 'ollama' as const,
name: 'Ollama Container',
image: 'ollama/ollama:latest',
gpuIds: ['gpu-0'],
port: 11434,
models: ['llama3:8b'],
};
assertExists(container.id);
assertExists(container.type);
assertExists(container.name);
assertExists(container.image);
assert(container.gpuIds.length > 0, 'Should have at least one GPU');
assert(container.port > 0, 'Port should be positive');
});
// -----------------------------------------------------------------------------
// Greenlit Model Tests
// -----------------------------------------------------------------------------
Deno.test('Greenlit model validation: valid model passes', () => {
const greenlist = {
version: '1.0',
models: [
{ name: 'llama3:8b', container: 'ollama', minVram: 8 },
{ name: 'mistral:7b', container: 'ollama', minVram: 8 },
],
};
const requestedModel = 'llama3:8b';
const availableVram = 24; // GB
const model = greenlist.models.find((m) => m.name === requestedModel);
assertExists(model, 'Model should be in greenlist');
assert(availableVram >= model.minVram, 'Should have enough VRAM');
});
Deno.test('Greenlit model validation: insufficient VRAM fails', () => {
const greenlist = {
version: '1.0',
models: [
{ name: 'llama3:70b', container: 'vllm', minVram: 48 },
],
};
const requestedModel = 'llama3:70b';
const availableVram = 24; // GB
const model = greenlist.models.find((m) => m.name === requestedModel);
assertExists(model, 'Model should be in greenlist');
assert(availableVram < model.minVram, 'Should NOT have enough VRAM');
});
Deno.test('Greenlit model validation: unlisted model rejected', () => {
const greenlist = {
version: '1.0',
models: [
{ name: 'llama3:8b', container: 'ollama', minVram: 8 },
],
};
const requestedModel = 'some-random-model:latest';
const model = greenlist.models.find((m) => m.name === requestedModel);
assertEquals(model, undefined, 'Model should NOT be in greenlist');
});
// -----------------------------------------------------------------------------
// API Request Validation Tests
// -----------------------------------------------------------------------------
Deno.test('Chat completion request: valid request passes', () => {
const request = {
model: 'llama3:8b',
messages: [
{ role: 'user', content: 'Hello!' },
],
};
assertExists(request.model, 'Model should be specified');
assert(request.messages.length > 0, 'Should have at least one message');
assert(
request.messages.every((m) => m.role && m.content),
'All messages should have role and content',
);
});
Deno.test('Chat completion request: missing model fails', () => {
const request = {
messages: [
{ role: 'user', content: 'Hello!' },
],
};
assertEquals((request as { model?: string }).model, undefined, 'Model should be missing');
});
Deno.test('Chat completion request: empty messages fails', () => {
const request = {
model: 'llama3:8b',
messages: [],
};
assertEquals(request.messages.length, 0, 'Messages should be empty');
});
Deno.test('Embedding request: valid request passes', () => {
const request = {
model: 'llama3:8b',
input: 'Hello, world!',
};
assertExists(request.model, 'Model should be specified');
assertExists(request.input, 'Input should be specified');
});
Deno.test('Embedding request: array input passes', () => {
const request = {
model: 'llama3:8b',
input: ['Hello', 'World'],
};
assertExists(request.model, 'Model should be specified');
assert(Array.isArray(request.input), 'Input should be an array');
assert(request.input.length > 0, 'Input should not be empty');
});
// -----------------------------------------------------------------------------
// Container Type Tests
// -----------------------------------------------------------------------------
Deno.test('Container types: ollama configuration', () => {
const ollamaConfig = {
type: 'ollama' as const,
image: 'ollama/ollama:latest',
defaultPort: 11434,
apiPath: '/api',
};
assertEquals(ollamaConfig.type, 'ollama');
assertEquals(ollamaConfig.defaultPort, 11434);
});
Deno.test('Container types: vllm configuration', () => {
const vllmConfig = {
type: 'vllm' as const,
image: 'vllm/vllm-openai:latest',
defaultPort: 8000,
apiPath: '/v1',
};
assertEquals(vllmConfig.type, 'vllm');
assertEquals(vllmConfig.defaultPort, 8000);
});
Deno.test('Container types: tgi configuration', () => {
const tgiConfig = {
type: 'tgi' as const,
image: 'ghcr.io/huggingface/text-generation-inference:latest',
defaultPort: 80,
apiPath: '/generate',
};
assertEquals(tgiConfig.type, 'tgi');
assertEquals(tgiConfig.defaultPort, 80);
});
// -----------------------------------------------------------------------------
// GPU Vendor Tests
// -----------------------------------------------------------------------------
Deno.test('GPU vendors: NVIDIA detection pattern', () => {
const nvidiaPatterns = ['NVIDIA', 'GeForce', 'Quadro', 'Tesla', 'RTX', 'GTX'];
const gpuName = 'NVIDIA GeForce RTX 4090';
const isNvidia = nvidiaPatterns.some((pattern) =>
gpuName.toUpperCase().includes(pattern.toUpperCase())
);
assert(isNvidia, 'Should detect NVIDIA GPU');
});
Deno.test('GPU vendors: AMD detection pattern', () => {
const amdPatterns = ['AMD', 'Radeon', 'RX'];
const gpuName = 'AMD Radeon RX 7900 XTX';
const isAmd = amdPatterns.some((pattern) =>
gpuName.toUpperCase().includes(pattern.toUpperCase())
);
assert(isAmd, 'Should detect AMD GPU');
});
Deno.test('GPU vendors: Intel detection pattern', () => {
const intelPatterns = ['Intel', 'Arc', 'Iris', 'UHD'];
const gpuName = 'Intel Arc A770';
const isIntel = intelPatterns.some((pattern) =>
gpuName.toUpperCase().includes(pattern.toUpperCase())
);
assert(isIntel, 'Should detect Intel GPU');
});
// -----------------------------------------------------------------------------
// VRAM Calculation Tests
// -----------------------------------------------------------------------------
Deno.test('VRAM calculation: MB to GB conversion', () => {
const vramMB = 24576; // 24 GB in MB
const vramGB = vramMB / 1024;
assertEquals(vramGB, 24);
});
Deno.test('VRAM calculation: model fits in available VRAM', () => {
const availableVramGB = 24;
const modelRequiredVramGB = 8;
const overhead = 2; // GB for system overhead
const fits = (modelRequiredVramGB + overhead) <= availableVramGB;
assert(fits, 'Model should fit in available VRAM');
});
Deno.test('VRAM calculation: multiple models VRAM sum', () => {
const models = [
{ name: 'llama3:8b', vram: 8 },
{ name: 'mistral:7b', vram: 8 },
];
const totalVram = models.reduce((sum, m) => sum + m.vram, 0);
assertEquals(totalVram, 16);
});