package config import ( "reflect" "strings" "testing" ) func TestFinalizeArtifactDependenciesUnionsNormalizesAndIsIdempotent(t *testing.T) { cfg := dependencyTestPipeline([]string{" beta ", "alpha", "beta"}) artifact := cfg.Scriptorium.Artifacts["result"] artifact.Inputs["gamma"] = ScriptoriumInputConfig{Source: "narratio.artifact.gamma"} artifact.Inputs["alpha"] = ScriptoriumInputConfig{Source: "narratio.artifact.alpha"} cfg.Scriptorium.Artifacts["result"] = artifact cfg.Scriptorium.Artifacts["gamma"] = ScriptoriumArtifactConfig{} finalizeArtifactDependencies(cfg) want := []string{"alpha", "beta", "gamma"} got := cfg.Scriptorium.Artifacts["result"] if !reflect.DeepEqual(got.DependsOn, want) { t.Fatalf("dependencies = %#v, want %#v", got.DependsOn, want) } if len(got.dependencyOrigins["alpha"]) != 2 { t.Fatalf("alpha origins = %#v, want explicit and input origins", got.dependencyOrigins["alpha"]) } firstOrigins := cloneDependencyOrigins(got.dependencyOrigins) finalizeArtifactDependencies(cfg) got = cfg.Scriptorium.Artifacts["result"] if !reflect.DeepEqual(got.DependsOn, want) { t.Fatalf("dependencies after second finalization = %#v, want %#v", got.DependsOn, want) } if !reflect.DeepEqual(got.dependencyOrigins, firstOrigins) { t.Fatalf("origins changed after second finalization: %#v != %#v", got.dependencyOrigins, firstOrigins) } } func TestInferredArtifactDependencyDiagnosticsPointToInputs(t *testing.T) { for _, test := range []struct { name string artifacts map[string]ScriptoriumArtifactConfig want []string }{ { name: "missing", artifacts: map[string]ScriptoriumArtifactConfig{ "result": {Inputs: map[string]ScriptoriumInputConfig{"missing": {Source: "narratio.artifact.unknown"}}}, }, want: []string{"pipeline.scriptorium.artifacts.result.inputs.missing.source", "not configured"}, }, { name: "self", artifacts: map[string]ScriptoriumArtifactConfig{ "result": {Inputs: map[string]ScriptoriumInputConfig{"self": {Source: "narratio.artifact.result"}}}, }, want: []string{"pipeline.scriptorium.artifacts.result.inputs.self.source", "itself"}, }, { name: "cycle", artifacts: map[string]ScriptoriumArtifactConfig{ "alpha": {Inputs: map[string]ScriptoriumInputConfig{"beta": {Source: "narratio.artifact.beta"}}}, "beta": {Inputs: map[string]ScriptoriumInputConfig{"alpha": {Source: "narratio.artifact.alpha"}}}, }, want: []string{".inputs.", "dependencies must not contain cycles"}, }, } { t.Run(test.name, func(t *testing.T) { cfg := &PipelineConfig{Scriptorium: &ScriptoriumConfig{Artifacts: test.artifacts}} finalizeArtifactDependencies(cfg) err := ValidateScriptoriumArtifactDependencies(cfg.Scriptorium.Artifacts) if err == nil { t.Fatal("expected dependency validation error") } for _, fragment := range test.want { if !strings.Contains(err.Error(), fragment) { t.Fatalf("error = %q, want fragment %q", err, fragment) } } }) } } func TestExplicitAndInferredDependencyGraphsHaveEqualDigests(t *testing.T) { explicit := dependencyTestPipeline([]string{"alpha"}) inferred := dependencyTestPipeline(nil) for _, cfg := range []*PipelineConfig{explicit, inferred} { finalizeArtifactDependencies(cfg) cfg.resolution = &pipelineResolutionMetadata{} if err := recomputePipelineEffectiveDigest(cfg); err != nil { t.Fatal(err) } } if got, want := EffectivePipelineDigest(explicit), EffectivePipelineDigest(inferred); got != want { t.Fatalf("equivalent dependency graph digests differ: %s != %s", got, want) } } func dependencyTestPipeline(dependsOn []string) *PipelineConfig { return &PipelineConfig{Scriptorium: &ScriptoriumConfig{ Binary: "scriptorium", Artifacts: map[string]ScriptoriumArtifactConfig{ "alpha": {}, "beta": {}, "result": { DependsOn: dependsOn, Inputs: map[string]ScriptoriumInputConfig{ "alpha": {Source: "narratio.artifact.alpha"}, }, }, }, }} }