feat: SDD workflow — test sync, song generation + validation, ReaScript hybrid pipeline
- compose-test-sync: fix 3 failing tests (NOTE_TO_MIDI, DrumLoopAnalyzer mock, section name) - generate-song: CLI wrapper + RPP validator (6 structural checks) + 4 e2e tests - reascript-hybrid: ReaScriptGenerator + command protocol + CLI + 16 unit tests - 110/110 tests passing - Full SDD cycle (propose→spec→design→tasks→apply→verify) for all 3 changes
This commit is contained in:
97
tests/test_generate_song.py
Normal file
97
tests/test_generate_song.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Tests for scripts/generate.py — E2E song generation and RPP validator."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parents[1]))
|
||||
|
||||
|
||||
class TestGenerateCLI:
|
||||
"""Smoke and integration tests for the generate.py CLI."""
|
||||
|
||||
def test_generate_cli_smoke(self, tmp_path):
|
||||
"""CLI produces a non-empty .rpp file at the expected path."""
|
||||
output = tmp_path / "song.rpp"
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/generate.py",
|
||||
"--bpm", "95",
|
||||
"--key", "Am",
|
||||
"--output", str(output),
|
||||
"--seed", "42",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
assert result.returncode == 0, f"stderr: {result.stderr}"
|
||||
assert output.exists(), f"File not created: {output}"
|
||||
assert output.stat().st_size > 0, "File is empty"
|
||||
|
||||
def test_validate_passes_for_valid_output(self, tmp_path):
|
||||
"""With --validate, CLI returns 0 when validator sees no errors."""
|
||||
output = tmp_path / "song.rpp"
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/generate.py",
|
||||
"--bpm", "95",
|
||||
"--key", "Am",
|
||||
"--output", str(output),
|
||||
"--seed", "42",
|
||||
"--validate",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
assert result.returncode == 0, f"stderr: {result.stderr}\nstdout: {result.stdout}"
|
||||
|
||||
def test_validate_detects_track_count_violation(self, tmp_path):
|
||||
"""Validator flags a project with fewer than 9 tracks."""
|
||||
from src.validator.rpp_validator import validate_rpp_output
|
||||
|
||||
rpp_path = tmp_path / "bad.rpp"
|
||||
# Write a minimal .rpp with only 5 <TRACK> blocks
|
||||
content = (
|
||||
"<REAPER_PROJECT 0.1 \"7.65/win64\" 0 0\n"
|
||||
+ " <TRACK\n NAME \"t1\"\n>\n"
|
||||
+ " <TRACK\n NAME \"t2\"\n>\n"
|
||||
+ " <TRACK\n NAME \"t3\"\n>\n"
|
||||
+ " <TRACK\n NAME \"t4\"\n>\n"
|
||||
+ " <TRACK\n NAME \"t5\"\n>\n"
|
||||
+ ">"
|
||||
)
|
||||
rpp_path.write_text(content, encoding="utf-8")
|
||||
errors = validate_rpp_output(str(rpp_path))
|
||||
assert any("Expected 9" in e for e in errors), f"Got: {errors}"
|
||||
|
||||
def test_reproducibility_same_seed(self, tmp_path):
|
||||
"""Two runs with the same seed produce byte-identical output."""
|
||||
output_a = tmp_path / "song_a.rpp"
|
||||
output_b = tmp_path / "song_b.rpp"
|
||||
for out_path in (output_a, output_b):
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/generate.py",
|
||||
"--bpm", "95",
|
||||
"--key", "Am",
|
||||
"--output", str(out_path),
|
||||
"--seed", "42",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
assert result.returncode == 0, f"stderr: {result.stderr}"
|
||||
|
||||
assert output_a.read_bytes() == output_b.read_bytes(), (
|
||||
"Outputs differ — seed does not guarantee reproducibility"
|
||||
)
|
||||
Reference in New Issue
Block a user