initial
This commit is contained in:
157
test/test.logger.ts
Normal file
157
test/test.logger.ts
Normal 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
216
test/test.showcase.ts
Normal 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
323
test/test.ts
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user