//go:build integration // LLDAP 读路径(LDAP)集成测试:读取对象重,验证 objectClass 命中与双成员属性读取。 // // 默认不参与 `go test ./...`,需显式启用: // // LLDAP_TEST_URL=ldap://127.0.0.1:3890 go test -tags=integration ./internal/ldaplib/ -v package ldaplib_test import ( "io" "log/slog" "os" "testing" "idsync/internal/config" "idsync/internal/ldaplib" ) func discardLogger() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) } func testConfig() config.Ldap { base := os.Getenv("LDAP_TEST_BASE_DN") if base == "" { base = "dc=example,dc=com" } url := os.Getenv("LDAP_TEST_URL") if url == "" { url = "ldap://127.0.0.1:3890" } return config.Ldap{ URL: url, BaseDN: base, AdminDN: "uid=admin,ou=people," + base, AdminPass: "adminpass", TimeoutSec: 10, } } func dialT(t *testing.T) *ldaplib.Conn { t.Helper() c, err := ldaplib.NewClient(testConfig(), discardLogger()) if err != nil { t.Fatalf("构造客户端失败: %v", err) } conn, err := c.Dial() if err != nil { t.Skipf("LLDAP 不可用,跳过集成测试: %v", err) } return conn } // TestIntegrationReadUsers 验证用户读视图:LLDAP 默认 admin 用户应能被 ListUsers 读到。 func TestIntegrationReadUsers(t *testing.T) { conn := dialT(t) defer conn.Close() users, err := conn.ListUsers("ou=people,dc=example,dc=com") if err != nil { t.Fatalf("ListUsers 失败: %v", err) } if _, ok := users["admin"]; !ok { t.Errorf("LLDAP 默认的 admin 用户应在 ou=people 读到;现有: %v", keysOf(users)) } } // TestIntegrationReadGroups 验证组读视图:能列出 ou=groups 下的 LLDAP 默认组。 func TestIntegrationReadGroups(t *testing.T) { conn := dialT(t) defer conn.Close() groups, err := conn.ListGroups("ou=groups,dc=example,dc=com") if err != nil { t.Fatalf("ListGroups 失败: %v", err) } if len(groups) == 0 { t.Errorf("ou=groups 下应至少有 LLDAP 默认组") } // 结构上应能解析成员集合(即便为空),确认 member/uniqueMember 双读不崩。 for dn := range groups { _ = groups[dn].Members() } } // TestIntegrationDNHelpers 验证 DN 构造与过滤器转义(供读侧比对复用)。 func TestIntegrationDNHelpers(t *testing.T) { if got := ldaplib.UserDN("alice", "ou=people,dc=example,dc=com"); got != "uid=alice,ou=people,dc=example,dc=com" { t.Errorf("UserDN = %q", got) } if got := ldaplib.GroupDN("od-1", "ou=groups,dc=example,dc=com"); got != "cn=od-1,ou=groups,dc=example,dc=com" { t.Errorf("GroupDN = %q", got) } if ldaplib.EscapeFilter("a(b)") == "a(b)" { t.Errorf("EscapeFilter 未转义") } } func keysOf[V any](m map[string]V) []string { out := make([]string, 0, len(m)) for k := range m { out = append(out, k) } return out }