153 lines
7.2 KiB
Go
153 lines
7.2 KiB
Go
package runner
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/audita/internal/core/config"
|
|
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/proposals"
|
|
)
|
|
|
|
type fakeFactory struct {
|
|
modules map[string]contracts.TranscriptModule
|
|
}
|
|
|
|
func (f fakeFactory) ModuleForSpec(spec contracts.ModuleRunSpec) (contracts.TranscriptModule, error) {
|
|
m, ok := f.modules[spec.InstanceName]
|
|
if !ok {
|
|
return nil, errors.New("module not registered")
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
type fakeModule struct {
|
|
key string
|
|
policy proposals.ReplacementPolicy
|
|
proposeF func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error)
|
|
}
|
|
|
|
func (m fakeModule) Key() string { return m.key }
|
|
func (m fakeModule) ReplacementPolicy() proposals.ReplacementPolicy { return m.policy }
|
|
func (m fakeModule) Validators() []contracts.Validator { return nil }
|
|
func (m fakeModule) Propose(ctx context.Context, req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
if m.proposeF == nil {
|
|
return nil, nil
|
|
}
|
|
return m.proposeF(req)
|
|
}
|
|
|
|
func TestRunnerOneModuleAppliesProposal(t *testing.T) {
|
|
transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}}
|
|
r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{
|
|
"grammar": fakeModule{key: "grammar", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 0.9}}, nil
|
|
}},
|
|
}})
|
|
|
|
out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar"}}})
|
|
if err != nil {
|
|
t.Fatalf("Run error: %v", err)
|
|
}
|
|
if out.FinalTranscript.Segments[0].Text != "the cat" {
|
|
t.Fatalf("expected corrected text, got %q", out.FinalTranscript.Segments[0].Text)
|
|
}
|
|
if len(out.ModuleResults) != 1 || len(out.ModuleResults[0].AppliedChanges) != 1 {
|
|
t.Fatalf("expected one module with one applied change, got %+v", out.ModuleResults)
|
|
}
|
|
}
|
|
|
|
func TestRunnerModulesRunSequentially(t *testing.T) {
|
|
transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}}
|
|
r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{
|
|
"m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil
|
|
}},
|
|
"m2": fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
if req.WorkingTranscript.Segments[0].Text != "the cat" {
|
|
t.Fatalf("second module did not see first module changes: %q", req.WorkingTranscript.Segments[0].Text)
|
|
}
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: 1}}, nil
|
|
}},
|
|
}})
|
|
|
|
out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}})
|
|
if err != nil {
|
|
t.Fatalf("Run error: %v", err)
|
|
}
|
|
if out.FinalTranscript.Segments[0].Text != "the dog" {
|
|
t.Fatalf("expected sequential updates, got %q", out.FinalTranscript.Segments[0].Text)
|
|
}
|
|
}
|
|
|
|
func TestRunnerSkippedRecorded(t *testing.T) {
|
|
transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "word word"}}}
|
|
r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{
|
|
"m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "word", CorrectedText: "term", Confidence: 1}}, nil
|
|
}},
|
|
}})
|
|
out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}})
|
|
if err != nil {
|
|
t.Fatalf("Run error: %v", err)
|
|
}
|
|
if len(out.ModuleResults[0].SkippedChanges) != 1 {
|
|
t.Fatalf("expected one skipped change, got %+v", out.ModuleResults[0].SkippedChanges)
|
|
}
|
|
}
|
|
|
|
func TestRunnerFailureReturnsPartialProgress(t *testing.T) {
|
|
transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}}
|
|
r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{
|
|
"m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil
|
|
}},
|
|
"m2": fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return nil, errors.New("boom")
|
|
}},
|
|
}})
|
|
|
|
out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if out.FinalTranscript.Segments[0].Text != "the cat" {
|
|
t.Fatalf("expected partial transcript progress preserved, got %q", out.FinalTranscript.Segments[0].Text)
|
|
}
|
|
if len(out.ModuleResults) != 2 || out.ModuleResults[1].Status != ModuleStatusFailed {
|
|
t.Fatalf("expected second module failed in results, got %+v", out.ModuleResults)
|
|
}
|
|
}
|
|
|
|
func TestRunnerDoesNotMutateInputTranscript(t *testing.T) {
|
|
transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh"}}}
|
|
before := transcript.Segments[0].Text
|
|
r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{
|
|
"m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
|
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil
|
|
}},
|
|
}})
|
|
|
|
_, err := r.Run(context.Background(), RunInput{Config: ptrConfig(config.Default()), Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}})
|
|
if err != nil {
|
|
t.Fatalf("Run error: %v", err)
|
|
}
|
|
if transcript.Segments[0].Text != before {
|
|
t.Fatalf("expected input transcript unchanged, got %q", transcript.Segments[0].Text)
|
|
}
|
|
}
|
|
|
|
func TestRunnerRepeatedModuleInstanceNames(t *testing.T) {
|
|
specs, err := contracts.ResolveModuleRunSpecs([]string{"glossary", "glossary"})
|
|
if err != nil {
|
|
t.Fatalf("ResolveModuleRunSpecs error: %v", err)
|
|
}
|
|
if specs[0].InstanceName != "glossary_1" || specs[1].InstanceName != "glossary_2" {
|
|
t.Fatalf("unexpected instance names: %+v", specs)
|
|
}
|
|
}
|
|
|
|
func ptrConfig(c config.Config) *config.Config { return &c }
|