Skip to content

Commit

Permalink
Add unit tests for buildOneOfExpressions (#208)
Browse files Browse the repository at this point in the history
* Add unit tests for buildOneOfExpressions

* Add missed changes and bypass linter error
  • Loading branch information
jaredoconnell committed Sep 13, 2024
1 parent 1a163a3 commit 166a6a9
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 6 deletions.
21 changes: 15 additions & 6 deletions workflow/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,31 @@ const YamlOneOfTag = "!oneof"

func buildOneOfExpressions(data yaml.Node, path []string) (any, error) {
if data.Type() != yaml.TypeIDMap {
return nil, fmt.Errorf("!oneof found on non-map node at %s; expected a map with a list of options and the discriminator ", strings.Join(path, " -> "))
return nil, fmt.Errorf(
"!oneof found on non-map node at %s; expected a map with a list of options and the discriminator ",
strings.Join(path, " -> "))
}
discriminatorNode, found := data.MapKey(YamlDiscriminatorKey)
if !found {
return nil, fmt.Errorf("key %q not present within !oneof at %q", YamlDiscriminatorKey, strings.Join(path, " -> "))
return nil, fmt.Errorf("key %q not present within %s at %q",
YamlDiscriminatorKey, YamlOneOfTag, strings.Join(path, " -> "))
}
if discriminatorNode.Type() != yaml.TypeIDString {
return nil, fmt.Errorf("%q within !oneof should be a string; got %s", discriminatorNode.Type(), YamlDiscriminatorKey)
return nil, fmt.Errorf("%q within %s should be a string; got %s",
YamlDiscriminatorKey, YamlOneOfTag, discriminatorNode.Type())
}
discriminator := discriminatorNode.Value()
if len(discriminator) == 0 {
return nil, fmt.Errorf("%q within %s is empty", YamlDiscriminatorKey, YamlOneOfTag)
}
oneOfOptionsNode, found := data.MapKey(YamlOneOfKey)
if !found {
return nil, fmt.Errorf("key %q not present within !oneof at %q", YamlOneOfKey, strings.Join(path, " -> "))
return nil, fmt.Errorf("key %q not present within %s at %q",
YamlOneOfKey, YamlOneOfTag, strings.Join(path, " -> "))
}
if oneOfOptionsNode.Type() != yaml.TypeIDMap {
return nil, fmt.Errorf("%q within !oneof should be a map; got %s", YamlOneOfKey, discriminatorNode.Type())
return nil, fmt.Errorf("%q within %q should be a map; got %s",
YamlOneOfKey, YamlOneOfTag, discriminatorNode.Type())
}
options := map[string]any{}
for _, optionNodeKey := range oneOfOptionsNode.MapKeys() {
Expand All @@ -87,7 +97,6 @@ func buildOneOfExpressions(data yaml.Node, path []string) (any, error) {
}
}

discriminator := discriminatorNode.Value()
return &infer.OneOfExpression{
Discriminator: discriminator,
Options: options,
Expand Down
105 changes: 105 additions & 0 deletions workflow/yaml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package workflow //nolint:testpackage // Tests internal functions for unit testing.

import (
"go.arcalot.io/assert"
"go.arcalot.io/lang"
"go.flow.arcalot.io/engine/internal/infer"
"go.flow.arcalot.io/engine/internal/yaml"
"go.flow.arcalot.io/expressions"
"testing"
)

func TestBuildOneOfExpression_Simple(t *testing.T) {
yamlInput := []byte(`
!oneof
discriminator: d
one_of:
a: !expr some_expr
b: !expr some_other_expr
`)
input := assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
result, err := buildOneOfExpressions(input, make([]string, 0))
assert.NoError(t, err)
assert.InstanceOf[*infer.OneOfExpression](t, result)
oneofResult := result.(*infer.OneOfExpression)
assert.Equals(t, oneofResult.Discriminator, "d")
assert.Equals(t, oneofResult.Options, map[string]any{
"a": lang.Must2(expressions.New("some_expr")),
"b": lang.Must2(expressions.New("some_other_expr")),
})
}

func TestBuildOneOfExpression_InputValidation(t *testing.T) {
// Not a map
yamlInput := []byte(`
!oneof "thisisastring"`)
input := assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err := buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "expected a map")

// Wrong discriminator key
yamlInput = []byte(`
!oneof
wrong_key: ""
one_of: {}`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err = buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "key \""+YamlDiscriminatorKey+"\" not present")

// Discriminator not a string
yamlInput = []byte(`
!oneof
discriminator: {}
one_of: {}`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err = buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "should be a string")

// Empty discriminator
yamlInput = []byte(`
!oneof
discriminator: ""
one_of: {}`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err = buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "is empty")

// Missing or wrong oneof key
yamlInput = []byte(`
!oneof
discriminator: "valid"
one_of_wrong: {}`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err = buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "key \"one_of\" not present")

// Wrong type for oneof options node
yamlInput = []byte(`
!oneof
discriminator: "valid"
one_of: wrong`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
_, err = buildOneOfExpressions(input, make([]string, 0))
assert.Error(t, err)
assert.Contains(t, err.Error(), "should be a map")

// Non-object type as option in one_of section.
yamlInput = []byte(`
!oneof
discriminator: "valid"
one_of:
a: test`)
input = assert.NoErrorR[yaml.Node](t)(yaml.New().Parse(yamlInput))
oneofResult, err := buildOneOfExpressions(input, make([]string, 0))
assert.NoError(t, err)
assert.InstanceOf[*infer.OneOfExpression](t, oneofResult)
_, err = oneofResult.(*infer.OneOfExpression).Type(nil, nil, nil)
assert.Error(t, err)
assert.Contains(t, err.Error(), "not an object")

}

0 comments on commit 166a6a9

Please sign in to comment.