195 lines
5.7 KiB
Go
195 lines
5.7 KiB
Go
package artifacts
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
|
)
|
|
|
|
func TestArtifactFromCandidatePreservesCandidateFields(t *testing.T) {
|
|
candidate := ArtifactCandidate{
|
|
Index: 7,
|
|
ExtractorKey: "generic-extractor",
|
|
ArtifactType: "generic-artifact",
|
|
SchemaVersion: "v1",
|
|
Payload: json.RawMessage(`{"name":"example"}`),
|
|
SourceRefs: []source.SourceRef{
|
|
{SourceID: "source-1", StartUnitID: "u1", EndUnitID: "u2"},
|
|
},
|
|
Metadata: map[string]any{
|
|
"confidence": 0.75,
|
|
},
|
|
}
|
|
|
|
artifact := ArtifactFromCandidate(candidate)
|
|
|
|
if artifact.ExtractorKey != candidate.ExtractorKey {
|
|
t.Fatalf("ExtractorKey = %q, want %q", artifact.ExtractorKey, candidate.ExtractorKey)
|
|
}
|
|
if artifact.ArtifactType != candidate.ArtifactType {
|
|
t.Fatalf("ArtifactType = %q, want %q", artifact.ArtifactType, candidate.ArtifactType)
|
|
}
|
|
if artifact.SchemaVersion != candidate.SchemaVersion {
|
|
t.Fatalf("SchemaVersion = %q, want %q", artifact.SchemaVersion, candidate.SchemaVersion)
|
|
}
|
|
if string(artifact.Payload) != string(candidate.Payload) {
|
|
t.Fatalf("Payload = %s, want %s", artifact.Payload, candidate.Payload)
|
|
}
|
|
if !reflect.DeepEqual(artifact.SourceRefs, candidate.SourceRefs) {
|
|
t.Fatalf("SourceRefs = %#v, want %#v", artifact.SourceRefs, candidate.SourceRefs)
|
|
}
|
|
if !reflect.DeepEqual(artifact.Metadata, candidate.Metadata) {
|
|
t.Fatalf("Metadata = %#v, want %#v", artifact.Metadata, candidate.Metadata)
|
|
}
|
|
|
|
candidate.Payload[0] = '['
|
|
candidate.SourceRefs[0].StartUnitID = "changed"
|
|
candidate.Metadata["confidence"] = 0.5
|
|
|
|
if string(artifact.Payload) != `{"name":"example"}` {
|
|
t.Fatalf("Payload changed after candidate mutation: %s", artifact.Payload)
|
|
}
|
|
if artifact.SourceRefs[0].StartUnitID != "u1" {
|
|
t.Fatalf("SourceRefs changed after candidate mutation: %#v", artifact.SourceRefs)
|
|
}
|
|
if artifact.Metadata["confidence"] != 0.75 {
|
|
t.Fatalf("Metadata changed after candidate mutation: %#v", artifact.Metadata)
|
|
}
|
|
}
|
|
|
|
func TestJSONMarshalUsesExpectedFieldNames(t *testing.T) {
|
|
candidate := ArtifactCandidate{
|
|
Index: 1,
|
|
ExtractorKey: "generic-extractor",
|
|
ArtifactType: "generic-artifact",
|
|
SchemaVersion: "v1",
|
|
Payload: json.RawMessage(`{"value":true}`),
|
|
SourceRefs: []source.SourceRef{
|
|
{SourceID: "source-1", StartUnitID: "u1", EndUnitID: "u1"},
|
|
},
|
|
Metadata: map[string]any{
|
|
"reviewed": true,
|
|
},
|
|
}
|
|
rejected := RejectedArtifact{
|
|
Candidate: candidate,
|
|
ValidatorName: "generic-validator",
|
|
ReasonCode: "invalid",
|
|
Message: "candidate was not accepted",
|
|
}
|
|
|
|
gotJSON, err := json.Marshal(rejected)
|
|
if err != nil {
|
|
t.Fatalf("json.Marshal() error = %v", err)
|
|
}
|
|
|
|
var got map[string]any
|
|
if err := json.Unmarshal(gotJSON, &got); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
|
|
assertHasKeys(t, got, "candidate", "validator_name", "reason_code", "message")
|
|
|
|
gotCandidate, ok := got["candidate"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("candidate = %#v, want object", got["candidate"])
|
|
}
|
|
assertHasKeys(t, gotCandidate, "index", "extractor_key", "artifact_type", "schema_version", "payload", "source_refs", "metadata")
|
|
|
|
gotRefs, ok := gotCandidate["source_refs"].([]any)
|
|
if !ok {
|
|
t.Fatalf("source_refs = %#v, want array", gotCandidate["source_refs"])
|
|
}
|
|
if len(gotRefs) != 1 {
|
|
t.Fatalf("len(source_refs) = %d, want 1", len(gotRefs))
|
|
}
|
|
gotRef, ok := gotRefs[0].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("source_refs[0] = %#v, want object", gotRefs[0])
|
|
}
|
|
assertHasKeys(t, gotRef, "source_id", "start_unit_id", "end_unit_id")
|
|
}
|
|
|
|
func TestRunManifestOmitsEmptyOptionalFields(t *testing.T) {
|
|
gotJSON, err := json.Marshal(RunManifest{})
|
|
if err != nil {
|
|
t.Fatalf("json.Marshal() error = %v", err)
|
|
}
|
|
|
|
if string(gotJSON) != "{}" {
|
|
t.Fatalf("json.Marshal(RunManifest{}) = %s, want {}", gotJSON)
|
|
}
|
|
}
|
|
|
|
func TestRunManifestIncludesPipelineAndArtifactLaneFields(t *testing.T) {
|
|
manifest := RunManifest{
|
|
PipelineID: "pipeline-1",
|
|
PipelineDigest: "sha256:abc123",
|
|
LLMProfiles: []LLMProfileManifest{
|
|
{ID: "default", Provider: "openai-compatible", Model: "model-a"},
|
|
},
|
|
ArtifactLanes: []ArtifactLaneManifest{
|
|
{
|
|
ID: "events",
|
|
Extractor: "event-extractor",
|
|
Merger: "appendorder",
|
|
Normalizer: "noop",
|
|
Validators: []string{"grounded"},
|
|
Metadata: map[string]any{
|
|
"extractor": map[string]any{"prompt_id": "test.prompt"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
gotJSON, err := json.Marshal(manifest)
|
|
if err != nil {
|
|
t.Fatalf("json.Marshal() error = %v", err)
|
|
}
|
|
|
|
var got map[string]any
|
|
if err := json.Unmarshal(gotJSON, &got); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
|
|
assertHasKeys(t, got, "pipeline_id", "pipeline_digest", "artifact_lanes", "llm_profiles")
|
|
|
|
profiles, ok := got["llm_profiles"].([]any)
|
|
if !ok {
|
|
t.Fatalf("llm_profiles = %#v, want array", got["llm_profiles"])
|
|
}
|
|
if len(profiles) != 1 {
|
|
t.Fatalf("len(llm_profiles) = %d, want 1", len(profiles))
|
|
}
|
|
profile, ok := profiles[0].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("llm_profiles[0] = %#v, want object", profiles[0])
|
|
}
|
|
assertHasKeys(t, profile, "id", "provider", "model")
|
|
|
|
lanes, ok := got["artifact_lanes"].([]any)
|
|
if !ok {
|
|
t.Fatalf("artifact_lanes = %#v, want array", got["artifact_lanes"])
|
|
}
|
|
if len(lanes) != 1 {
|
|
t.Fatalf("len(artifact_lanes) = %d, want 1", len(lanes))
|
|
}
|
|
lane, ok := lanes[0].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("artifact_lanes[0] = %#v, want object", lanes[0])
|
|
}
|
|
assertHasKeys(t, lane, "id", "extractor", "merger", "normalizer", "validators", "metadata")
|
|
}
|
|
|
|
func assertHasKeys(t *testing.T, values map[string]any, keys ...string) {
|
|
t.Helper()
|
|
|
|
for _, key := range keys {
|
|
if _, ok := values[key]; !ok {
|
|
t.Fatalf("missing key %q in %#v", key, values)
|
|
}
|
|
}
|
|
}
|