package contracts import ( "context" "encoding/json" "testing" "gitea.maximumdirect.net/eric/notarius/internal/core/artifacts" "gitea.maximumdirect.net/eric/notarius/internal/core/source" ) var _ InputAdapter = fakeAdapter{} var _ Extractor = fakeExtractor{} var _ Validator = fakeValidator{} var _ StructuredLLMClient = fakeLLMClient{} func TestFakeExtractorReturnsCandidateAndValidator(t *testing.T) { validator := fakeValidator{name: "generic-validator"} extractor := fakeExtractor{ key: "generic-extractor", artifactType: "generic-artifact", schemaVersion: "v1", validators: []Validator{validator}, } doc := &source.SourceDocument{ ID: "source-1", Kind: "document", Format: "text/plain", Digest: "sha256:abc123", Units: []source.SourceUnit{ {ID: "u1", Kind: "section", Text: "Source text."}, }, } result, err := extractor.Extract(context.Background(), ExtractionRequest{Source: doc}) if err != nil { t.Fatalf("Extract() error = %v, want nil", err) } if extractor.Key() != "generic-extractor" { t.Fatalf("Key() = %q, want generic-extractor", extractor.Key()) } if extractor.ArtifactType() != "generic-artifact" { t.Fatalf("ArtifactType() = %q, want generic-artifact", extractor.ArtifactType()) } if extractor.SchemaVersion() != "v1" { t.Fatalf("SchemaVersion() = %q, want v1", extractor.SchemaVersion()) } if len(extractor.Validators()) != 1 { t.Fatalf("len(Validators()) = %d, want 1", len(extractor.Validators())) } if extractor.Validators()[0].Name() != "generic-validator" { t.Fatalf("Validators()[0].Name() = %q, want generic-validator", extractor.Validators()[0].Name()) } if len(result.Candidates) != 1 { t.Fatalf("len(Candidates) = %d, want 1", len(result.Candidates)) } candidate := result.Candidates[0] if candidate.Index != 0 { t.Fatalf("Candidate.Index = %d, want 0", candidate.Index) } if candidate.ExtractorKey != extractor.Key() { t.Fatalf("Candidate.ExtractorKey = %q, want %q", candidate.ExtractorKey, extractor.Key()) } if candidate.ArtifactType != extractor.ArtifactType() { t.Fatalf("Candidate.ArtifactType = %q, want %q", candidate.ArtifactType, extractor.ArtifactType()) } if candidate.SchemaVersion != extractor.SchemaVersion() { t.Fatalf("Candidate.SchemaVersion = %q, want %q", candidate.SchemaVersion, extractor.SchemaVersion()) } if string(candidate.Payload) != `{"value":"example"}` { t.Fatalf("Candidate.Payload = %s, want example payload", candidate.Payload) } } type fakeAdapter struct { key string doc *source.SourceDocument } func (adapter fakeAdapter) Key() string { return adapter.key } func (adapter fakeAdapter) Parse(ctx context.Context, req ParseRequest) (*source.SourceDocument, error) { return adapter.doc, nil } type fakeExtractor struct { key string artifactType string schemaVersion string validators []Validator } func (extractor fakeExtractor) Key() string { return extractor.key } func (extractor fakeExtractor) ArtifactType() string { return extractor.artifactType } func (extractor fakeExtractor) SchemaVersion() string { return extractor.schemaVersion } func (extractor fakeExtractor) Validators() []Validator { return extractor.validators } func (extractor fakeExtractor) Extract(ctx context.Context, req ExtractionRequest) (ExtractionResult, error) { return ExtractionResult{ Candidates: []artifacts.Candidate{ { Index: 0, ExtractorKey: extractor.key, ArtifactType: extractor.artifactType, SchemaVersion: extractor.schemaVersion, Payload: json.RawMessage(`{"value":"example"}`), SourceRefs: []source.SourceRef{ { SourceID: req.Source.ID, StartUnitID: req.Source.Units[0].ID, EndUnitID: req.Source.Units[0].ID, }, }, }, }, }, nil } type fakeValidator struct { name string } func (validator fakeValidator) Name() string { return validator.name } func (validator fakeValidator) Validate(ctx context.Context, req ValidationRequest) (ValidationResult, error) { decisions := make([]ValidationDecision, 0, len(req.Candidates)) for _, candidate := range req.Candidates { decisions = append(decisions, ValidationDecision{ CandidateIndex: candidate.Index, Approved: true, ReasonCode: "accepted", Message: "candidate accepted", }) } return ValidationResult{ ValidatorName: validator.name, Decisions: decisions, }, nil } type fakeLLMClient struct{} func (client fakeLLMClient) CompleteStructured(ctx context.Context, req StructuredCompletionRequest, out any) (StructuredCompletionResponse, error) { return StructuredCompletionResponse{ Content: json.RawMessage(`{"value":"example"}`), }, nil }