Files
audita/internal/core/schema/transcript_test.go

298 lines
8.4 KiB
Go

package schema
import (
"os"
"testing"
)
func TestParseSourceTranscriptJSON_BareArray(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_bare_array.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
transcript, err := ParseSourceTranscriptJSON(raw)
if err != nil {
t.Fatalf("ParseSourceTranscriptJSON failed: %v", err)
}
if len(transcript.Segments) != 2 {
t.Fatalf("expected 2 segments, got %d", len(transcript.Segments))
}
s1 := transcript.Segments[0]
if *s1.ID != 1 {
t.Errorf("expected segment[0].id = 1, got %d", *s1.ID)
}
if s1.Speaker != "Alice" {
t.Errorf("expected segment[0].speaker = Alice, got %q", s1.Speaker)
}
if s1.Start != 0.0 {
t.Errorf("expected segment[0].start = 0.0, got %g", s1.Start)
}
if s1.End != 1.5 {
t.Errorf("expected segment[0].end = 1.5, got %g", s1.End)
}
if s1.Text != "Hello world." {
t.Errorf("expected segment[0].text = Hello world., got %q", s1.Text)
}
if s1.Categories != nil {
t.Errorf("expected segment[0].categories = nil, got %v", s1.Categories)
}
s2 := transcript.Segments[1]
if *s2.ID != 2 {
t.Errorf("expected segment[1].id = 2, got %d", *s2.ID)
}
if len(s2.Categories) != 1 || s2.Categories[0] != "greeting" {
t.Errorf("expected segment[1].categories = [greeting], got %v", s2.Categories)
}
}
func TestParseSourceTranscriptJSON_ObjectWithSegments(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_object.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
transcript, err := ParseSourceTranscriptJSON(raw)
if err != nil {
t.Fatalf("ParseSourceTranscriptJSON failed: %v", err)
}
if len(transcript.Segments) != 2 {
t.Fatalf("expected 2 segments, got %d", len(transcript.Segments))
}
if *transcript.Segments[0].ID != 1 {
t.Errorf("expected segment[0].id = 1, got %d", *transcript.Segments[0].ID)
}
}
func TestParseSourceTranscriptJSON_MalformedJSON(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_malformed.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for malformed JSON, got nil")
}
parseErr, ok := err.(*ParseError)
if !ok {
t.Errorf("expected *ParseError, got %T", err)
} else if parseErr.Message != "transcript is not valid JSON" {
t.Errorf("expected 'transcript is not valid JSON', got %q", parseErr.Message)
}
}
func TestParseSourceTranscriptJSON_EmptySpeaker(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_empty_speaker.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for empty speaker, got nil")
}
assertValidationError(t, err, "speaker", "must not be empty")
}
func TestParseSourceTranscriptJSON_EmptyText(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_empty_text.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for empty text, got nil")
}
assertValidationError(t, err, "text", "must not be empty")
}
func TestParseSourceTranscriptJSON_InvalidTimes(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_invalid_times.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for invalid times, got nil")
}
assertValidationErrorContains(t, err, "end", "must be greater than or equal to start")
}
func TestParseSourceTranscriptJSON_DuplicateIDs(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_duplicate_ids.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for duplicate IDs, got nil")
}
assertValidationErrorContains(t, err, "id", "duplicate id")
}
func TestParseSourceTranscriptJSON_EmptyArray(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_empty_array.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for empty array, got nil")
}
parseErr, ok := err.(*ParseError)
if !ok {
t.Errorf("expected *ParseError, got %T", err)
} else if parseErr.Message != "transcript must contain at least one segment" {
t.Errorf("expected 'transcript must contain at least one segment', got %q", parseErr.Message)
}
}
func TestParseTranscriptJSON_NonSequentialID(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_non_sequential_id.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
_, err = ParseTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for non-sequential ID, got nil")
}
assertValidationErrorContains(t, err, "id", "must be sequential")
}
func TestParseTranscriptJSONLenient_AssignsSequentialIDs(t *testing.T) {
raw, err := os.ReadFile("testdata/transcript_no_ids.json")
if err != nil {
t.Fatalf("failed to read test fixture: %v", err)
}
transcript, err := ParseTranscriptJSONLenient(raw)
if err != nil {
t.Fatalf("ParseTranscriptJSONLenient failed: %v", err)
}
if len(transcript.Segments) != 2 {
t.Fatalf("expected 2 segments, got %d", len(transcript.Segments))
}
if transcript.Segments[0].ID != 1 {
t.Errorf("expected segment[0].id = 1, got %d", transcript.Segments[0].ID)
}
if transcript.Segments[1].ID != 2 {
t.Errorf("expected segment[1].id = 2, got %d", transcript.Segments[1].ID)
}
}
func TestParseSourceTranscriptJSON_UnsupportedTopLevelShape(t *testing.T) {
raw := []byte(`"just a string"`)
_, err := ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for unsupported top-level shape, got nil")
}
parseErr, ok := err.(*ParseError)
if !ok {
t.Errorf("expected *ParseError, got %T", err)
} else if parseErr.Message != "transcript must be a JSON array or an object with a segments array" {
t.Errorf("expected unsupported shape message, got %q", parseErr.Message)
}
}
func TestParseSourceTranscriptJSON_ObjectWithoutSegments(t *testing.T) {
raw := []byte(`{"metadata": {"version": "1.0"}}`)
_, err := ParseSourceTranscriptJSON(raw)
if err == nil {
t.Fatal("expected error for object without segments, got nil")
}
parseErr, ok := err.(*ParseError)
if !ok {
t.Errorf("expected *ParseError, got %T", err)
} else if parseErr.Message != "transcript object must contain a segments array" {
t.Errorf("expected 'transcript object must contain a segments array', got %q", parseErr.Message)
}
}
func TestTranscriptToJSON(t *testing.T) {
transcript := &Transcript{
Segments: []Segment{
{ID: 1, Speaker: "Alice", Start: 0.0, End: 1.5, Text: "Hello world."},
{ID: 2, Speaker: "Bob", Start: 2.0, End: 3.5, Text: "Hi there.", Categories: []string{"greeting"}},
},
}
raw, err := TranscriptToJSON(transcript)
if err != nil {
t.Fatalf("TranscriptToJSON failed: %v", err)
}
parsed, err := ParseTranscriptJSON(raw)
if err != nil {
t.Fatalf("failed to parse round-trip JSON: %v", err)
}
if len(parsed.Segments) != 2 {
t.Fatalf("expected 2 segments after round-trip, got %d", len(parsed.Segments))
}
if parsed.Segments[0].Speaker != "Alice" {
t.Errorf("expected segment[0].speaker = Alice, got %q", parsed.Segments[0].Speaker)
}
if len(parsed.Segments[1].Categories) != 1 || parsed.Segments[1].Categories[0] != "greeting" {
t.Errorf("expected segment[1].categories = [greeting], got %v", parsed.Segments[1].Categories)
}
}
func assertValidationError(t *testing.T, err error, fieldContains, messageContains string) {
t.Helper()
valErr, ok := err.(*ValidationError)
if !ok {
t.Errorf("expected *ValidationError, got %T", err)
return
}
if fieldContains != "" && valErr.Field == "" {
t.Errorf("expected field to contain %q, got empty field", fieldContains)
}
if messageContains != "" && valErr.Message == "" {
t.Errorf("expected message to contain %q, got empty message", messageContains)
}
}
func assertValidationErrorContains(t *testing.T, err error, fieldContains, messageContains string) {
t.Helper()
valErr, ok := err.(*ValidationError)
if !ok {
t.Errorf("expected *ValidationError, got %T", err)
return
}
if fieldContains != "" && valErr.Field == "" {
t.Errorf("expected field to contain %q, got empty field", fieldContains)
}
if messageContains != "" && valErr.Message == "" {
t.Errorf("expected message to contain %q, got empty message", messageContains)
}
}