Skip to content

Commit

Permalink
test: more reference tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasjarosch committed Feb 15, 2024
1 parent 7caee81 commit 9aca97c
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 9 deletions.
12 changes: 6 additions & 6 deletions reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import (
"github.com/lukasjarosch/skipper/data"
)

// TODO: handle reference-to-reference
// TODO: PathReferences / KeyReferences
// TODO: handle cyclic references
// TODO: handle multiple references per path
// TODO: handle reference-to-reference with multiple references on the same path
// TODO: PathReferences which allow yaml keys to be references as well

var (
// ReferenceRegex defines the strings which are valid references
Expand All @@ -24,6 +20,7 @@ var (

ErrUndefinedReferenceTarget = fmt.Errorf("undefined reference target path")
ErrReferenceSourceIsNil = fmt.Errorf("reference source is nil")
ErrReferenceCycle = fmt.Errorf("reference cycles are not allowed")
)

// Reference is a reference to a value with a different path.
Expand Down Expand Up @@ -113,7 +110,7 @@ func ResolveReferences(references []Reference, resolveSource ReferenceSourceGett
return ref.TargetPath.String()
}

g := graph.New(referenceNodeHash, graph.Acyclic(), graph.Directed())
g := graph.New(referenceNodeHash, graph.Acyclic(), graph.Directed(), graph.PreventCycles())

// Register all references as nodes
var nodes []referenceVertex
Expand Down Expand Up @@ -151,6 +148,9 @@ func ResolveReferences(references []Reference, resolveSource ReferenceSourceGett

err = g.AddEdge(refNode.TargetPath.String(), n.TargetPath.String())
if err != nil {
if errors.Is(err, graph.ErrEdgeCreatesCycle) {
return nil, fmt.Errorf("reference %s -> %s would introduce cycle: %w", refNode.Name(), refDep.Name(), ErrReferenceCycle)
}
return nil, err
}
}
Expand Down
60 changes: 59 additions & 1 deletion reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func TestResolveReferencesChained(t *testing.T) {
expected := []Reference{
{
Path: data.NewPath("chained.gotcha"),
TargetPath: data.NewPath("chained.john.name"),
TargetPath: data.NewPath("chained.john.first_name"),
},
{
Path: data.NewPath("chained.name_placeholder"),
Expand All @@ -168,3 +168,61 @@ func TestResolveReferencesChained(t *testing.T) {

assert.Equal(t, resolved, expected)
}

func TestResolveReferencesCycle(t *testing.T) {
class, err := NewClass("testdata/references/local/cycle.yaml", codec.NewYamlCodec(), data.NewPathFromOsPath("cycle"))
assert.NoError(t, err)

references, err := ParseReferences(class)
assert.NoError(t, err)
assert.NotNil(t, references)

resolved, err := ResolveReferences(references, class)
assert.ErrorIs(t, err, ErrReferenceCycle)
assert.Len(t, resolved, 0)
}

func TestResolveReferencesMulti(t *testing.T) {
class, err := NewClass("testdata/references/local/multi.yaml", codec.NewYamlCodec(), data.NewPathFromOsPath("multi"))
assert.NoError(t, err)

expected := []Reference{
{
Path: data.NewPath("multi.project.description"),
TargetPath: data.NewPath("project.name"),
},
{
Path: data.NewPath("multi.project.description"),
TargetPath: data.NewPath("multi.project.name"),
},
{
Path: data.NewPath("multi.project.description"),
TargetPath: data.NewPath("project.name"),
},
{
Path: data.NewPath("multi.project.description"),
TargetPath: data.NewPath("multi.project.repo"),
},
{
Path: data.NewPath("multi.project.repo"),
TargetPath: data.NewPath("multi.common.repo_url"),
},
}

references, err := ParseReferences(class)
assert.NoError(t, err)
assert.NotNil(t, references)
assert.Len(t, references, len(expected))

resolved, err := ResolveReferences(references, class)
assert.NoError(t, err)
assert.Len(t, resolved, len(expected))

// The TopologicalSort does always produce a correct
// result, but the order still may change as there
// is usually more than one solution.
// Hence we only check that the sincle nested reference
// is properly resolved. Here the 'multi.common.repo_url'
// MUST be the first reference to be resolved.
assert.Equal(t, resolved[0], expected[4])
}
5 changes: 3 additions & 2 deletions testdata/references/local/chained.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
chained:
greeting: Hello, ${first_name}
gotcha: ${chained:john:name}
gotcha: ${chained:john:first_name}
john:
name: John
first_name: John
last_name: Doe
first_name: ${name_placeholder}
name_placeholder: ${gotcha}
4 changes: 4 additions & 0 deletions testdata/references/local/cycle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cycle:
john: ${middle}
name: ${john}
middle: ${name}
9 changes: 9 additions & 0 deletions testdata/references/local/multi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
multi:
common:
repo_url: "github.com/lukasjarosch/skipper"
project:
name: "skipper"
repo: "${multi:common:repo_url}"
description: >
The ${project:name} project is very cool. Because ${multi:project:name} helps in working with the Infrastructure as Data concept.
The project '${project:name}' is hosted on ${multi:project:repo}.

0 comments on commit 9aca97c

Please sign in to comment.