Files
GraceSolutions 5e67d48f34 test(rules): add unit tests for variable expansion and engine helpers
Covers Expander.Expand / ExpandStrict behaviour for object, rule, custom,
and now sources, including error paths and template listing. Covers engine
helpers resolveBaseDN, resolveScope, collectAttributes, buildConditionGroups,
and parentDN.
2026-04-23 12:18:12 -04:00

138 lines
3.6 KiB
Go

package variables
import (
"strings"
"testing"
"time"
)
func newTestContext() *Context {
ctx := NewContext()
ctx.Now = time.Date(2025, 7, 14, 9, 30, 45, 0, time.UTC)
ctx.WithObject(map[string][]string{
"sAMAccountName": {"alice"},
"department": {"Sales"},
"mail": {"alice@example.com"},
})
ctx.WithRule("Onboard Users", "rule-123")
ctx.Custom["tenant"] = "acme"
return ctx
}
func TestExpand_ObjectAndRuleAndCustom(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
got, err := e.Expand("User {{object.sAMAccountName}} in {{object.department}} (rule {{rule.name}}, tenant {{custom.tenant}})", ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
want := "User alice in Sales (rule Onboard Users, tenant acme)"
if got != want {
t.Errorf("Expand mismatch:\n got: %q\nwant: %q", got, want)
}
}
func TestExpand_NowProperties(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
cases := map[string]string{
"{{now.utc_date}}": "2025-07-14",
"{{now.utc_time}}": "09:30:45",
"{{now.utc_datetime}}": "2025-07-14T09:30:45Z",
"{{now.year}}": "2025",
"{{now.month}}": "07",
"{{now.day}}": "14",
"{{now.hour}}": "09",
"{{now.minute}}": "30",
}
for tmpl, want := range cases {
got, err := e.Expand(tmpl, ctx)
if err != nil {
t.Errorf("%s: unexpected error: %v", tmpl, err)
continue
}
if got != want {
t.Errorf("%s => %q, want %q", tmpl, got, want)
}
}
}
func TestExpand_UnknownVariableReturnsErrorAndKeepsOriginal(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
got, err := e.Expand("hello {{object.missing}}", ctx)
if err == nil {
t.Fatal("expected error for missing object property")
}
if !strings.Contains(got, "{{object.missing}}") {
t.Errorf("expected original placeholder preserved, got %q", got)
}
}
func TestExpandStrict_FailsOnUnresolved(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
_, err := e.Expand("{{unknownSource.foo}}", ctx)
if err == nil {
t.Fatal("expected Expand to surface error for unknown source")
}
_, err = e.ExpandStrict("{{object.missing}}", ctx)
if err == nil {
t.Fatal("expected ExpandStrict to fail on missing property")
}
}
func TestExpand_NoVariables(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
got, err := e.Expand("plain text", ctx)
if err != nil || got != "plain text" {
t.Errorf("got (%q, %v), want (\"plain text\", nil)", got, err)
}
}
func TestExpand_InvalidPath(t *testing.T) {
e := NewExpander()
ctx := newTestContext()
_, err := e.Expand("{{object}}", ctx)
if err == nil {
t.Fatal("expected error for variable without source.property format")
}
}
func TestListVariables_DedupesAndPreservesOrder(t *testing.T) {
e := NewExpander()
got := e.ListVariables("{{object.a}} and {{object.b}} and {{object.a}}")
if len(got) != 2 {
t.Fatalf("expected 2 unique variables, got %d (%v)", len(got), got)
}
if got[0] != "{{object.a}}" || got[1] != "{{object.b}}" {
t.Errorf("unexpected order: %v", got)
}
}
func TestValidateTemplate_MismatchedDelimiters(t *testing.T) {
e := NewExpander()
warnings := e.ValidateTemplate("{{object.foo}")
if len(warnings) == 0 {
t.Fatal("expected a warning for mismatched delimiters")
}
if len(e.ValidateTemplate("{{object.foo}}")) != 0 {
t.Fatal("expected no warnings for balanced delimiters")
}
}
func TestWithObject_UsesFirstValue(t *testing.T) {
ctx := NewContext().WithObject(map[string][]string{
"memberOf": {"CN=A,DC=x", "CN=B,DC=x"},
})
if ctx.Object["memberOf"] != "CN=A,DC=x" {
t.Errorf("expected first value retained, got %q", ctx.Object["memberOf"])
}
}