git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
431 lines
13 KiB
TypeScript
431 lines
13 KiB
TypeScript
/**
|
|
* Tests for Self-Learning Generator
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { SelfLearningGenerator } from '../../src/generators/self-learning.js';
|
|
import type { SelfLearningConfig, GenerateOptions } from '../../src/generators/self-learning.js';
|
|
|
|
describe('SelfLearningGenerator', () => {
|
|
let config: SelfLearningConfig;
|
|
|
|
beforeEach(() => {
|
|
config = {
|
|
task: 'code-generation',
|
|
learningRate: 0.1,
|
|
iterations: 5
|
|
};
|
|
});
|
|
|
|
describe('Initialization', () => {
|
|
it('should create generator with valid config', () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
expect(generator).toBeDefined();
|
|
});
|
|
|
|
it('should accept quality threshold', () => {
|
|
const generatorWithThreshold = new SelfLearningGenerator({
|
|
...config,
|
|
qualityThreshold: 0.9
|
|
});
|
|
expect(generatorWithThreshold).toBeDefined();
|
|
});
|
|
|
|
it('should accept maxAttempts option', () => {
|
|
const generatorWithMax = new SelfLearningGenerator({
|
|
...config,
|
|
maxAttempts: 20
|
|
});
|
|
expect(generatorWithMax).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Generation and Learning', () => {
|
|
it('should generate output with quality improvement', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Generate a function to validate emails'
|
|
});
|
|
|
|
expect(result.output).toBeDefined();
|
|
expect(result.finalQuality).toBeGreaterThan(0);
|
|
expect(result.finalQuality).toBeLessThanOrEqual(1);
|
|
expect(result.improvement).toBeGreaterThanOrEqual(0);
|
|
expect(result.iterations).toBe(5);
|
|
expect(result.metrics).toHaveLength(5);
|
|
});
|
|
|
|
it('should show quality improvement over iterations', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Test improvement tracking'
|
|
});
|
|
|
|
const firstQuality = result.metrics[0].quality;
|
|
const lastQuality = result.metrics[result.metrics.length - 1].quality;
|
|
|
|
// Quality should generally improve (or at least not decrease significantly)
|
|
expect(lastQuality).toBeGreaterThanOrEqual(firstQuality * 0.95);
|
|
expect(result.improvement).toBeDefined();
|
|
});
|
|
|
|
it('should track metrics for each iteration', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Track iteration metrics'
|
|
});
|
|
|
|
expect(result.metrics).toHaveLength(5);
|
|
result.metrics.forEach((metric, index) => {
|
|
expect(metric.iteration).toBe(index + 1);
|
|
expect(metric.quality).toBeGreaterThan(0);
|
|
expect(typeof metric.improvement).toBe('number');
|
|
expect(Array.isArray(metric.feedback)).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should apply learning rate correctly', async () => {
|
|
const highLearningRate = new SelfLearningGenerator({
|
|
...config,
|
|
learningRate: 0.5,
|
|
iterations: 3
|
|
});
|
|
const lowLearningRate = new SelfLearningGenerator({
|
|
...config,
|
|
learningRate: 0.05,
|
|
iterations: 3
|
|
});
|
|
|
|
const highResult = await highLearningRate.generate({
|
|
prompt: 'Test high learning rate'
|
|
});
|
|
const lowResult = await lowLearningRate.generate({
|
|
prompt: 'Test low learning rate'
|
|
});
|
|
|
|
// Higher learning rate should generally lead to faster improvement
|
|
expect(highResult.improvement).toBeDefined();
|
|
expect(lowResult.improvement).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Test Integration', () => {
|
|
it('should evaluate against test cases', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const tests = [
|
|
(output: any) => output.content.length > 10,
|
|
(output: any) => output.quality > 0.5,
|
|
(output: any) => output.metadata !== undefined
|
|
];
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Generate with tests',
|
|
tests
|
|
});
|
|
|
|
expect(result.finalQuality).toBeGreaterThan(0);
|
|
result.metrics.forEach(metric => {
|
|
expect(metric.testsPassingRate).toBeDefined();
|
|
expect(metric.testsPassingRate).toBeGreaterThanOrEqual(0);
|
|
expect(metric.testsPassingRate).toBeLessThanOrEqual(1);
|
|
});
|
|
});
|
|
|
|
it('should track test passing rate', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const tests = [
|
|
(output: any) => output.quality > 0.6,
|
|
(output: any) => output.quality > 0.7
|
|
];
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Track test pass rate',
|
|
tests
|
|
});
|
|
|
|
// Test passing rate should be tracked for each iteration
|
|
result.metrics.forEach(metric => {
|
|
expect(metric.testsPassingRate).toBeGreaterThanOrEqual(0);
|
|
expect(metric.testsPassingRate).toBeLessThanOrEqual(1);
|
|
});
|
|
});
|
|
|
|
it('should handle failing tests gracefully', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const impossibleTests = [
|
|
() => false, // Always fails
|
|
() => false
|
|
];
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Handle test failures',
|
|
tests: impossibleTests
|
|
});
|
|
|
|
expect(result.output).toBeDefined();
|
|
expect(result.finalQuality).toBeGreaterThan(0);
|
|
// Should complete despite test failures
|
|
});
|
|
});
|
|
|
|
describe('Event Emissions', () => {
|
|
it('should emit start event', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
let startEmitted = false;
|
|
|
|
generator.on('start', (data) => {
|
|
startEmitted = true;
|
|
expect(data.task).toBe('code-generation');
|
|
expect(data.iterations).toBe(5);
|
|
});
|
|
|
|
await generator.generate({ prompt: 'Test start event' });
|
|
expect(startEmitted).toBe(true);
|
|
});
|
|
|
|
it('should emit improvement events', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const improvements: any[] = [];
|
|
|
|
generator.on('improvement', (metrics) => {
|
|
improvements.push(metrics);
|
|
});
|
|
|
|
await generator.generate({ prompt: 'Test improvement events' });
|
|
|
|
expect(improvements).toHaveLength(5);
|
|
improvements.forEach(metric => {
|
|
expect(metric.iteration).toBeDefined();
|
|
expect(metric.quality).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it('should emit complete event', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
let completeData: any = null;
|
|
|
|
generator.on('complete', (data) => {
|
|
completeData = data;
|
|
});
|
|
|
|
await generator.generate({ prompt: 'Test complete event' });
|
|
|
|
expect(completeData).toBeDefined();
|
|
expect(completeData.finalQuality).toBeDefined();
|
|
expect(completeData.improvement).toBeDefined();
|
|
expect(completeData.iterations).toBe(5);
|
|
});
|
|
|
|
it('should emit threshold-reached event', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
qualityThreshold: 0.6,
|
|
iterations: 10
|
|
});
|
|
let thresholdReached = false;
|
|
|
|
generator.on('threshold-reached', (data) => {
|
|
thresholdReached = true;
|
|
expect(data.quality).toBeGreaterThanOrEqual(0.6);
|
|
});
|
|
|
|
await generator.generate({ prompt: 'Test threshold' });
|
|
// Threshold might or might not be reached depending on random variation
|
|
});
|
|
});
|
|
|
|
describe('Quality Thresholds', () => {
|
|
it('should stop when quality threshold is reached', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
qualityThreshold: 0.7,
|
|
iterations: 10
|
|
});
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Test early stopping'
|
|
});
|
|
|
|
// Should stop before completing all iterations if threshold reached
|
|
expect(result.iterations).toBeLessThanOrEqual(10);
|
|
if (result.finalQuality >= 0.7) {
|
|
expect(result.iterations).toBeLessThan(10);
|
|
}
|
|
});
|
|
|
|
it('should use initial quality if provided', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Test initial quality',
|
|
initialQuality: 0.8
|
|
});
|
|
|
|
expect(result.output).toBeDefined();
|
|
// Improvement calculation should be based on initial quality
|
|
});
|
|
});
|
|
|
|
describe('History Tracking', () => {
|
|
it('should maintain learning history', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
await generator.generate({ prompt: 'First generation' });
|
|
|
|
const history = generator.getHistory();
|
|
expect(history).toHaveLength(5);
|
|
expect(history[0].iteration).toBe(1);
|
|
expect(history[4].iteration).toBe(5);
|
|
});
|
|
|
|
it('should accumulate history across multiple generations', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
await generator.generate({ prompt: 'First' });
|
|
await generator.generate({ prompt: 'Second' });
|
|
|
|
const history = generator.getHistory();
|
|
expect(history.length).toBe(10); // 5 + 5 iterations
|
|
});
|
|
|
|
it('should reset history when reset is called', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
await generator.generate({ prompt: 'Generate before reset' });
|
|
|
|
expect(generator.getHistory().length).toBe(5);
|
|
|
|
generator.reset();
|
|
|
|
expect(generator.getHistory()).toHaveLength(0);
|
|
});
|
|
|
|
it('should emit reset event', () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
let resetEmitted = false;
|
|
|
|
generator.on('reset', () => {
|
|
resetEmitted = true;
|
|
});
|
|
|
|
generator.reset();
|
|
expect(resetEmitted).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Feedback Generation', () => {
|
|
it('should generate relevant feedback', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Test feedback generation'
|
|
});
|
|
|
|
result.metrics.forEach(metric => {
|
|
expect(Array.isArray(metric.feedback)).toBe(true);
|
|
expect(metric.feedback.length).toBeGreaterThan(0);
|
|
metric.feedback.forEach(fb => {
|
|
expect(typeof fb).toBe('string');
|
|
expect(fb.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should provide contextual feedback based on quality', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const result = await generator.generate({
|
|
prompt: 'Test contextual feedback'
|
|
});
|
|
|
|
// Feedback should vary based on performance
|
|
const feedbackTypes = new Set(
|
|
result.metrics.flatMap(m => m.feedback)
|
|
);
|
|
expect(feedbackTypes.size).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle zero iterations', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
iterations: 0
|
|
});
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Test zero iterations'
|
|
});
|
|
|
|
expect(result.output).toBeNull();
|
|
expect(result.metrics).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle very high learning rate', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
learningRate: 1.0
|
|
});
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Test high learning rate'
|
|
});
|
|
|
|
expect(result.output).toBeDefined();
|
|
expect(result.finalQuality).toBeLessThanOrEqual(1.0);
|
|
});
|
|
|
|
it('should handle very low learning rate', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
learningRate: 0.001
|
|
});
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Test low learning rate'
|
|
});
|
|
|
|
expect(result.output).toBeDefined();
|
|
// Improvement should be minimal but positive
|
|
});
|
|
|
|
it('should handle single iteration', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
iterations: 1
|
|
});
|
|
|
|
const result = await generator.generate({
|
|
prompt: 'Single iteration test'
|
|
});
|
|
|
|
expect(result.iterations).toBe(1);
|
|
expect(result.metrics).toHaveLength(1);
|
|
expect(result.output).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Performance', () => {
|
|
it('should complete within reasonable time', async () => {
|
|
const generator = new SelfLearningGenerator(config);
|
|
const startTime = Date.now();
|
|
|
|
await generator.generate({
|
|
prompt: 'Performance test'
|
|
});
|
|
|
|
const duration = Date.now() - startTime;
|
|
expect(duration).toBeLessThan(2000); // Should complete in under 2 seconds
|
|
});
|
|
|
|
it('should handle many iterations efficiently', async () => {
|
|
const generator = new SelfLearningGenerator({
|
|
...config,
|
|
iterations: 20
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
await generator.generate({
|
|
prompt: 'Many iterations test'
|
|
});
|
|
const duration = Date.now() - startTime;
|
|
|
|
expect(duration).toBeLessThan(5000); // Even with 20 iterations
|
|
});
|
|
});
|
|
});
|