All checks were successful
ci/woodpecker/tag/release Pipeline was successful
280 lines
8.9 KiB
Go
280 lines
8.9 KiB
Go
package normalize
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseReaderObjectWithSegmentsParses(t *testing.T) {
|
|
input := `{
|
|
"segments": [
|
|
{"start": 1.0, "end": 2.0, "speaker": " Alice ", "text": "hello", "id": 100}
|
|
]
|
|
}`
|
|
|
|
parsed, err := ParseReader(strings.NewReader(input))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Shape != ShapeObjectWithSegments {
|
|
t.Fatalf("shape = %q, want %q", parsed.Shape, ShapeObjectWithSegments)
|
|
}
|
|
if len(parsed.Segments) != 1 {
|
|
t.Fatalf("segment count = %d, want 1", len(parsed.Segments))
|
|
}
|
|
segment := parsed.Segments[0]
|
|
if segment.Speaker != "Alice" {
|
|
t.Fatalf("speaker = %q, want %q", segment.Speaker, "Alice")
|
|
}
|
|
if segment.OriginalID == nil || *segment.OriginalID != 100 {
|
|
t.Fatalf("original id = %v, want 100", segment.OriginalID)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderBareSegmentArrayParses(t *testing.T) {
|
|
input := `[
|
|
{"start": 1.0, "end": 2.0, "speaker": "Alice", "text": "hello"},
|
|
{"start": 3.0, "end": 4.0, "speaker": "Bob", "text": "world"}
|
|
]`
|
|
|
|
parsed, err := ParseReader(strings.NewReader(input))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Shape != ShapeBareSegmentsArray {
|
|
t.Fatalf("shape = %q, want %q", parsed.Shape, ShapeBareSegmentsArray)
|
|
}
|
|
if len(parsed.Segments) != 2 {
|
|
t.Fatalf("segment count = %d, want 2", len(parsed.Segments))
|
|
}
|
|
}
|
|
|
|
func TestParseReaderInvalidJSONFails(t *testing.T) {
|
|
_, err := ParseReader(strings.NewReader(`{"segments":`))
|
|
if err == nil {
|
|
t.Fatal("expected parse error")
|
|
}
|
|
if !strings.Contains(err.Error(), "decode normalize input JSON") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderObjectMissingSegmentsFails(t *testing.T) {
|
|
_, err := ParseReader(strings.NewReader(`{"items":[]}`))
|
|
if err == nil {
|
|
t.Fatal("expected missing segments error")
|
|
}
|
|
if !strings.Contains(err.Error(), "must contain a \"segments\" field") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderSegmentsNotArrayFails(t *testing.T) {
|
|
_, err := ParseReader(strings.NewReader(`{"segments": {}}`))
|
|
if err == nil {
|
|
t.Fatal("expected segments not array error")
|
|
}
|
|
if !strings.Contains(err.Error(), "\"segments\" must be an array") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderTopLevelScalarShapesFail(t *testing.T) {
|
|
tests := []string{`"text"`, `42`, `null`, `true`}
|
|
for _, input := range tests {
|
|
_, err := ParseReader(strings.NewReader(input))
|
|
if err == nil {
|
|
t.Fatalf("expected top-level shape error for %s", input)
|
|
}
|
|
if !strings.Contains(err.Error(), "top-level object") {
|
|
t.Fatalf("unexpected error for %s: %v", input, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingStartUsesEndValue(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"end":2,"speaker":"A","text":"t"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 1 {
|
|
t.Fatalf("segment count = %d, want 1", len(parsed.Segments))
|
|
}
|
|
if parsed.Segments[0].Start != 2 || parsed.Segments[0].End != 2 {
|
|
t.Fatalf("segment timing = %v..%v, want 2..2", parsed.Segments[0].Start, parsed.Segments[0].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingEndUsesStartValue(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":1,"speaker":"A","text":"t"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 1 {
|
|
t.Fatalf("segment count = %d, want 1", len(parsed.Segments))
|
|
}
|
|
if parsed.Segments[0].Start != 1 || parsed.Segments[0].End != 1 {
|
|
t.Fatalf("segment timing = %v..%v, want 1..1", parsed.Segments[0].Start, parsed.Segments[0].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingSpeakerUsesUnknownPlaceholder(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":1,"end":2,"text":"t"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Segments[0].Speaker != "Unknown_Speaker" {
|
|
t.Fatalf("speaker = %q, want Unknown_Speaker", parsed.Segments[0].Speaker)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderEmptySpeakerUsesUnknownPlaceholder(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":1,"end":2,"speaker":" ","text":"t"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Segments[0].Speaker != "Unknown_Speaker" {
|
|
t.Fatalf("speaker = %q, want Unknown_Speaker", parsed.Segments[0].Speaker)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingTextDropsSegment(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":1,"end":2,"speaker":"A"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 0 {
|
|
t.Fatalf("segment count = %d, want 0", len(parsed.Segments))
|
|
}
|
|
}
|
|
|
|
func TestParseReaderEndBeforeStartSwapsValues(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":3,"end":2,"speaker":"A","text":"t"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Segments[0].Start != 2 || parsed.Segments[0].End != 3 {
|
|
t.Fatalf("segment timing = %v..%v, want 2..3", parsed.Segments[0].Start, parsed.Segments[0].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderNegativeStartFails(t *testing.T) {
|
|
_, err := ParseReader(strings.NewReader(`[{"start":-1,"end":2,"speaker":"A","text":"t"}]`))
|
|
assertContains(t, err, "start must be >= 0")
|
|
}
|
|
|
|
func TestParseReaderEmptySegmentsArrayAccepted(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`{"segments":[]}`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 0 {
|
|
t.Fatalf("segment count = %d, want 0", len(parsed.Segments))
|
|
}
|
|
}
|
|
|
|
func TestParseReaderCategoriesPreservedWhenValid(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"start":1,"end":2,"speaker":"A","text":"t","categories":["filler","backchannel"]}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 1 {
|
|
t.Fatalf("segment count = %d, want 1", len(parsed.Segments))
|
|
}
|
|
if len(parsed.Segments[0].Categories) != 2 {
|
|
t.Fatalf("categories length = %d, want 2", len(parsed.Segments[0].Categories))
|
|
}
|
|
if parsed.Segments[0].Categories[0] != "filler" || parsed.Segments[0].Categories[1] != "backchannel" {
|
|
t.Fatalf("categories = %v", parsed.Segments[0].Categories)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingBothTimesMiddleUsesNeighborMidpoint(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[
|
|
{"start":1,"end":2,"speaker":"A","text":"left"},
|
|
{"speaker":"B","text":"middle"},
|
|
{"start":6,"end":7,"speaker":"C","text":"right"}
|
|
]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Segments[1].Start != 4 || parsed.Segments[1].End != 4 {
|
|
t.Fatalf("middle timing = %v..%v, want 4..4", parsed.Segments[1].Start, parsed.Segments[1].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingBothTimesEdgeFallbacks(t *testing.T) {
|
|
parsedFirst, err := ParseReader(strings.NewReader(`[
|
|
{"speaker":"A","text":"first"},
|
|
{"start":5,"end":6,"speaker":"B","text":"second"}
|
|
]`))
|
|
if err != nil {
|
|
t.Fatalf("parse first failed: %v", err)
|
|
}
|
|
if parsedFirst.Segments[0].Start != 5 || parsedFirst.Segments[0].End != 5 {
|
|
t.Fatalf("first timing = %v..%v, want 5..5", parsedFirst.Segments[0].Start, parsedFirst.Segments[0].End)
|
|
}
|
|
|
|
parsedLast, err := ParseReader(strings.NewReader(`[
|
|
{"start":1,"end":2,"speaker":"A","text":"first"},
|
|
{"speaker":"B","text":"last"}
|
|
]`))
|
|
if err != nil {
|
|
t.Fatalf("parse last failed: %v", err)
|
|
}
|
|
if parsedLast.Segments[1].Start != 2 || parsedLast.Segments[1].End != 2 {
|
|
t.Fatalf("last timing = %v..%v, want 2..2", parsedLast.Segments[1].Start, parsedLast.Segments[1].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderMissingBothTimesSingleSegmentUsesZero(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[{"speaker":"A","text":"only"}]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if parsed.Segments[0].Start != 0 || parsed.Segments[0].End != 0 {
|
|
t.Fatalf("timing = %v..%v, want 0..0", parsed.Segments[0].Start, parsed.Segments[0].End)
|
|
}
|
|
}
|
|
|
|
func TestParseReaderEmptyOrWhitespaceTextDropped(t *testing.T) {
|
|
parsed, err := ParseReader(strings.NewReader(`[
|
|
{"start":1,"end":2,"speaker":"A","text":"ok"},
|
|
{"start":2,"end":3,"speaker":"A","text":""},
|
|
{"start":3,"end":4,"speaker":"A","text":" "}
|
|
]`))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if len(parsed.Segments) != 1 {
|
|
t.Fatalf("segment count = %d, want 1", len(parsed.Segments))
|
|
}
|
|
}
|
|
|
|
func TestParseReaderOriginalInputIndexPreserved(t *testing.T) {
|
|
input := `[
|
|
{"start":1,"end":2,"speaker":"A","text":"one"},
|
|
{"start":2,"end":3,"speaker":"B","text":"two"},
|
|
{"start":3,"end":4,"speaker":"C","text":"three"}
|
|
]`
|
|
parsed, err := ParseReader(strings.NewReader(input))
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
for index, segment := range parsed.Segments {
|
|
if segment.InputIndex != index {
|
|
t.Fatalf("segment %d input index = %d, want %d", index, segment.InputIndex, index)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertContains(t *testing.T, err error, fragment string) {
|
|
t.Helper()
|
|
if err == nil {
|
|
t.Fatalf("expected error containing %q", fragment)
|
|
}
|
|
if !strings.Contains(err.Error(), fragment) {
|
|
t.Fatalf("error = %q, want substring %q", err.Error(), fragment)
|
|
}
|
|
}
|