Unify trim selector projection across output schemas
This commit is contained in:
@@ -399,6 +399,106 @@ func TestApplyMinimalDoesNotIncludeOverlapGroups(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplySelectorPolicyIsSharedAcrossSchemas(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
opts Options
|
||||
wantTexts []string
|
||||
wantOldToNew map[int]int
|
||||
wantRemoved []int
|
||||
wantSegmentCount int
|
||||
wantErrorSubstring string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "keep preserves input order regardless of selector order",
|
||||
opts: Options{Mode: ModeKeep, Selector: mustParseSelector(t, "4,1,3")},
|
||||
wantTexts: []string{"alpha", "gamma", "delta"},
|
||||
wantOldToNew: map[int]int{1: 1, 3: 2, 4: 3},
|
||||
wantRemoved: []int{2},
|
||||
wantSegmentCount: 3,
|
||||
},
|
||||
{
|
||||
name: "remove reports deterministic renumbering metadata",
|
||||
opts: Options{Mode: ModeRemove, Selector: mustParseSelector(t, "2,4")},
|
||||
wantTexts: []string{"alpha", "gamma"},
|
||||
wantOldToNew: map[int]int{1: 1, 3: 2},
|
||||
wantRemoved: []int{2, 4},
|
||||
wantSegmentCount: 2,
|
||||
},
|
||||
{
|
||||
name: "missing selected id returns error",
|
||||
opts: Options{Mode: ModeKeep, Selector: mustParseSelector(t, "9")},
|
||||
wantErrorSubstring: "does not exist",
|
||||
},
|
||||
{
|
||||
name: "empty selector returns error",
|
||||
opts: Options{Mode: ModeKeep, Selector: Selector{}},
|
||||
wantErrorSubstring: "selector cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "invalid mode returns error",
|
||||
opts: Options{Mode: Mode("bad"), Selector: mustParseSelector(t, "1")},
|
||||
wantErrorSubstring: `invalid trim mode "bad"`,
|
||||
},
|
||||
{
|
||||
name: "empty output blocked when allow empty is false",
|
||||
opts: Options{Mode: ModeRemove, Selector: mustParseSelector(t, "1-4")},
|
||||
wantErrorSubstring: "empty transcript",
|
||||
},
|
||||
{
|
||||
name: "empty output allowed when allow empty is true",
|
||||
opts: Options{Mode: ModeRemove, Selector: mustParseSelector(t, "1-4"), AllowEmpty: true},
|
||||
wantTexts: []string{},
|
||||
wantOldToNew: map[int]int{},
|
||||
wantRemoved: []int{1, 2, 3, 4},
|
||||
wantSegmentCount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fullInput := fullTranscriptFixture()
|
||||
intermediateInput := intermediateFixture()
|
||||
minimalInput := minimalFixture()
|
||||
|
||||
fullResult, fullErr := Apply(fullInput, test.opts)
|
||||
intermediateResult, intermediateErr := ApplyIntermediate(intermediateInput, test.opts)
|
||||
minimalResult, minimalErr := ApplyMinimal(minimalInput, test.opts)
|
||||
|
||||
if test.wantErrorSubstring != "" {
|
||||
assertErrorContains(t, fullErr, test.wantErrorSubstring)
|
||||
assertErrorContains(t, intermediateErr, test.wantErrorSubstring)
|
||||
assertErrorContains(t, minimalErr, test.wantErrorSubstring)
|
||||
return
|
||||
}
|
||||
if fullErr != nil {
|
||||
t.Fatalf("apply full failed: %v", fullErr)
|
||||
}
|
||||
if intermediateErr != nil {
|
||||
t.Fatalf("apply intermediate failed: %v", intermediateErr)
|
||||
}
|
||||
if minimalErr != nil {
|
||||
t.Fatalf("apply minimal failed: %v", minimalErr)
|
||||
}
|
||||
|
||||
assertIntSlice(t, extractFullIDs(fullResult.Transcript.Segments), extractSequentialIDs(test.wantSegmentCount))
|
||||
assertIntSlice(t, extractIntermediateIDs(intermediateResult.Transcript.Segments), extractSequentialIDs(test.wantSegmentCount))
|
||||
assertIntSlice(t, extractMinimalIDs(minimalResult.Transcript.Segments), extractSequentialIDs(test.wantSegmentCount))
|
||||
assertStringSlice(t, extractFullTexts(fullResult.Transcript.Segments), test.wantTexts)
|
||||
assertStringSlice(t, extractIntermediateTexts(intermediateResult.Transcript.Segments), test.wantTexts)
|
||||
assertStringSlice(t, extractMinimalTexts(minimalResult.Transcript.Segments), test.wantTexts)
|
||||
assertIntMap(t, fullResult.OldToNewID, test.wantOldToNew)
|
||||
assertIntMap(t, intermediateResult.OldToNewID, test.wantOldToNew)
|
||||
assertIntMap(t, minimalResult.OldToNewID, test.wantOldToNew)
|
||||
assertIntSlice(t, fullResult.RemovedIDs, test.wantRemoved)
|
||||
assertIntSlice(t, intermediateResult.RemovedIDs, test.wantRemoved)
|
||||
assertIntSlice(t, minimalResult.RemovedIDs, test.wantRemoved)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyOutputInvariantsValidAfterRenumberAndOverlapRecompute(t *testing.T) {
|
||||
input := overlapTranscriptFixture()
|
||||
selector := mustParseSelector(t, "2,1")
|
||||
@@ -666,3 +766,108 @@ func equalStringSlices(got []string, want []string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func assertErrorContains(t *testing.T, err error, substring string) {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q", substring)
|
||||
}
|
||||
if !strings.Contains(err.Error(), substring) {
|
||||
t.Fatalf("error %q does not contain %q", err.Error(), substring)
|
||||
}
|
||||
}
|
||||
|
||||
func assertStringSlice(t *testing.T, got []string, want []string) {
|
||||
t.Helper()
|
||||
if !equalStringSlices(got, want) {
|
||||
t.Fatalf("slice = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func extractSequentialIDs(count int) []int {
|
||||
ids := make([]int, count)
|
||||
for index := range ids {
|
||||
ids[index] = index + 1
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func extractFullIDs(segments []schema.Segment) []int {
|
||||
ids := make([]int, len(segments))
|
||||
for index, segment := range segments {
|
||||
ids[index] = segment.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func extractIntermediateIDs(segments []schema.IntermediateSegment) []int {
|
||||
ids := make([]int, len(segments))
|
||||
for index, segment := range segments {
|
||||
ids[index] = segment.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func extractMinimalIDs(segments []schema.MinimalSegment) []int {
|
||||
ids := make([]int, len(segments))
|
||||
for index, segment := range segments {
|
||||
ids[index] = segment.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func extractFullTexts(segments []schema.Segment) []string {
|
||||
texts := make([]string, len(segments))
|
||||
for index, segment := range segments {
|
||||
texts[index] = segment.Text
|
||||
}
|
||||
return texts
|
||||
}
|
||||
|
||||
func extractIntermediateTexts(segments []schema.IntermediateSegment) []string {
|
||||
texts := make([]string, len(segments))
|
||||
for index, segment := range segments {
|
||||
texts[index] = segment.Text
|
||||
}
|
||||
return texts
|
||||
}
|
||||
|
||||
func extractMinimalTexts(segments []schema.MinimalSegment) []string {
|
||||
texts := make([]string, len(segments))
|
||||
for index, segment := range segments {
|
||||
texts[index] = segment.Text
|
||||
}
|
||||
return texts
|
||||
}
|
||||
|
||||
func intermediateFixture() schema.IntermediateTranscript {
|
||||
return schema.IntermediateTranscript{
|
||||
Metadata: schema.IntermediateMetadata{
|
||||
Application: "seriatim",
|
||||
Version: "v-test",
|
||||
OutputSchema: schema.OutputSchemaIntermediate,
|
||||
},
|
||||
Segments: []schema.IntermediateSegment{
|
||||
{ID: 1, Start: 1, End: 2, Speaker: "Alice", Text: "alpha", Categories: []string{"word-run"}},
|
||||
{ID: 2, Start: 2, End: 3, Speaker: "Bob", Text: "beta", Categories: []string{"filler", "backchannel"}},
|
||||
{ID: 3, Start: 3, End: 4, Speaker: "Carol", Text: "gamma", Categories: []string{"normal"}},
|
||||
{ID: 4, Start: 4, End: 5, Speaker: "Dan", Text: "delta", Categories: []string{"normal"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func minimalFixture() schema.MinimalTranscript {
|
||||
return schema.MinimalTranscript{
|
||||
Metadata: schema.MinimalMetadata{
|
||||
Application: "seriatim",
|
||||
Version: "v-test",
|
||||
OutputSchema: schema.OutputSchemaMinimal,
|
||||
},
|
||||
Segments: []schema.MinimalSegment{
|
||||
{ID: 1, Start: 1, End: 2, Speaker: "Alice", Text: "alpha"},
|
||||
{ID: 2, Start: 2, End: 3, Speaker: "Bob", Text: "beta"},
|
||||
{ID: 3, Start: 3, End: 4, Speaker: "Carol", Text: "gamma"},
|
||||
{ID: 4, Start: 4, End: 5, Speaker: "Dan", Text: "delta"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user