Files
idsync/internal/ldaplib/integration_test.go
T
caowangrenbo 0fffdd5ccb test: 集成测试改用真 LLDAP 容器;修复两处物化缺陷
- 集成测试重构:syncer e2e/materialize、ldaplib 读路径、lldapgql 全改用本地
  LLDAP 容器(LDAP 3890 / GraphQL /api/graphql 17170),删旧 OpenLDAP 断言
- 修复 1:ActiveUsers 不填 DepartmentIDs(存于关系表),BuildFromMirror 拿到空成员;
  新增 ActiveUsersWithDepts,物化 plan 用之
- 修复 2:deleted 用户重新可见后不回 active,无法重建;RevertToActive 纳入 deleted,
  快照 reconcile 对其置回 active + 作废 token
- 关键安全回归单测保留并配变异验证:未确认不删、非受管不删、admin 永不移出、
  删除最后阶段、绝不碰密码。全量单测+vet+集成全绿
2026-09-29 23:53:24 +08:00

104 lines
2.7 KiB
Go

//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
}