/**
* Application Layer - Monitoring Workflow Tests
*
* Tests for the monitoring application layer interfaces and workflows
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestUtils } from '../../helpers/test-utils.js';
// Test-specific interfaces
interface ISystemMonitor {
getSystemMetrics(): SystemMetrics;
startMonitoring(config: MonitoringConfig): Promise<void>;
stopMonitoring(): Promise<void>;
getAlerts(): Alert[];
clearAlerts(): void;
}
interface IPerformanceMonitor {
getPerformanceMetrics(): PerformanceMetrics;
startProfiling(operation: string): Promise<string>;
stopProfiling(profileId: string): Promise<void>;
recordOperation(operation: string, latency: number, success: boolean): void;
getOperationStats(operation: string): OperationStats | undefined;
}
interface SystemMetrics {
timestamp: Date;
cpu: {
usage: number;
loadAverage: number[];
cores: number;
};
memory: {
total: number;
used: number;
free: number;
usage: number;
};
disk: {
total: number;
used: number;
free: number;
usage: number;
};
network: {
bytesIn: number;
bytesOut: number;
packetsIn: number;
packetsOut: number;
connectionsActive: number;
};
}
interface PerformanceMetrics {
timestamp: Date;
operations: {
indexing: OperationMetrics;
search: OperationMetrics;
serving: OperationMetrics;
};
cache: {
hitRate: number;
missRate: number;
evictionRate: number;
size: number;
maxSize: number;
};
embeddings: {
generationLatency: number;
cacheHitRate: number;
modelLoadTime: number;
batchSize: number;
};
}
interface OperationMetrics {
totalOperations: number;
successfulOperations: number;
failedOperations: number;
averageLatency: number;
throughput: number;
}
interface OperationStats {
total: number;
successful: number;
failed: number;
averageLatency: number;
throughput: number;
}
interface MonitoringConfig {
enabled: boolean;
interval: number;
retentionPeriod: number;
alerts: {
enabled: boolean;
rules: AlertRule[];
};
metrics: {
system: boolean;
performance: boolean;
custom: boolean;
};
exporters: {
type: string;
endpoint?: string;
path?: string;
interval: number;
}[];
}
interface AlertRule {
name: string;
metric: string;
operator: '>' | '<' | '=' | '>=' | '<=';
threshold: number;
level: AlertLevel;
duration: number;
description?: string;
actions?: string[];
}
type AlertLevel = 'info' | 'warning' | 'error' | 'critical';
interface Alert {
id: string;
rule: string;
level: AlertLevel;
message: string;
triggeredAt: Date;
resolvedAt?: Date;
value: number;
threshold: number;
}
describe('Application Layer - Monitoring', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await TestUtils.createTempDir('monitoring-test-');
});
afterEach(async () => {
await TestUtils.cleanupTempDir(tempDir);
});
describe('SystemMonitor Interface', () => {
it('should define proper system monitoring contract', () => {
const mockMonitor: Partial<ISystemMonitor> = {
getSystemMetrics: (): SystemMetrics => ({
timestamp: new Date(),
cpu: {
usage: 25.5,
loadAverage: [1.2, 1.1, 1.0],
cores: 8
},
memory: {
total: 16 * 1024 * 1024 * 1024, // 16GB
used: 8 * 1024 * 1024 * 1024, // 8GB
free: 8 * 1024 * 1024 * 1024, // 8GB
usage: 50.0
},
disk: {
total: 1024 * 1024 * 1024 * 1024, // 1TB
used: 512 * 1024 * 1024 * 1024, // 512GB
free: 512 * 1024 * 1024 * 1024, // 512GB
usage: 50.0
},
network: {
bytesIn: 1024000,
bytesOut: 512000,
packetsIn: 1000,
packetsOut: 800,
connectionsActive: 25
}
}),
startMonitoring: async (config: MonitoringConfig): Promise<void> => {},
stopMonitoring: async (): Promise<void> => {},
getAlerts: () => [],
clearAlerts: () => {}
};
expect(mockMonitor.getSystemMetrics).toBeDefined();
expect(mockMonitor.startMonitoring).toBeDefined();
expect(mockMonitor.stopMonitoring).toBeDefined();
expect(mockMonitor.getAlerts).toBeDefined();
expect(mockMonitor.clearAlerts).toBeDefined();
});
it('should track system metrics accurately', () => {
const metrics: SystemMetrics = {
timestamp: new Date(),
cpu: {
usage: 42.3,
loadAverage: [2.1, 1.8, 1.5],
cores: 16
},
memory: {
total: 32 * 1024 * 1024 * 1024, // 32GB
used: 24 * 1024 * 1024 * 1024, // 24GB
free: 8 * 1024 * 1024 * 1024, // 8GB
usage: 75.0
},
disk: {
total: 2 * 1024 * 1024 * 1024 * 1024, // 2TB
used: 1.5 * 1024 * 1024 * 1024 * 1024, // 1.5TB
free: 0.5 * 1024 * 1024 * 1024 * 1024, // 0.5TB
usage: 75.0
},
network: {
bytesIn: 5 * 1024 * 1024, // 5MB
bytesOut: 2 * 1024 * 1024, // 2MB
packetsIn: 5000,
packetsOut: 3000,
connectionsActive: 150
}
};
expect(metrics.cpu.usage).toBe(42.3);
expect(metrics.cpu.cores).toBe(16);
expect(metrics.memory.usage).toBe(75.0);
expect(metrics.disk.usage).toBe(75.0);
expect(metrics.network.connectionsActive).toBe(150);
});
});
describe('PerformanceMonitor Interface', () => {
it('should define proper performance monitoring contract', () => {
const mockMonitor: Partial<IPerformanceMonitor> = {
getPerformanceMetrics: (): PerformanceMetrics => ({
timestamp: new Date(),
operations: {
indexing: {
totalOperations: 100,
successfulOperations: 95,
failedOperations: 5,
averageLatency: 250,
throughput: 10.5
},
search: {
totalOperations: 500,
successfulOperations: 498,
failedOperations: 2,
averageLatency: 75,
throughput: 45.2
},
serving: {
totalOperations: 1000,
successfulOperations: 999,
failedOperations: 1,
averageLatency: 25,
throughput: 120.0
}
},
cache: {
hitRate: 85.5,
missRate: 14.5,
evictionRate: 2.1,
size: 128 * 1024 * 1024, // 128MB
maxSize: 256 * 1024 * 1024 // 256MB
},
embeddings: {
generationLatency: 150,
cacheHitRate: 60.0,
modelLoadTime: 5000,
batchSize: 32
}
}),
startProfiling: async (operation: string): Promise<string> => 'profile-id',
stopProfiling: async (profileId: string): Promise<void> => {},
recordOperation: (operation: string, latency: number, success: boolean): void => {},
getOperationStats: (operation: string) => undefined
};
expect(mockMonitor.getPerformanceMetrics).toBeDefined();
expect(mockMonitor.startProfiling).toBeDefined();
expect(mockMonitor.stopProfiling).toBeDefined();
expect(mockMonitor.recordOperation).toBeDefined();
expect(mockMonitor.getOperationStats).toBeDefined();
});
it('should track performance metrics over time', () => {
const metrics: PerformanceMetrics = {
timestamp: new Date(),
operations: {
indexing: {
totalOperations: 250,
successfulOperations: 245,
failedOperations: 5,
averageLatency: 300,
throughput: 8.2
},
search: {
totalOperations: 1200,
successfulOperations: 1195,
failedOperations: 5,
averageLatency: 85,
throughput: 52.3
},
serving: {
totalOperations: 2500,
successfulOperations: 2498,
failedOperations: 2,
averageLatency: 30,
throughput: 98.7
}
},
cache: {
hitRate: 78.5,
missRate: 21.5,
evictionRate: 5.2,
size: 200 * 1024 * 1024, // 200MB
maxSize: 512 * 1024 * 1024 // 512MB
},
embeddings: {
generationLatency: 175,
cacheHitRate: 55.0,
modelLoadTime: 3500,
batchSize: 64
}
};
expect(metrics.operations.indexing.totalOperations).toBe(250);
expect(metrics.operations.search.averageLatency).toBe(85);
expect(metrics.cache.hitRate).toBe(78.5);
expect(metrics.embeddings.batchSize).toBe(64);
});
});
describe('Monitoring Configuration', () => {
it('should support comprehensive monitoring configuration', () => {
const config: MonitoringConfig = {
enabled: true,
interval: 60000, // 1 minute
retentionPeriod: 7 * 24 * 60 * 60 * 1000, // 7 days
alerts: {
enabled: true,
rules: [
{
name: 'High CPU Usage',
metric: 'cpu.usage',
operator: '>',
threshold: 80,
level: 'warning',
duration: 300000 // 5 minutes
},
{
name: 'Low Memory',
metric: 'memory.free',
operator: '<',
threshold: 1024 * 1024 * 1024, // 1GB
level: 'critical',
duration: 60000 // 1 minute
}
]
},
metrics: {
system: true,
performance: true,
custom: true
},
exporters: [
{
type: 'prometheus',
endpoint: 'http://localhost:9090/metrics',
interval: 30000
},
{
type: 'file',
path: '/var/log/folder-mcp/metrics.json',
interval: 300000
}
]
};
expect(config.enabled).toBe(true);
expect(config.interval).toBe(60000);
expect(config.alerts.rules).toHaveLength(2);
expect(config.exporters).toHaveLength(2);
});
it('should validate alert rule structure', () => {
const alertRule: AlertRule = {
name: 'Disk Space Low',
metric: 'disk.usage',
operator: '>',
threshold: 90,
level: 'critical',
duration: 120000, // 2 minutes
description: 'Disk usage is critically high',
actions: ['email', 'slack']
};
expect(alertRule.name).toBe('Disk Space Low');
expect(alertRule.metric).toBe('disk.usage');
expect(alertRule.operator).toBe('>');
expect(alertRule.threshold).toBe(90);
expect(alertRule.level).toBe('critical');
expect(alertRule.actions).toContain('email');
});
});
describe('Alert System', () => {
it('should support different alert levels', () => {
const levels: AlertLevel[] = ['info', 'warning', 'error', 'critical'];
levels.forEach(level => {
const rule: AlertRule = {
name: `Test ${level} Alert`,
metric: 'test.metric',
operator: '>',
threshold: 50,
level,
duration: 60000
};
expect(rule.level).toBe(level);
});
});
it('should handle alert triggering and resolution', () => {
const alert = {
id: 'alert-123',
rule: 'High CPU Usage',
level: 'warning' as AlertLevel,
message: 'CPU usage is above 80% for 5 minutes',
triggeredAt: new Date(),
resolvedAt: undefined,
value: 85.3,
threshold: 80
};
expect(alert.id).toBe('alert-123');
expect(alert.level).toBe('warning');
expect(alert.value).toBeGreaterThan(alert.threshold);
expect(alert.resolvedAt).toBeUndefined();
// Simulate resolution
const resolvedAlert = {
...alert,
resolvedAt: new Date()
};
expect(resolvedAlert.resolvedAt).toBeInstanceOf(Date);
});
});
describe('Metrics Collection', () => {
it('should collect metrics at regular intervals', async () => {
const metricsHistory: SystemMetrics[] = [];
// Simulate collecting metrics over time
for (let i = 0; i < 5; i++) {
const metrics: SystemMetrics = {
timestamp: new Date(Date.now() + i * 60000), // 1 minute intervals
cpu: {
usage: 20 + Math.random() * 60, // Random between 20-80%
loadAverage: [1 + Math.random(), 1 + Math.random(), 1 + Math.random()],
cores: 8
},
memory: {
total: 16 * 1024 * 1024 * 1024,
used: (8 + Math.random() * 4) * 1024 * 1024 * 1024, // Random between 8-12GB
free: 0, // Will be calculated
usage: 0 // Will be calculated
},
disk: {
total: 1024 * 1024 * 1024 * 1024,
used: 500 * 1024 * 1024 * 1024,
free: 524 * 1024 * 1024 * 1024,
usage: 48.8
},
network: {
bytesIn: Math.floor(Math.random() * 1024000),
bytesOut: Math.floor(Math.random() * 512000),
packetsIn: Math.floor(Math.random() * 1000),
packetsOut: Math.floor(Math.random() * 800),
connectionsActive: Math.floor(Math.random() * 100)
}
};
// Calculate derived values
metrics.memory.free = metrics.memory.total - metrics.memory.used;
metrics.memory.usage = (metrics.memory.used / metrics.memory.total) * 100;
metricsHistory.push(metrics);
}
expect(metricsHistory).toHaveLength(5);
expect(metricsHistory[0]!.timestamp).not.toEqual(metricsHistory[4]!.timestamp);
// Verify all metrics have reasonable values
metricsHistory.forEach(metrics => {
expect(metrics.cpu.usage).toBeGreaterThanOrEqual(20);
expect(metrics.cpu.usage).toBeLessThanOrEqual(80);
expect(metrics.memory.usage).toBeGreaterThan(0);
expect(metrics.memory.usage).toBeLessThan(100);
});
});
});
describe('Performance Profiling', () => {
it('should support operation profiling', async () => {
const operations = ['indexing', 'searching', 'serving'];
const profiles: Record<string, { id: string; startTime: number; endTime?: number }> = {};
// Start profiling for each operation
for (const operation of operations) {
const profileId = `profile-${operation}-${Date.now()}`;
profiles[operation] = {
id: profileId,
startTime: performance.now()
};
}
// Simulate some work
await TestUtils.wait(10);
// Stop profiling
for (const operation of operations) {
profiles[operation]!.endTime = performance.now();
}
// Verify all profiles were created and completed
operations.forEach(operation => {
const profile = profiles[operation];
expect(profile!.id).toMatch(new RegExp(`profile-${operation}-\\d+`));
expect(profile!.startTime).toBeGreaterThan(0);
expect(profile!.endTime).toBeGreaterThan(profile!.startTime);
});
});
});
});