package ding import ( "bytes" "encoding/binary" "testing" ) func TestRender_ProducesWAVHeader(t *testing.T) { data := Render(DefaultOptions()) if len(data) < 44 { t.Fatalf("expected at least 44 bytes (WAV header), got %d", len(data)) } if !bytes.HasPrefix(data, []byte("RIFF")) { t.Errorf("expected RIFF prefix") } if !bytes.Equal(data[8:12], []byte("WAVE")) { t.Errorf("expected WAVE format marker") } if !bytes.Equal(data[12:16], []byte("fmt ")) { t.Errorf("expected fmt chunk") } // PCM format if pcm := binary.LittleEndian.Uint16(data[20:22]); pcm != 1 { t.Errorf("expected PCM (1), got %d", pcm) } // Channels if ch := binary.LittleEndian.Uint16(data[22:24]); ch != 2 { t.Errorf("expected stereo (2), got %d", ch) } // Sample rate if sr := binary.LittleEndian.Uint32(data[24:28]); sr != 22050 { t.Errorf("expected default sample rate 22050, got %d", sr) } // Bits per sample if bps := binary.LittleEndian.Uint16(data[34:36]); bps != 16 { t.Errorf("expected 16 bits/sample, got %d", bps) } } func TestRender_DefaultSizeApproximately229KB(t *testing.T) { data := Render(DefaultOptions()) // Default: 3 repetitions of 0.6 s + 2 gaps of 0.4 s = 2.6 s total. // 22050 Hz * 2 ch * 2 bytes * 2.6 s ≈ 229320 data bytes + 44 byte header. const wantData = 22050 * 2 * 2 * 260 / 100 // 2.6 seconds, integer math if got := len(data); got < wantData || got > wantData+500 { t.Errorf("expected ~%d bytes, got %d", wantData, got) } } func TestRender_OverridePitchAffectsContent(t *testing.T) { a := Render(DefaultOptions()) b := Render(Options{PitchHigh: 1200}.WithDefaults()) if bytes.Equal(a, b) { t.Errorf("expected different content for different PitchHigh values") } // Same length (envelope doesn't change). if len(a) != len(b) { t.Errorf("expected same byte length: %d vs %d", len(a), len(b)) } } func TestRender_OverrideSampleRateChangesByteRate(t *testing.T) { data := Render(Options{SampleRate: 44100}.WithDefaults()) if sr := binary.LittleEndian.Uint32(data[24:28]); sr != 44100 { t.Errorf("expected 44100, got %d", sr) } } func TestSafeSampleRate_ClampsOutOfRange(t *testing.T) { cases := []struct { in int want uint32 }{ {22050, 22050}, {44100, 44100}, {192000, 192000}, {0, 22050}, // zero → default {-1, 22050}, // negative → default {200000, 22050}, // above max → default {1 << 31, 22050}, // way beyond uint32 → default (the original CodeQL concern) {1 << 33, 22050}, // wraps to a different value on int→uint32; default protects us {int(maxSampleRate) + 1, 22050}, } for _, c := range cases { if got := safeSampleRate(c.in); got != c.want { t.Errorf("safeSampleRate(%d) = %d, want %d", c.in, got, c.want) } } } func TestRender_HugeSampleRateDoesNotTruncateOrPanic(t *testing.T) { // Regression for the int→uint32 truncation CodeQL flagged. // A caller (test, future SDK user) bypassing the handler's // sampleRateParam guard with an int well above uint32 used // to silently wrap. The defensive clamp now substitutes the // default sample rate instead. data := Render(Options{SampleRate: 1 << 33}.WithDefaults()) if sr := binary.LittleEndian.Uint32(data[24:28]); sr != uint32(DefaultOptions().SampleRate) { t.Errorf("expected clamp to default sample rate, got %d", sr) } } func TestRender_RepeatProducesLongerAudio(t *testing.T) { once := Render(Options{Repeat: 1}.WithDefaults()) thrice := Render(Options{Repeat: 3}.WithDefaults()) if len(thrice) <= len(once) { t.Errorf("expected Repeat:3 to produce more bytes than Repeat:1: %d vs %d", len(thrice), len(once)) } } func TestWithDefaults_FillsZeroFields(t *testing.T) { got := Options{PitchHigh: 1000}.WithDefaults() if got.PitchHigh != 1000 { t.Errorf("override should be preserved, got %f", got.PitchHigh) } if got.SampleRate != 22050 { t.Errorf("expected default SampleRate, got %d", got.SampleRate) } if got.PitchMid <= 0 { t.Errorf("expected PitchMid filled from default, got %f", got.PitchMid) } } func TestWithDefaults_ClampsInvalidPeak(t *testing.T) { got := Options{Peak: 2.0}.WithDefaults() if got.Peak != DefaultOptions().Peak { t.Errorf("expected default Peak for invalid input, got %f", got.Peak) } got = Options{Peak: -0.5}.WithDefaults() if got.Peak != DefaultOptions().Peak { t.Errorf("expected default Peak for negative input, got %f", got.Peak) } } func TestRender_SamplesDontClip(t *testing.T) { data := Render(DefaultOptions()) // Walk every sample, ensure no value is at the 16-bit extreme // (which would indicate clipping). Header is 44 bytes. for i := 44; i+1 < len(data); i += 2 { s := int16(binary.LittleEndian.Uint16(data[i : i+2])) if s == 32767 || s == -32768 { t.Errorf("sample at offset %d hit clipping (%d)", i, s) return } } }