Skip to main content
Glama
PatternGenerator.test.ts14.3 kB
import { PatternGenerator } from '../services/PatternGenerator'; describe('PatternGenerator', () => { let generator: PatternGenerator; beforeEach(() => { generator = new PatternGenerator(); }); describe('generateDrumPattern', () => { test('should generate techno drum pattern', () => { const pattern = generator.generateDrumPattern('techno', 0.5); expect(pattern).toContain('s('); expect(pattern).toContain('bd'); }); test('should generate house drum pattern', () => { const pattern = generator.generateDrumPattern('house', 0.5); expect(pattern).toContain('s('); expect(pattern).toContain('hh'); }); test('should handle complexity levels', () => { const simple = generator.generateDrumPattern('techno', 0.0); const complex = generator.generateDrumPattern('techno', 1.0); expect(simple).toBeTruthy(); expect(complex).toBeTruthy(); expect(simple.length).toBeLessThanOrEqual(complex.length); }); test('should generate all supported drum styles', () => { const styles = ['techno', 'house', 'dnb', 'breakbeat', 'trap', 'jungle', 'ambient', 'experimental']; styles.forEach(style => { const pattern = generator.generateDrumPattern(style, 0.5); expect(pattern).toBeTruthy(); expect(pattern).toContain('s('); }); }); test('should default to techno for unknown styles', () => { const pattern = generator.generateDrumPattern('unknown', 0.5); expect(pattern).toBeTruthy(); expect(pattern).toContain('bd'); }); test('should handle complexity edge cases', () => { expect(generator.generateDrumPattern('techno', 0)).toBeTruthy(); expect(generator.generateDrumPattern('techno', 1)).toBeTruthy(); expect(generator.generateDrumPattern('techno', 0.5)).toBeTruthy(); }); }); describe('generateBassline', () => { test('should generate techno bassline', () => { const bass = generator.generateBassline('C', 'techno'); expect(bass).toContain('note('); expect(bass).toContain('C2'); expect(bass).toContain('sawtooth'); }); test('should generate house bassline', () => { const bass = generator.generateBassline('D', 'house'); expect(bass).toContain('D2'); expect(bass).toContain('sine'); }); test('should generate acid bassline with modulation', () => { const bass = generator.generateBassline('E', 'acid'); expect(bass).toContain('E2'); expect(bass).toContain('cutoff'); expect(bass).toContain('sine.range'); }); test('should handle all supported bass styles', () => { const styles = ['techno', 'house', 'dnb', 'acid', 'dub', 'funk', 'jazz', 'ambient']; styles.forEach(style => { const bass = generator.generateBassline('C', style); expect(bass).toContain('note('); expect(bass).toContain('C'); }); }); test('should work with different keys', () => { const keys = ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C#', 'F#']; keys.forEach(key => { const bass = generator.generateBassline(key, 'techno'); expect(bass).toContain(key); }); }); test('should default to techno for unknown styles', () => { const bass = generator.generateBassline('C', 'unknown'); expect(bass).toContain('sawtooth'); }); }); describe('generateMelody', () => { test('should generate melody from scale', () => { const scale = ['c', 'd', 'e', 'f', 'g', 'a', 'b']; const melody = generator.generateMelody(scale, 8); expect(melody).toContain('note('); expect(melody).toContain('triangle'); }); test('should generate melody with specified length', () => { const scale = ['c', 'd', 'e']; const melody4 = generator.generateMelody(scale, 4); const melody8 = generator.generateMelody(scale, 8); const melody16 = generator.generateMelody(scale, 16); const notes4 = melody4.match(/[a-g]\d+/g); const notes8 = melody8.match(/[a-g]\d+/g); const notes16 = melody16.match(/[a-g]\d+/g); expect(notes4?.length).toBe(4); expect(notes8?.length).toBe(8); expect(notes16?.length).toBe(16); }); test('should respect octave range', () => { const scale = ['c', 'd', 'e', 'f', 'g']; const melody = generator.generateMelody(scale, 8, [4, 5]); const notes = melody.match(/[a-g](\d+)/g) || []; notes.forEach(note => { const octave = parseInt(note.slice(-1)); expect(octave).toBeGreaterThanOrEqual(4); expect(octave).toBeLessThanOrEqual(5); }); }); test('should use default parameters', () => { const scale = ['c', 'd', 'e']; const melody = generator.generateMelody(scale); expect(melody).toBeTruthy(); expect(melody).toContain('note('); }); test('should only use notes from provided scale', () => { const scale = ['c', 'e', 'g']; const melody = generator.generateMelody(scale, 8); const notePattern = /([a-g]#?)\d+/g; const matches = melody.match(notePattern) || []; matches.forEach(match => { const note = match.replace(/\d+/g, ''); expect(scale).toContain(note); }); }); }); describe('generateChords', () => { test('should generate triad voicing', () => { const chords = generator.generateChords('"C" "F" "G"', 'triad'); expect(chords).toContain('note('); expect(chords).toContain('sawtooth'); expect(chords).toContain('.struct("1 ~ ~ ~")'); }); test('should generate sustained voicing', () => { const chords = generator.generateChords('"C" "F"', 'sustained'); expect(chords).toContain('.attack(0.5)'); expect(chords).toContain('.release(2)'); }); test('should generate pad voicing', () => { const chords = generator.generateChords('"C"', 'pad'); expect(chords).toContain('.attack(2)'); expect(chords).toContain('.release(4)'); expect(chords).toContain('.room(0.8)'); }); test('should default to triad for unknown voicing', () => { const chords = generator.generateChords('"C"', 'unknown'); expect(chords).toContain('.struct("1 ~ ~ ~")'); }); }); describe('generateCompletePattern', () => { test('should generate complete techno pattern', () => { const pattern = generator.generateCompletePattern('techno', 'C', 130); expect(pattern).toContain('// techno'); expect(pattern).toContain('setcpm(130)'); expect(pattern).toContain('stack('); expect(pattern).toContain('// Drums'); expect(pattern).toContain('// Bass'); expect(pattern).toContain('// Chords'); expect(pattern).toContain('// Melody'); }); test('should generate pattern for different styles', () => { const styles = ['techno', 'house', 'jazz', 'ambient']; styles.forEach(style => { const pattern = generator.generateCompletePattern(style, 'C', 120); expect(pattern).toContain(`// ${style}`); expect(pattern).toContain('stack('); }); }); test('should use correct key', () => { const pattern = generator.generateCompletePattern('house', 'D', 120); expect(pattern).toContain('D'); }); test('should set correct BPM', () => { const pattern140 = generator.generateCompletePattern('dnb', 'C', 140); const pattern90 = generator.generateCompletePattern('ambient', 'C', 90); expect(pattern140).toContain('setcpm(140)'); expect(pattern90).toContain('setcpm(90)'); }); test('should use appropriate scale for style', () => { const jazzPattern = generator.generateCompletePattern('jazz', 'C', 120); const technoPattern = generator.generateCompletePattern('techno', 'C', 120); // Jazz should use dorian, others use minor (indirectly verified through note choices) expect(jazzPattern).toBeTruthy(); expect(technoPattern).toBeTruthy(); }); }); describe('generateVariation', () => { test('should add subtle variation', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'subtle'); expect(varied).toContain(base); expect(varied).toContain('.sometimes'); }); test('should add moderate variation', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'moderate'); expect(varied).toContain('.every(4'); expect(varied).toContain('.sometimes'); }); test('should add extreme variation', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'extreme'); expect(varied).toContain('.every(2'); expect(varied).toContain('.jux'); }); test('should add glitch variation', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'glitch'); expect(varied).toContain('.chop'); }); test('should add evolving variation', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'evolving'); expect(varied).toContain('.slow(4)'); expect(varied).toContain('.palindrome'); }); test('should default to subtle for unknown type', () => { const base = 's("bd*4")'; const varied = generator.generateVariation(base, 'unknown'); expect(varied).toContain('.sometimes'); }); }); describe('generateFill', () => { test('should generate techno fill', () => { const fill = generator.generateFill('techno', 1); expect(fill).toContain('s('); expect(fill).toContain('bd'); }); test('should handle different bar lengths', () => { const fill1 = generator.generateFill('house', 1); const fill2 = generator.generateFill('house', 2); expect(fill1).toContain('.fast(1)'); expect(fill2).toContain('.fast(2)'); }); test('should generate fills for all supported styles', () => { const styles = ['techno', 'house', 'dnb', 'trap', 'breakbeat']; styles.forEach(style => { const fill = generator.generateFill(style, 1); expect(fill).toBeTruthy(); }); }); test('should default to techno for unknown style', () => { const fill = generator.generateFill('unknown', 1); expect(fill).toContain('bd'); }); }); describe('generateTransition', () => { test('should generate transition between styles', () => { const transition = generator.generateTransition('techno', 'house', 4); expect(transition).toContain('// Transition from techno to house'); expect(transition).toContain('stack('); expect(transition).toContain('// Fade out techno'); expect(transition).toContain('// Fade in house'); }); test('should use specified bar length', () => { const transition8 = generator.generateTransition('house', 'dnb', 8); expect(transition8).toContain('.slow(8)'); }); }); describe('generateEuclideanPattern', () => { test('should generate Euclidean pattern with default sound', () => { const pattern = generator.generateEuclideanPattern(3, 8); expect(pattern).toContain('s("bd")'); expect(pattern).toContain('.struct('); }); test('should use specified sound', () => { const pattern = generator.generateEuclideanPattern(5, 16, 'cp'); expect(pattern).toContain('s("cp")'); }); test('should generate valid rhythm structure', () => { const pattern = generator.generateEuclideanPattern(4, 12, 'hh'); expect(pattern).toContain('struct'); // Pattern should contain the rhythm from MusicTheory expect(pattern).toMatch(/1.*~/); }); }); describe('generatePolyrhythm', () => { test('should generate polyrhythm with multiple sounds', () => { const sounds = ['bd', 'cp', 'hh']; const patterns = [3, 5, 7]; const poly = generator.generatePolyrhythm(sounds, patterns); expect(poly).toContain('stack('); expect(poly).toContain('s("bd")'); expect(poly).toContain('s("cp")'); expect(poly).toContain('s("hh")'); expect(poly).toContain('.euclid(3, 16)'); expect(poly).toContain('.euclid(5, 16)'); expect(poly).toContain('.euclid(7, 16)'); }); test('should throw error for mismatched arrays', () => { const sounds = ['bd', 'cp']; const patterns = [3, 5, 7]; expect(() => generator.generatePolyrhythm(sounds, patterns)) .toThrow('Number of sounds must match number of patterns'); }); test('should handle single sound/pattern pair', () => { const poly = generator.generatePolyrhythm(['bd'], [4]); expect(poly).toContain('s("bd")'); expect(poly).toContain('.euclid(4, 16)'); }); test('should handle empty arrays', () => { expect(() => generator.generatePolyrhythm([], [])) .not.toThrow(); }); }); describe('integration tests', () => { test('should create complete musical composition', () => { const pattern = generator.generateCompletePattern('house', 'A', 125); const variation = generator.generateVariation(pattern, 'moderate'); expect(pattern).toBeTruthy(); expect(variation).toContain(pattern); }); test('should combine multiple generators', () => { const drums = generator.generateDrumPattern('techno', 0.7); const bass = generator.generateBassline('C', 'techno'); const fill = generator.generateFill('techno', 1); expect(drums).toBeTruthy(); expect(bass).toBeTruthy(); expect(fill).toBeTruthy(); }); test('should handle complex workflow', () => { // Create initial pattern const initial = generator.generateCompletePattern('dnb', 'D', 174); expect(initial).toBeTruthy(); // Add variation const varied = generator.generateVariation(initial, 'glitch'); expect(varied).toContain(initial); // Generate transition const transition = generator.generateTransition('dnb', 'ambient', 8); expect(transition).toBeTruthy(); // Generate final pattern const final = generator.generateCompletePattern('ambient', 'D', 80); expect(final).toBeTruthy(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/williamzujkowski/strudel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server