package engine import ( "testing" "github.com/Grace-Solutions/OrchestrAD/internal/models" ldapv3 "github.com/go-ldap/ldap/v3" ) func strPtr(s string) *string { return &s } func TestResolveBaseDN(t *testing.T) { conn := &models.ADConnection{RootDN: "DC=example,DC=com"} if got := resolveBaseDN(&models.Rule{}, conn); got != "DC=example,DC=com" { t.Errorf("expected connection RootDN fallback, got %q", got) } override := "OU=Staff,DC=example,DC=com" rule := &models.Rule{BaseDNOverride: &override} if got := resolveBaseDN(rule, conn); got != override { t.Errorf("expected rule override, got %q", got) } empty := "" rule = &models.Rule{BaseDNOverride: &empty} if got := resolveBaseDN(rule, conn); got != "DC=example,DC=com" { t.Errorf("empty override should fall back to connection, got %q", got) } if got := resolveBaseDN(&models.Rule{}, nil); got != "" { t.Errorf("nil connection should yield empty string, got %q", got) } } func TestResolveScope(t *testing.T) { conn := &models.ADConnection{DefaultSearchScope: "OneLevel"} if got := resolveScope(&models.Rule{}, conn); got != ldapv3.ScopeSingleLevel { t.Errorf("expected OneLevel from connection, got %d", got) } rule := &models.Rule{SearchScopeOverride: strPtr("Base")} if got := resolveScope(rule, conn); got != ldapv3.ScopeBaseObject { t.Errorf("expected Base from override, got %d", got) } rule = &models.Rule{SearchScopeOverride: strPtr("Subtree")} if got := resolveScope(rule, conn); got != ldapv3.ScopeWholeSubtree { t.Errorf("Subtree override should produce WholeSubtree, got %d", got) } if got := resolveScope(&models.Rule{}, nil); got != ldapv3.ScopeWholeSubtree { t.Errorf("empty inputs should default to WholeSubtree, got %d", got) } } func TestCollectAttributes_IncludesDefaultsAndConditionNames(t *testing.T) { rule := &models.Rule{ ConditionGroups: []models.RuleConditionGroup{ { Conditions: []models.RuleCondition{ {AttributeName: "department"}, {AttributeName: "title"}, {AttributeName: ""}, }, }, }, } attrs := collectAttributes(rule) needs := []string{"dn", "cn", "objectClass", "sAMAccountName", "member", "memberOf", "department", "title"} for _, want := range needs { if !contains(attrs, want) { t.Errorf("expected attributes to contain %q, got %v", want, attrs) } } if contains(attrs, "") { t.Errorf("empty attribute name should not be collected, got %v", attrs) } } func TestBuildConditionGroups_FiltersDisabled(t *testing.T) { val := "Sales" custom := "(memberOf=CN=VIP,DC=x)" rule := &models.Rule{ ConditionGroups: []models.RuleConditionGroup{ { IsEnabled: true, JoinOperator: "AND", Negate: true, Conditions: []models.RuleCondition{ {IsEnabled: true, AttributeName: "department", Operator: "Equals", ComparisonValue: &val}, {IsEnabled: false, AttributeName: "title", Operator: "Equals"}, {IsEnabled: true, Operator: "Custom", CustomLdapExpression: &custom}, }, }, {IsEnabled: false, Conditions: []models.RuleCondition{{IsEnabled: true, AttributeName: "x"}}}, }, } groups := buildConditionGroups(rule) if len(groups) != 1 { t.Fatalf("expected 1 enabled group, got %d", len(groups)) } g := groups[0] if !g.Negate || string(g.JoinOperator) != "AND" { t.Errorf("unexpected group flags: negate=%v op=%q", g.Negate, g.JoinOperator) } if len(g.Conditions) != 2 { t.Fatalf("expected 2 enabled conditions, got %d", len(g.Conditions)) } if g.Conditions[0].Attribute != "department" || g.Conditions[0].Value != "Sales" { t.Errorf("first condition mismatch: %+v", g.Conditions[0]) } if g.Conditions[1].CustomLdap != custom { t.Errorf("expected custom expression preserved, got %q", g.Conditions[1].CustomLdap) } } func TestParentDN(t *testing.T) { cases := map[string]string{ "CN=Alice,OU=Staff,DC=example,DC=com": "OU=Staff,DC=example,DC=com", "CN=Alice, OU=Staff,DC=example,DC=com": "OU=Staff,DC=example,DC=com", "DC=com": "", "": "", } for input, want := range cases { if got := parentDN(input); got != want { t.Errorf("parentDN(%q) = %q, want %q", input, got, want) } } } func contains(s []string, v string) bool { for _, x := range s { if x == v { return true } } return false }