package shared import ( "context" "reflect" "strings" "testing" "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" ) func TestPrepareChunkExtraction(t *testing.T) { canceled, cancel := context.WithCancel(context.Background()) cancel() tests := []struct { name string ctx context.Context configure func(*contracts.TypedExtractionRequest) sourceInput contracts.LLMInputMaterial want contracts.LLMInputMaterial wantErr string mutateOutput bool }{ { name: "nil context", wantErr: "context must not be nil", }, { name: "canceled context", ctx: canceled, wantErr: "context error before extraction", }, { name: "nil source", configure: func(req *contracts.TypedExtractionRequest) { req.Source = nil }, wantErr: "source must not be nil", }, { name: "nil chunk", configure: func(req *contracts.TypedExtractionRequest) { req.Chunk = nil }, wantErr: "chunk must not be nil", }, { name: "empty chunk units", configure: func(req *contracts.TypedExtractionRequest) { req.Chunk = &source.Chunk{ID: req.Chunk.ID} }, wantErr: "units must not be empty", }, { name: "fallback to chunk content", want: contracts.NewLLMInputMaterial("source", "application/json", []byte(`{"units":[1,2]}`), "", ""), mutateOutput: true, }, { name: "clone isolation", sourceInput: contracts.NewLLMInputMaterial("source", "application/json", []byte(`{"units":[1,2]}`), "sha256:source", "file:///source.json"), want: contracts.NewLLMInputMaterial("source", "application/json", []byte(`{"units":[1,2]}`), "sha256:source", "file:///source.json"), mutateOutput: true, }, { name: "mismatched content", sourceInput: contracts.NewLLMInputMaterial("source", "application/json", []byte(`{"units":[9]}`), "sha256:other", "file:///other.json"), wantErr: "source input must match chunk", }, { name: "default fields", sourceInput: contracts.LLMInputMaterial{ Content: []byte(`{"units":[1,2]}`), Digest: "sha256:source", OriginURI: "file:///source.json", }, want: contracts.LLMInputMaterial{ Name: "source", MediaType: "application/json", Content: []byte(`{"units":[1,2]}`), Digest: "sha256:source", OriginURI: "file:///source.json", SizeBytes: int64(len(`{"units":[1,2]}`)), }, }, { name: "preserve explicit metadata", sourceInput: contracts.LLMInputMaterial{ Name: "transcript", MediaType: "text/plain", Content: []byte(`{"units":[1,2]}`), Digest: "sha256:explicit", OriginURI: "file:///explicit.txt", SizeBytes: 42, }, want: contracts.LLMInputMaterial{ Name: "transcript", MediaType: "text/plain", Content: []byte(`{"units":[1,2]}`), Digest: "sha256:explicit", OriginURI: "file:///explicit.txt", SizeBytes: 42, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { req := extractionRequest() req.SourceInput = test.sourceInput if test.configure != nil { test.configure(&req) } ctx := test.ctx if ctx == nil && test.wantErr != "context must not be nil" { ctx = context.Background() } got, err := PrepareChunkExtraction(ctx, req) if test.wantErr != "" { if err == nil || !strings.Contains(err.Error(), test.wantErr) { t.Fatalf("PrepareChunkExtraction() error = %v, want %q", err, test.wantErr) } return } if err != nil { t.Fatalf("PrepareChunkExtraction() error = %v, want nil", err) } if !reflect.DeepEqual(got, test.want) { t.Fatalf("PrepareChunkExtraction() = %#v, want %#v", got, test.want) } if test.mutateOutput { got.Content[0] = 'x' if string(req.SourceInput.Content) != string(test.sourceInput.Content) || string(req.Chunk.Content) != `{"units":[1,2]}` { t.Fatal("PrepareChunkExtraction() output shares request content") } } }) } } func extractionRequest() contracts.TypedExtractionRequest { doc := &source.SourceDocument{ID: "session-alpha"} chunk := &source.Chunk{ ID: "session-alpha:chunk:0", SourceID: doc.ID, Content: []byte(`{"units":[1,2]}`), MediaType: "application/json", Units: []source.SourceUnit{{ID: 1}}, } return contracts.TypedExtractionRequest{Source: doc, Chunk: chunk} }