From cce044325b898ea3a7445744e13173bcc3ffa615 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Tue, 30 Sep 2025 22:09:08 +0200 Subject: [PATCH 1/4] feat: add local directory server for tests through containers --- .github/workflows/pr.yml | 3 + Makefile | 96 +++++++++++++++------------ testdata/01_Organizational_Units.ldif | 7 ++ testdata/02_Groups.ldif | 7 ++ testdata/02_People.ldif | 21 ++++++ v3/ldap_test.go | 18 ++--- 6 files changed, 101 insertions(+), 51 deletions(-) create mode 100755 testdata/01_Organizational_Units.ldif create mode 100644 testdata/02_Groups.ldif create mode 100644 testdata/02_People.ldif diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3c283d1d..b6f49806 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -24,6 +24,9 @@ jobs: - name: Version run: go version + - name: Setup local directory server + run: make local-server + - name: Build, Validate, and Test run: | cd ${{ matrix.directory }} diff --git a/Makefile b/Makefile index 0a414a0e..23b6bc5f 100644 --- a/Makefile +++ b/Makefile @@ -1,56 +1,58 @@ -.PHONY: default install build test quicktest fmt vet lint +.PHONY: default install build test quicktest fmt vet lint -# List of all release tags "supported" by our current Go version -# E.g. ":go1.1:go1.2:go1.3:go1.4:go1.5:go1.6:go1.7:go1.8:go1.9:go1.10:go1.11:go1.12:" -GO_RELEASE_TAGS := $(shell go list -f ':{{join (context.ReleaseTags) ":"}}:' runtime) +default: fmt vet lint build quicktest -# Only use the `-race` flag on newer versions of Go (version 1.3 and newer) -ifeq (,$(findstring :go1.3:,$(GO_RELEASE_TAGS))) - RACE_FLAG := -else - RACE_FLAG := -race -cpu 1,2,4 +CONTAINER_CMD := $(shell command -v podman 2>/dev/null) +ifeq ($(CONTAINER_CMD),) + CONTAINER_CMD := $(shell command -v docker 2>/dev/null) endif -# Run `go vet` on Go 1.12 and newer. For Go 1.5-1.11, use `go tool vet` -ifneq (,$(findstring :go1.12:,$(GO_RELEASE_TAGS))) - GO_VET := go vet \ - -atomic \ - -bool \ - -copylocks \ - -nilfunc \ - -printf \ - -rangeloops \ - -unreachable \ - -unsafeptr \ - -unusedresult \ - . -else ifneq (,$(findstring :go1.5:,$(GO_RELEASE_TAGS))) - GO_VET := go tool vet \ - -atomic \ - -bool \ - -copylocks \ - -nilfunc \ - -printf \ - -shadow \ - -rangeloops \ - -unreachable \ - -unsafeptr \ - -unusedresult \ - . -else - GO_VET := @echo "go vet skipped -- not supported on this version of Go" +# Check if we found either command +ifeq ($(CONTAINER_CMD),) + $(error Neither podman nor docker found in PATH) endif -default: fmt vet lint build quicktest - install: go get -t -v ./... build: go build -v ./... -test: - go test -v $(RACE_FLAG) -cover ./... +LDAP_ADMIN_DN := cn=admin,dc=example,dc=com +LDAP_ADMIN_PASSWORD := admin123 +LDAP_BASE_DN := dc=example,dc=com +LDAP_URL := ldap://127.0.0.1:389 +CONTAINER_NAME := go-ldap-test + +local-server: + -$(CONTAINER_CMD) rm -f -t 10 $(CONTAINER_NAME) + $(CONTAINER_CMD) \ + run \ + --rm \ + -d \ + --name $(CONTAINER_NAME) \ + --hostname "$(CONTAINER_NAME).example.com" \ + -p "127.0.0.1:3389:389" \ + -p "127.0.0.1:3636:636" \ + -e LDAP_ORGANISATION="Example Inc" \ + -e LDAP_DOMAIN="example.com" \ + -e LDAP_ADMIN_PASSWORD="$(LDAP_ADMIN_PASSWORD)" \ + -e LDAP_TLS_VERIFY_CLIENT="never" \ + docker.io/osixia/openldap:1.5.0 + + @echo "Waiting for LDAP server to be ready..." + @$(CONTAINER_CMD) exec $(CONTAINER_NAME) /bin/sh -c 'until ldapsearch -x -H $(LDAP_URL) -b "$(LDAP_BASE_DN)" -D "$(LDAP_ADMIN_DN)" -w $(LDAP_ADMIN_PASSWORD) > /dev/null 2>&1; do echo "LDAP server not ready yet, waiting..."; sleep 2; done' + @echo "LDAP server is ready!" + + @echo "Configuring anonymous access..." + @$(CONTAINER_CMD) exec $(CONTAINER_NAME) /bin/sh -c 'echo "dn: olcDatabase={1}mdb,cn=config\nchangetype: modify\nreplace: olcAccess\nolcAccess: {0}to * by * read" | ldapmodify -Y EXTERNAL -H ldapi:///' + + $(CONTAINER_CMD) cp -a ./testdata $(CONTAINER_NAME):/ + @echo "Loading LDIF files..." + @$(CONTAINER_CMD) exec $(CONTAINER_NAME) /bin/sh -c 'for file in /testdata/*.ldif; do echo "Processing $$file..."; cat "$$file" | ldapadd -v -x -H $(LDAP_URL) -D "$(LDAP_ADMIN_DN)" -w $(LDAP_ADMIN_PASSWORD); done' + +delete-container: + -$(CONTAINER_CMD) rm -f -t 10 $(CONTAINER_NAME) quicktest: go test ./... @@ -71,7 +73,17 @@ fmt: fi vet: - $(GO_VET) + go vet \ + -atomic \ + -bool \ + -copylocks \ + -nilfunc \ + -printf \ + -rangeloops \ + -unreachable \ + -unsafeptr \ + -unusedresult \ + ./... # https://github.com/golang/lint # go get github.com/golang/lint/golint diff --git a/testdata/01_Organizational_Units.ldif b/testdata/01_Organizational_Units.ldif new file mode 100755 index 00000000..4ec72af7 --- /dev/null +++ b/testdata/01_Organizational_Units.ldif @@ -0,0 +1,7 @@ +dn: ou=people,dc=example,dc=com +objectClass: organizationalUnit +ou: people + +dn: ou=groups,dc=example,dc=com +objectClass: organizationalUnit +ou: groups diff --git a/testdata/02_Groups.ldif b/testdata/02_Groups.ldif new file mode 100644 index 00000000..f0962f85 --- /dev/null +++ b/testdata/02_Groups.ldif @@ -0,0 +1,7 @@ +dn: cn=DistributionList,ou=groups,dc=example,dc=com +objectClass: groupOfNames +objectClass: top +cn: DistributionList +member: +member: cn=DoeJo,ou=people,dc=example,dc=com +member: cn=MustermannMa,ou=people,dc=example,dc=com diff --git a/testdata/02_People.ldif b/testdata/02_People.ldif new file mode 100644 index 00000000..131e76ed --- /dev/null +++ b/testdata/02_People.ldif @@ -0,0 +1,21 @@ +dn: cn=DoeJo,ou=people,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: DoeJo +sn: Doe +displayName: Doe, John +givenName: John +mail: John.Doe@example.com + +dn: cn=MustermannMa,ou=people,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: MustermannMa +sn: Mustermann +displayName: Mustermann, Max +givenName: Max +mail: Max.Mustermann@example.com diff --git a/v3/ldap_test.go b/v3/ldap_test.go index 5b96e039..ce83b278 100644 --- a/v3/ldap_test.go +++ b/v3/ldap_test.go @@ -10,16 +10,16 @@ import ( ) const ( - ldapServer = "ldap://ldap.itd.umich.edu:389" - ldapsServer = "ldaps://ldap.itd.umich.edu:636" - baseDN = "dc=umich,dc=edu" + ldapServer = "ldap://127.0.0.1:3389" + ldapsServer = "ldaps://127.0.0.1:3636" + baseDN = "dc=example,dc=com" ) var filter = []string{ - "(cn=cis-fac)", - "(&(owner=*)(cn=cis-fac))", - "(&(objectclass=rfc822mailgroup)(cn=*Computer*))", - "(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))", + "(cn=DoeJo)", + "(&(sn=Doe)(givenName=John))", + "(|(sn=Doe)(givenName=Max*))", + "(objectClass=inetOrgPerson)", } var attributes = []string{ @@ -258,9 +258,9 @@ func TestCompare(t *testing.T) { } defer l.Close() - const dn = "cn=math mich,ou=User Groups,ou=Groups,dc=umich,dc=edu" + const dn = "cn=DoeJo,ou=people,dc=example,dc=com" const attribute = "cn" - const value = "math mich" + const value = "DoeJo" sr, err := l.Compare(dn, attribute, value) if err != nil { From eb131e89e8ce070b99fa83330f6062a951eb951f Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 1 Oct 2025 15:26:01 +0200 Subject: [PATCH 2/4] feat: add more tests and update the README.md --- .githooks/pre-push | 6 --- Makefile | 12 +++--- README.md | 26 +++++++----- v3/add.go | 6 +++ v3/add_test.go | 62 +++++++++++++++++++++++++++++ v3/bind_test.go | 68 +++++++++++++++++++++++++++++++ v3/del.go | 6 +++ v3/del_test.go | 97 +++++++++++++++++++++++++++++++++++++++++++++ v3/extended.go | 6 +++ v3/extended_test.go | 35 +++++++++------- v3/ldap_test.go | 12 ++++++ v3/unbind_test.go | 32 +++++++++++++++ v3/whoami_test.go | 27 +++++++++++++ 13 files changed, 360 insertions(+), 35 deletions(-) delete mode 100755 .githooks/pre-push create mode 100644 v3/add_test.go create mode 100644 v3/bind_test.go create mode 100644 v3/del_test.go create mode 100644 v3/unbind_test.go create mode 100644 v3/whoami_test.go diff --git a/.githooks/pre-push b/.githooks/pre-push deleted file mode 100755 index 4325ee31..00000000 --- a/.githooks/pre-push +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -# install from the root of the repo with: -# ln -s ../../.githooks/pre-push .git/hooks/pre-push - -make vet fmt lint \ No newline at end of file diff --git a/Makefile b/Makefile index 23b6bc5f..62b179fd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -.PHONY: default install build test quicktest fmt vet lint +.PHONY: default install build test test fmt vet lint -default: fmt vet lint build quicktest +default: fmt vet lint build test CONTAINER_CMD := $(shell command -v podman 2>/dev/null) ifeq ($(CONTAINER_CMD),) @@ -13,7 +13,7 @@ ifeq ($(CONTAINER_CMD),) endif install: - go get -t -v ./... + go get -t -x ./... build: go build -v ./... @@ -51,11 +51,11 @@ local-server: @echo "Loading LDIF files..." @$(CONTAINER_CMD) exec $(CONTAINER_NAME) /bin/sh -c 'for file in /testdata/*.ldif; do echo "Processing $$file..."; cat "$$file" | ldapadd -v -x -H $(LDAP_URL) -D "$(LDAP_ADMIN_DN)" -w $(LDAP_ADMIN_PASSWORD); done' -delete-container: +stop-local-server: -$(CONTAINER_CMD) rm -f -t 10 $(CONTAINER_NAME) -quicktest: - go test ./... +test: + go test -v ./... fuzz: go test -fuzz=FuzzParseDN -fuzztime=600s . diff --git a/README.md b/README.md index 5d7f2fb2..3a6a35f4 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,20 @@ The library implements the following specifications: ## Features: - Connecting to LDAP server (non-TLS, TLS, STARTTLS, through a custom dialer) -- Binding to LDAP server (Simple Bind, GSSAPI, SASL) +- Bind Requests / Responses (Simple Bind, GSSAPI, SASL) - "Who Am I" Requests / Responses -- Searching for entries (normal and asynchronous) -- Filter Compile / Decompile -- Paging Search Results +- Search Requests / Responses (normal, paging and asynchronous) - Modify Requests / Responses - Add Requests / Responses - Delete Requests / Responses - Modify DN Requests / Responses +- Unbind Requests / Responses +- Password Modify Requests / Responses +- Content Synchronization Requests / Responses +- LDAPv3 Filter Compile / Decompile +- Server Side Sorting of Search Results +- LDAPv3 Extended Operations +- LDAPv3 Control Support ## Go Modules: @@ -36,13 +41,16 @@ Bug reports and pull requests are welcome! Before submitting a pull request, please make sure tests and verification scripts pass: ``` -make all -``` +# Setup local directory server using Docker or Podman +make local-server -To set up a pre-push hook to run the tests and verify scripts before pushing: +# Run gofmt, go vet and go test +cd ./v3 +make -f ../Makefile -``` -ln -s ../../.githooks/pre-push .git/hooks/pre-push +# (Optionally) Stop and delete the directory server container afterwards +cd .. +make stop-local-server ``` --- diff --git a/v3/add.go b/v3/add.go index ab32b0b6..6d8854e0 100644 --- a/v3/add.go +++ b/v3/add.go @@ -1,7 +1,9 @@ package ldap import ( + "errors" "fmt" + ber "github.com/go-asn1-ber/asn1-ber" ) @@ -66,6 +68,10 @@ func NewAddRequest(dn string, controls []Control) *AddRequest { // Add performs the given AddRequest func (l *Conn) Add(addRequest *AddRequest) error { + if addRequest == nil { + return NewError(ErrorNetwork, errors.New("AddRequest cannot be nil")) + } + msgCtx, err := l.doRequest(addRequest) if err != nil { return err diff --git a/v3/add_test.go b/v3/add_test.go new file mode 100644 index 00000000..c09aa642 --- /dev/null +++ b/v3/add_test.go @@ -0,0 +1,62 @@ +package ldap + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConn_Add(t *testing.T) { + l, err := getTestConnection(true) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + dn := "cn=new_user,ou=people,dc=example,dc=com" + // Delete the entry if it already exists from previous test runs + _ = l.Del(NewDelRequest(dn, nil)) + + t.Run("create entry", func(t *testing.T) { + err := l.Add(&AddRequest{ + DN: dn, + Attributes: []Attribute{ + { + Type: "objectClass", + Vals: []string{"top", "person", "organizationalPerson", "inetOrgPerson"}, + }, + { + Type: "cn", + Vals: []string{"testuser"}, + }, + { + Type: "givenName", + Vals: []string{"Test User"}, + }, + { + Type: "sn", + Vals: []string{"Dummy"}, + }, + }, + }) + assert.NoError(t, err) + }) + t.Run("create entry with no attributes", func(t *testing.T) { + err := l.Add(&AddRequest{ + DN: dn, + Attributes: nil, + }) + assert.Error(t, err) + assert.Truef(t, IsErrorWithCode(err, LDAPResultProtocolError), "Expected LDAPResultProtocolError, got %v", err) + }) + t.Run("empty AddRequest", func(t *testing.T) { + err := l.Add(&AddRequest{}) + assert.Error(t, err) + }) + t.Run("nil AddRequest", func(t *testing.T) { + err := l.Add(nil) + fmt.Println("expected AddRequest, got nil") + assert.Error(t, err) + }) +} diff --git a/v3/bind_test.go b/v3/bind_test.go new file mode 100644 index 00000000..b764149d --- /dev/null +++ b/v3/bind_test.go @@ -0,0 +1,68 @@ +package ldap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConn_Bind(t *testing.T) { + l, err := getTestConnection(false) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + tests := []struct { + name string + dn string + password string + wantError bool + errorCode uint16 + }{ + { + name: "invalid credentials", + dn: "cn=admin," + baseDN, + password: "AAAAAAAAAA", + wantError: true, + errorCode: LDAPResultInvalidCredentials, + }, + { + name: "no credentials", + dn: "", + password: "", + wantError: true, + errorCode: ErrorEmptyPassword, + }, + { + name: "valid credentials", + dn: "cn=admin," + baseDN, + password: "admin123", + wantError: false, + errorCode: LDAPResultSuccess, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := l.Bind(tt.dn, tt.password) + if tt.wantError { + assert.Error(t, err) + assert.Truef(t, IsErrorWithCode(err, tt.errorCode), "Expected error code %v, got %d", tt.errorCode, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestConn_UnauthenticatedBind(t *testing.T) { + l, err := getTestConnection(false) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + err = l.UnauthenticatedBind("cn=admin," + baseDN) + assert.Error(t, err) + assert.Truef(t, IsErrorWithCode(err, LDAPResultUnwillingToPerform), "Expected LDAPResultUnwillingToPerform, got %v", err) +} diff --git a/v3/del.go b/v3/del.go index 62306951..cb7a683f 100644 --- a/v3/del.go +++ b/v3/del.go @@ -1,7 +1,9 @@ package ldap import ( + "errors" "fmt" + ber "github.com/go-asn1-ber/asn1-ber" ) @@ -35,6 +37,10 @@ func NewDelRequest(DN string, Controls []Control) *DelRequest { // Del executes the given delete request func (l *Conn) Del(delRequest *DelRequest) error { + if delRequest == nil { + return NewError(ErrorNetwork, errors.New("DelRequest cannot be nil")) + } + msgCtx, err := l.doRequest(delRequest) if err != nil { return err diff --git a/v3/del_test.go b/v3/del_test.go new file mode 100644 index 00000000..6b99ec51 --- /dev/null +++ b/v3/del_test.go @@ -0,0 +1,97 @@ +package ldap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConn_Del(t *testing.T) { + l, err := getTestConnection(true) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + dn := "cn=testuser,ou=people,dc=example,dc=com" + + // Remove the entry if it exists from previous test runs + _ = l.Del(NewDelRequest(dn, nil)) + + assert.NoError(t, l.Add(&AddRequest{ + DN: dn, + Attributes: []Attribute{ + { + Type: "objectClass", + Vals: []string{"top", "person", "organizationalPerson", "inetOrgPerson"}, + }, + { + Type: "cn", + Vals: []string{"testuser"}, + }, + { + Type: "givenName", + Vals: []string{"Test User"}, + }, + { + Type: "sn", + Vals: []string{"Dummy"}, + }, + }, + })) + t.Logf("Added user") + + tests := []struct { + name string + dn string + wantErr bool + errorCode uint16 + }{ + { + name: "empty DN", + dn: "", + wantErr: true, + errorCode: LDAPResultUnwillingToPerform, + }, + { + name: "invalid DN", + dn: "AAAAAAAAAAAAAAAAAA", + wantErr: true, + errorCode: LDAPResultInvalidDNSyntax, + }, + { + name: "delete user", + dn: dn, + wantErr: false, + }, + { + name: "delete entry with children", + dn: "ou=people," + baseDN, + wantErr: true, + errorCode: LDAPResultNotAllowedOnNonLeaf, + }, + { + name: "delete non existing entry", + dn: "ou=nonexisting," + baseDN, + wantErr: true, + errorCode: LDAPResultNoSuchObject, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + delRequest := NewDelRequest(tt.dn, nil) + err := l.Del(delRequest) + if tt.wantErr && err != nil { + assert.Error(t, err) + assert.Truef(t, IsErrorWithCode(err, tt.errorCode), "Expected error with code %d, got %d", tt.errorCode, err) + } else { + assert.NoError(t, err) + } + }) + } + + t.Run("nil DelRequest", func(t *testing.T) { + err := l.Del(nil) + assert.Error(t, err) + }) +} diff --git a/v3/extended.go b/v3/extended.go index e71d982f..921238fe 100644 --- a/v3/extended.go +++ b/v3/extended.go @@ -1,7 +1,9 @@ package ldap import ( + "errors" "fmt" + ber "github.com/go-asn1-ber/asn1-ber" ) @@ -56,6 +58,10 @@ type ExtendedResponse struct { // Extended performs an extended request. The resulting // ExtendedResponse may return a value in the form of a *ber.Packet func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { + if er == nil { + return nil, NewError(ErrorNetwork, errors.New("ExtendedRequest cannot be nil")) + } + msgCtx, err := l.doRequest(er) if err != nil { return nil, err diff --git a/v3/extended_test.go b/v3/extended_test.go index 6bd83a17..8d9ef63f 100644 --- a/v3/extended_test.go +++ b/v3/extended_test.go @@ -2,26 +2,36 @@ package ldap import ( "testing" + + "github.com/stretchr/testify/assert" ) -func TestExtendedRequest_WhoAmI(t *testing.T) { - l, err := DialURL(ldapServer) +func TestConn_Extended(t *testing.T) { + l, err := getTestConnection(true) if err != nil { - t.Errorf("%s failed: %v", t.Name(), err) - return + t.Fatal(err) } defer l.Close() - l.Bind("", "") // anonymous - defer l.Unbind() + t.Run("nil ExtendedRequest", func(t *testing.T) { + response, err := l.Extended(nil) + assert.Nil(t, response) + assert.Error(t, err) + }) +} + +func TestExtendedRequest_WhoAmI(t *testing.T) { + l, err := getTestConnection(true) + if err != nil { + t.Fatal(err) + } + defer l.Close() rfc4532req := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) // request value is var rfc4532resp *ExtendedResponse - if rfc4532resp, err = l.Extended(rfc4532req); err != nil { - t.Errorf("%s failed: %v", t.Name(), err) - return - } + rfc4532resp, err = l.Extended(rfc4532req) + assert.NoError(t, err) t.Logf("%#v\n", rfc4532resp) } @@ -34,8 +44,5 @@ func TestExtendedRequest_FastBind(t *testing.T) { request := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) _, err = conn.Extended(request) - if err != nil { - t.Errorf("%s failed: %v", t.Name(), err) - return - } + assert.NoError(t, err) } diff --git a/v3/ldap_test.go b/v3/ldap_test.go index ce83b278..d923e6a1 100644 --- a/v3/ldap_test.go +++ b/v3/ldap_test.go @@ -27,6 +27,18 @@ var attributes = []string{ "description", } +func getTestConnection(withBind bool) (*Conn, error) { + l, err := DialURL(ldapServer) + if err != nil { + return nil, err + } + if withBind { + err = l.Bind("cn=admin,"+baseDN, "admin123") + } + + return l, err +} + func TestUnsecureDialURL(t *testing.T) { l, err := DialURL(ldapServer) if err != nil { diff --git a/v3/unbind_test.go b/v3/unbind_test.go new file mode 100644 index 00000000..52f7242e --- /dev/null +++ b/v3/unbind_test.go @@ -0,0 +1,32 @@ +package ldap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConn_Unbind(t *testing.T) { + t.Run("unbind", func(t *testing.T) { + l, err := getTestConnection(false) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + assert.NoError(t, l.Unbind()) + }) + // We should not be able to reuse the connection after unbinding. + t.Run("reuse connection", func(t *testing.T) { + l, err := getTestConnection(false) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + assert.NoError(t, l.Unbind()) + + err = l.Unbind() + assert.True(t, IsErrorWithCode(err, ErrorNetwork), "Expected ErrorNetwork, got %v", err) + }) +} diff --git a/v3/whoami_test.go b/v3/whoami_test.go new file mode 100644 index 00000000..50312b50 --- /dev/null +++ b/v3/whoami_test.go @@ -0,0 +1,27 @@ +package ldap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConn_WhoAmI(t *testing.T) { + l, err := getTestConnection(false) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + t.Run("unauthenticated", func(t *testing.T) { + result, err := l.WhoAmI(nil) + assert.NoError(t, err) + assert.Equal(t, "", result.AuthzID) + }) + t.Run("authenticated", func(t *testing.T) { + assert.NoError(t, l.Bind("cn=admin,"+baseDN, "admin123")) + result, err := l.WhoAmI(nil) + assert.NoError(t, err) + assert.Equal(t, "dn:cn=admin,"+baseDN, result.AuthzID) + }) +} From 0b0c6dbef2b685dc8611552e3e9e0d77eda58661 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 1 Oct 2025 15:49:11 +0200 Subject: [PATCH 3/4] update go test command --- .github/workflows/pr.yml | 3 +-- Makefile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b6f49806..21b274f8 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -31,6 +31,5 @@ jobs: run: | cd ${{ matrix.directory }} go vet . - go test . - go test -cover -race -cpu 1,2,4 . + go test -v -cover -race -count=1 . go build . diff --git a/Makefile b/Makefile index 62b179fd..db20a825 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ stop-local-server: -$(CONTAINER_CMD) rm -f -t 10 $(CONTAINER_NAME) test: - go test -v ./... + go test -v -cover -race -count=1 . fuzz: go test -fuzz=FuzzParseDN -fuzztime=600s . From 33f0e0c57a2274918cffc65c604b9c2566a1590d Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 1 Oct 2025 15:51:05 +0200 Subject: [PATCH 4/4] update go version to test in CI --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 21b274f8..25c2318a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: ["1.24", "1.23"] + go: ["1.25", "1.24"] directory: ["./v3"] name: Go ${{ matrix.go }}.x PR Validate ${{ matrix.directory }} (Modules) steps: