Implement raw module output contracts
This commit is contained in:
@@ -2,11 +2,8 @@ package noop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/artifacts"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||
)
|
||||
@@ -39,7 +36,15 @@ func (n *Normalizer) Normalize(ctx context.Context, req contracts.NormalizeReque
|
||||
if err := ctx.Err(); err != nil {
|
||||
return contracts.NormalizeResult{}, normalizerErrorf("context error before normalize: %w", err)
|
||||
}
|
||||
return contracts.NormalizeResult{Candidates: cloneCandidates(req.Candidates)}, nil
|
||||
return contracts.NormalizeResult{
|
||||
Output: contracts.NormalizeOutput{
|
||||
LaneID: req.LaneID,
|
||||
NormalizerKey: Key,
|
||||
SourceID: req.MergeOutput.SourceID,
|
||||
Schema: req.MergeOutput.Schema,
|
||||
Payload: cloneRawPayload(req.MergeOutput.Payload),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ModuleSpec() pipeline.ModuleSpec {
|
||||
@@ -57,24 +62,13 @@ func Register(registry *pipeline.NormalizerRegistry) error {
|
||||
})
|
||||
}
|
||||
|
||||
func cloneCandidates(candidates []artifacts.ArtifactCandidate) []artifacts.ArtifactCandidate {
|
||||
if len(candidates) == 0 {
|
||||
return nil
|
||||
func cloneRawPayload(payload contracts.RawPayload) contracts.RawPayload {
|
||||
return contracts.RawPayload{
|
||||
Content: append([]byte(nil), payload.Content...),
|
||||
MediaType: payload.MediaType,
|
||||
Metadata: cloneMetadata(payload.Metadata),
|
||||
Warnings: append([]contracts.Warning(nil), payload.Warnings...),
|
||||
}
|
||||
|
||||
out := make([]artifacts.ArtifactCandidate, 0, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
out = append(out, artifacts.ArtifactCandidate{
|
||||
Index: candidate.Index,
|
||||
ExtractorKey: candidate.ExtractorKey,
|
||||
ArtifactType: candidate.ArtifactType,
|
||||
SchemaVersion: candidate.SchemaVersion,
|
||||
Payload: append(json.RawMessage(nil), candidate.Payload...),
|
||||
SourceRefs: append([]source.SourceRef(nil), candidate.SourceRefs...),
|
||||
Metadata: cloneMetadata(candidate.Metadata),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneMetadata(metadata map[string]any) map[string]any {
|
||||
|
||||
@@ -2,12 +2,9 @@ package noop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/artifacts"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||
)
|
||||
@@ -43,98 +40,60 @@ func TestModuleSpecAndRegister(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizePassesThroughOrderAndValues(t *testing.T) {
|
||||
input := []artifacts.ArtifactCandidate{
|
||||
candidate(3, "third"),
|
||||
candidate(1, "first"),
|
||||
candidate(2, "second"),
|
||||
}
|
||||
func TestNormalizePassesThroughMergeOutput(t *testing.T) {
|
||||
input := mergeOutput(`{"name":"original"}`)
|
||||
|
||||
result, err := New().Normalize(context.Background(), contracts.NormalizeRequest{Candidates: input})
|
||||
result, err := New().Normalize(context.Background(), contracts.NormalizeRequest{
|
||||
LaneID: "events",
|
||||
MergeOutput: input,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Normalize() error = %v, want nil", err)
|
||||
}
|
||||
if len(result.Warnings) != 0 {
|
||||
t.Fatalf("Warnings = %#v, want none", result.Warnings)
|
||||
}
|
||||
|
||||
got := candidateNames(result.Candidates)
|
||||
want := []string{"third", "first", "second"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("candidate order = %#v, want %#v", got, want)
|
||||
if result.Output.LaneID != "events" || result.Output.NormalizerKey != Key {
|
||||
t.Fatalf("output provenance = %#v, want lane and normalizer", result.Output)
|
||||
}
|
||||
if !reflect.DeepEqual(result.Candidates[0].SourceRefs, input[0].SourceRefs) {
|
||||
t.Fatalf("SourceRefs = %#v, want %#v", result.Candidates[0].SourceRefs, input[0].SourceRefs)
|
||||
if string(result.Output.Payload.Content) != `{"name":"original"}` {
|
||||
t.Fatalf("content = %s, want original content", result.Output.Payload.Content)
|
||||
}
|
||||
if !reflect.DeepEqual(result.Candidates[0].Metadata, input[0].Metadata) {
|
||||
t.Fatalf("Metadata = %#v, want %#v", result.Candidates[0].Metadata, input[0].Metadata)
|
||||
if result.Output.Payload.Metadata["name"] != "original" {
|
||||
t.Fatalf("metadata = %#v, want original metadata", result.Output.Payload.Metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeDefensivelyCopiesCandidates(t *testing.T) {
|
||||
input := []artifacts.ArtifactCandidate{candidate(1, "original")}
|
||||
func TestNormalizeDefensivelyCopiesRawPayload(t *testing.T) {
|
||||
input := mergeOutput(`{"name":"original"}`)
|
||||
|
||||
result, err := New().Normalize(context.Background(), contracts.NormalizeRequest{Candidates: input})
|
||||
result, err := New().Normalize(context.Background(), contracts.NormalizeRequest{
|
||||
LaneID: "events",
|
||||
MergeOutput: input,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Normalize() error = %v, want nil", err)
|
||||
}
|
||||
if len(result.Candidates) != 1 {
|
||||
t.Fatalf("len(Candidates) = %d, want 1", len(result.Candidates))
|
||||
}
|
||||
|
||||
input[0].Index = 99
|
||||
input[0].Payload[0] = '['
|
||||
input[0].SourceRefs[0].EndUnitID = 99
|
||||
input[0].Metadata["name"] = "changed"
|
||||
input.Payload.Content[0] = '['
|
||||
input.Payload.Metadata["name"] = "changed"
|
||||
|
||||
got := result.Candidates[0]
|
||||
if got.Index != 1 {
|
||||
t.Fatalf("Index = %d, want 1", got.Index)
|
||||
if string(result.Output.Payload.Content) != `{"name":"original"}` {
|
||||
t.Fatalf("content changed after input mutation: %s", result.Output.Payload.Content)
|
||||
}
|
||||
if string(got.Payload) != `{"name":"original"}` {
|
||||
t.Fatalf("Payload = %s, want original payload", got.Payload)
|
||||
}
|
||||
if got.SourceRefs[0].EndUnitID != 1 {
|
||||
t.Fatalf("SourceRefs = %#v, want original source ref", got.SourceRefs)
|
||||
}
|
||||
if got.Metadata["name"] != "original" {
|
||||
t.Fatalf("Metadata = %#v, want original metadata", got.Metadata)
|
||||
if result.Output.Payload.Metadata["name"] != "original" {
|
||||
t.Fatalf("metadata changed after input mutation: %#v", result.Output.Payload.Metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeHandlesEmptyInput(t *testing.T) {
|
||||
result, err := New().Normalize(context.Background(), contracts.NormalizeRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("Normalize() error = %v, want nil", err)
|
||||
}
|
||||
if len(result.Candidates) != 0 {
|
||||
t.Fatalf("len(Candidates) = %d, want 0", len(result.Candidates))
|
||||
}
|
||||
if len(result.Warnings) != 0 {
|
||||
t.Fatalf("Warnings = %#v, want none", result.Warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func candidate(index int, name string) artifacts.ArtifactCandidate {
|
||||
return artifacts.ArtifactCandidate{
|
||||
Index: index,
|
||||
ExtractorKey: "generic-extractor",
|
||||
ArtifactType: "generic-artifact",
|
||||
SchemaVersion: "v1",
|
||||
Payload: json.RawMessage(`{"name":"` + name + `"}`),
|
||||
SourceRefs: []source.SourceRef{
|
||||
{SourceID: "source-1", StartUnitID: 1, EndUnitID: 1},
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"name": name,
|
||||
func mergeOutput(content string) contracts.MergeOutput {
|
||||
return contracts.MergeOutput{
|
||||
LaneID: "events",
|
||||
MergerKey: "merge",
|
||||
SourceID: "source-1",
|
||||
Schema: contracts.ResponseSchema{ID: "schema-id", Name: "schema-name", Version: "v1"},
|
||||
Payload: contracts.RawPayload{
|
||||
Content: []byte(content),
|
||||
MediaType: "application/json",
|
||||
Metadata: map[string]any{"name": "original"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func candidateNames(candidates []artifacts.ArtifactCandidate) []string {
|
||||
names := make([]string, 0, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
names = append(names, candidate.Metadata["name"].(string))
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user