Centralize builder request cloning
This commit is contained in:
@@ -305,6 +305,7 @@ func TestPrepareDeliversTargetReferencesAsIndependentBuildInputs(t *testing.T) {
|
||||
if name != "extract" {
|
||||
return
|
||||
}
|
||||
request.Options["nested"].(map[string]any)["value"] = "mutated by extractor builder"
|
||||
slot := request.References.Slots["extract"]
|
||||
slot.Items[0].Content = []byte("mutated by extractor builder")
|
||||
request.References.Slots["extract"] = slot
|
||||
@@ -318,6 +319,7 @@ func TestPrepareDeliversTargetReferencesAsIndependentBuildInputs(t *testing.T) {
|
||||
resolved.Steps[0].ArtifactLanes[0].ExtractReferences.ReferenceSet = constructionReferenceSet("extract", "extract reference")
|
||||
resolved.Steps[0].ArtifactLanes[0].MergeReferences.ReferenceSet = constructionReferenceSet("merge", "merge reference")
|
||||
resolved.Steps[0].ArtifactLanes[0].NormalizeReferences.ReferenceSet = constructionReferenceSet("normalize", "normalize reference")
|
||||
resolved.Steps[0].ArtifactLanes[0].Extract.Options = constructionBuildRequest().Options
|
||||
|
||||
prepared, err := Prepare(resolved, registries, ModuleDependencies{})
|
||||
if err != nil {
|
||||
@@ -339,6 +341,15 @@ func TestPrepareDeliversTargetReferencesAsIndependentBuildInputs(t *testing.T) {
|
||||
if got := constructionReferenceContent(resolved.Steps[0].ArtifactLanes[0].ExtractReferences.ReferenceSet); got != "extract reference" {
|
||||
t.Fatalf("resolved extract references = %q, want original content", got)
|
||||
}
|
||||
if got := constructionReferenceContent(prepared.resolved.Steps[0].ArtifactLanes[0].ExtractReferences.ReferenceSet); got != "extract reference" {
|
||||
t.Fatalf("prepared extract references = %q, want original content", got)
|
||||
}
|
||||
if got := resolved.Steps[0].ArtifactLanes[0].Extract.Options["nested"].(map[string]any)["value"]; got != "original" {
|
||||
t.Fatalf("resolved extract options = %#v, want original nested value", got)
|
||||
}
|
||||
if got := prepared.resolved.Steps[0].ArtifactLanes[0].Extract.Options["nested"].(map[string]any)["value"]; got != "original" {
|
||||
t.Fatalf("prepared extract options = %#v, want original nested value", got)
|
||||
}
|
||||
|
||||
_, err = prepared.Steps[0].lanes[0].typed.extract(context.Background(), prepared.Steps[0].lanes[0].typed.extractor, contracts.TypedExtractionRequest{
|
||||
References: CloneReferenceSet(resolved.Steps[0].ArtifactLanes[0].ExtractReferences.ReferenceSet),
|
||||
@@ -354,6 +365,163 @@ func TestPrepareDeliversTargetReferencesAsIndependentBuildInputs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisteredBuildersReceiveIndependentBuildRequests(t *testing.T) {
|
||||
request := constructionBuildRequest()
|
||||
probe := buildRequestMutationProbe{t: t, want: cloneBuildRequest(request)}
|
||||
|
||||
inputs := NewInputAdapterRegistry()
|
||||
if err := inputs.RegisterBuilderWithSpec(testModuleSpec("input", StageInput), rejectUnconfiguredOptions, func(request BuildRequest) (contracts.InputAdapter, error) {
|
||||
probe.observe(request)
|
||||
return &constructionInput{key: "input"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
chunkers := NewChunkerRegistry()
|
||||
if err := chunkers.RegisterBuilderWithSpec(testModuleSpec("chunk", StageChunk), rejectUnconfiguredOptions, func(request BuildRequest) (contracts.Chunker, error) {
|
||||
probe.observe(request)
|
||||
return &typedTestChunker{key: "chunk"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outputs := NewOutputEncoderRegistry()
|
||||
if err := outputs.RegisterBuilderWithSpec(testModuleSpec("output", StageOutput), rejectUnconfiguredOptions, func(request BuildRequest) (contracts.OutputEncoder, error) {
|
||||
probe.observe(request)
|
||||
return &typedTestOutput{key: "output"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
extractors := NewExtractorRegistry()
|
||||
extractSpec := testModuleSpec("extract", StageExtract)
|
||||
extractSpec.ArtifactKind = "test/notes"
|
||||
if err := RegisterExtractorBuilder(extractors, extractSpec, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.Extractor[codecNotes], error) {
|
||||
probe.observe(request)
|
||||
return typedTestExtractor[codecNotes]{key: "extract"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mergers := NewMergerRegistry()
|
||||
mergeSpec := testModuleSpec("merge", StageMerge)
|
||||
mergeSpec.ArtifactKind = "test/notes"
|
||||
if err := RegisterMergerBuilder(mergers, mergeSpec, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.Merger[codecNotes], error) {
|
||||
probe.observe(request)
|
||||
return typedTestMerger[codecNotes]{key: "merge"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
normalizers := NewNormalizerRegistry()
|
||||
normalizeSpec := testModuleSpec("normalize", StageNormalize)
|
||||
normalizeSpec.ArtifactKind = "test/notes"
|
||||
if err := RegisterNormalizerBuilder(normalizers, normalizeSpec, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.Normalizer[codecNotes], error) {
|
||||
probe.observe(request)
|
||||
return typedTestNormalizer[codecNotes]{key: "normalize"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
validators := NewValidatorRegistry()
|
||||
if err := RegisterChunkValidatorBuilder(validators, ValidatorSpec{Key: "chunk-validator", ExecutionClass: contracts.ExecutionClassDeterministic}, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.ChunkValidator, error) {
|
||||
probe.observe(request)
|
||||
return typedTestChunkValidator{key: "chunk-validator"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := RegisterTypedValidatorBuilder(validators, "test/notes", ValidatorSpec{Key: "typed-validator", ExecutionClass: contracts.ExecutionClassDeterministic}, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.TypedValidator[codecNotes], error) {
|
||||
probe.observe(request)
|
||||
return typedTestValidator[codecNotes]{key: "typed-validator"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := RegisterSerializedValidatorBuilder(validators, SerializedValidatorSpec{ValidatorSpec: ValidatorSpec{Key: "serialized-validator", ExecutionClass: contracts.ExecutionClassDeterministic}, SupportsArtifacts: true}, rejectUnconfiguredOptions, func(request BuildRequest) (contracts.SerializedValidator, error) {
|
||||
probe.observe(request)
|
||||
return typedTestSerializedValidator{key: "serialized-validator"}, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
builders := []struct {
|
||||
name string
|
||||
call func() error
|
||||
}{
|
||||
{name: "input", call: func() error { _, err := inputs.BuildWithRequest("input", request); return err }},
|
||||
{name: "chunk", call: func() error { _, err := chunkers.BuildWithRequest("chunk", request); return err }},
|
||||
{name: "output", call: func() error { _, err := outputs.BuildWithRequest("output", request); return err }},
|
||||
{name: "extract", call: func() error {
|
||||
entry, _ := extractors.typedEntry("extract")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
{name: "merge", call: func() error {
|
||||
entry, _ := mergers.typedEntry("merge", "test/notes")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
{name: "normalize", call: func() error {
|
||||
entry, _ := normalizers.typedEntry("normalize", "test/notes")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
{name: "chunk validator", call: func() error {
|
||||
entry, _ := validators.chunkEntry("chunk-validator")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
{name: "typed validator", call: func() error {
|
||||
entry, _ := validators.typedEntry("typed-validator", "test/notes")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
{name: "serialized validator", call: func() error {
|
||||
entry, _ := validators.serializedEntry("serialized-validator")
|
||||
_, err := entry.builder(request)
|
||||
return err
|
||||
}},
|
||||
}
|
||||
for _, builder := range builders {
|
||||
t.Run(builder.name, func(t *testing.T) {
|
||||
if err := builder.call(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
if !reflect.DeepEqual(request, probe.want) {
|
||||
t.Fatalf("build request = %#v, want unchanged %#v", request, probe.want)
|
||||
}
|
||||
}
|
||||
|
||||
type buildRequestMutationProbe struct {
|
||||
t *testing.T
|
||||
want BuildRequest
|
||||
}
|
||||
|
||||
func (probe buildRequestMutationProbe) observe(request BuildRequest) {
|
||||
probe.t.Helper()
|
||||
if !reflect.DeepEqual(request, probe.want) {
|
||||
probe.t.Fatalf("builder request = %#v, want independently owned %#v", request, probe.want)
|
||||
}
|
||||
options := request.Options["nested"].(map[string]any)
|
||||
options["value"] = "mutated"
|
||||
request.Options["items"].([]any)[0].(map[string]any)["value"] = "mutated"
|
||||
request.Options["bytes"].([]byte)[0] = 'x'
|
||||
slot := request.References.Slots["reference"]
|
||||
slot.Items[0].Content[0] = 'x'
|
||||
slot.Items = append(slot.Items, contracts.ReferenceItem{SlotName: "reference", Content: []byte("extra")})
|
||||
request.References.Slots["reference"] = slot
|
||||
delete(request.References.Slots, "unused")
|
||||
}
|
||||
|
||||
func constructionBuildRequest() BuildRequest {
|
||||
return BuildRequest{
|
||||
Options: map[string]any{
|
||||
"nested": map[string]any{"value": "original"},
|
||||
"items": []any{map[string]any{"value": "original"}},
|
||||
"bytes": []byte("original"),
|
||||
},
|
||||
References: contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{
|
||||
"reference": {Slot: contracts.ReferenceSlot{Name: "reference"}, Items: []contracts.ReferenceItem{{SlotName: "reference", Content: []byte("original")}}},
|
||||
"unused": {Slot: contracts.ReferenceSlot{Name: "unused"}},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareFailuresOccurBeforeInputParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user