Skip to content

Commit 97082cc

Browse files
authored
test: improve test coverage (#568)
* feat: add more tests and update the README.md * update go test command * update go version test in CI
1 parent ad3fa74 commit 97082cc

File tree

14 files changed

+362
-38
lines changed

14 files changed

+362
-38
lines changed

.githooks/pre-push

Lines changed: 0 additions & 6 deletions
This file was deleted.

.github/workflows/pr.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
go: ["1.24", "1.23"]
12+
go: ["1.25", "1.24"]
1313
directory: ["./v3"]
1414
name: Go ${{ matrix.go }}.x PR Validate ${{ matrix.directory }} (Modules)
1515
steps:
@@ -31,6 +31,5 @@ jobs:
3131
run: |
3232
cd ${{ matrix.directory }}
3333
go vet .
34-
go test .
35-
go test -cover -race -cpu 1,2,4 .
34+
go test -v -cover -race -count=1 .
3635
go build .

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
.PHONY: default install build test quicktest fmt vet lint
1+
.PHONY: default install build test test fmt vet lint
22

3-
default: fmt vet lint build quicktest
3+
default: fmt vet lint build test
44

55
CONTAINER_CMD := $(shell command -v podman 2>/dev/null)
66
ifeq ($(CONTAINER_CMD),)
@@ -13,7 +13,7 @@ ifeq ($(CONTAINER_CMD),)
1313
endif
1414

1515
install:
16-
go get -t -v ./...
16+
go get -t -x ./...
1717

1818
build:
1919
go build -v ./...
@@ -51,11 +51,11 @@ local-server:
5151
@echo "Loading LDIF files..."
5252
@$(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'
5353

54-
delete-container:
54+
stop-local-server:
5555
-$(CONTAINER_CMD) rm -f -t 10 $(CONTAINER_NAME)
5656

57-
quicktest:
58-
go test ./...
57+
test:
58+
go test -v -cover -race -count=1 .
5959

6060
fuzz:
6161
go test -fuzz=FuzzParseDN -fuzztime=600s .

README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@ The library implements the following specifications:
1515
## Features:
1616

1717
- Connecting to LDAP server (non-TLS, TLS, STARTTLS, through a custom dialer)
18-
- Binding to LDAP server (Simple Bind, GSSAPI, SASL)
18+
- Bind Requests / Responses (Simple Bind, GSSAPI, SASL)
1919
- "Who Am I" Requests / Responses
20-
- Searching for entries (normal and asynchronous)
21-
- Filter Compile / Decompile
22-
- Paging Search Results
20+
- Search Requests / Responses (normal, paging and asynchronous)
2321
- Modify Requests / Responses
2422
- Add Requests / Responses
2523
- Delete Requests / Responses
2624
- Modify DN Requests / Responses
25+
- Unbind Requests / Responses
26+
- Password Modify Requests / Responses
27+
- Content Synchronization Requests / Responses
28+
- LDAPv3 Filter Compile / Decompile
29+
- Server Side Sorting of Search Results
30+
- LDAPv3 Extended Operations
31+
- LDAPv3 Control Support
2732

2833
## Go Modules:
2934

@@ -36,13 +41,16 @@ Bug reports and pull requests are welcome!
3641
Before submitting a pull request, please make sure tests and verification scripts pass:
3742

3843
```
39-
make all
40-
```
44+
# Setup local directory server using Docker or Podman
45+
make local-server
4146
42-
To set up a pre-push hook to run the tests and verify scripts before pushing:
47+
# Run gofmt, go vet and go test
48+
cd ./v3
49+
make -f ../Makefile
4350
44-
```
45-
ln -s ../../.githooks/pre-push .git/hooks/pre-push
51+
# (Optionally) Stop and delete the directory server container afterwards
52+
cd ..
53+
make stop-local-server
4654
```
4755

4856
---

v3/add.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package ldap
22

33
import (
4+
"errors"
45
"fmt"
6+
57
ber "github.com/go-asn1-ber/asn1-ber"
68
)
79

@@ -66,6 +68,10 @@ func NewAddRequest(dn string, controls []Control) *AddRequest {
6668

6769
// Add performs the given AddRequest
6870
func (l *Conn) Add(addRequest *AddRequest) error {
71+
if addRequest == nil {
72+
return NewError(ErrorNetwork, errors.New("AddRequest cannot be nil"))
73+
}
74+
6975
msgCtx, err := l.doRequest(addRequest)
7076
if err != nil {
7177
return err

v3/add_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package ldap
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestConn_Add(t *testing.T) {
11+
l, err := getTestConnection(true)
12+
if err != nil {
13+
t.Fatal(err)
14+
}
15+
defer l.Close()
16+
17+
dn := "cn=new_user,ou=people,dc=example,dc=com"
18+
// Delete the entry if it already exists from previous test runs
19+
_ = l.Del(NewDelRequest(dn, nil))
20+
21+
t.Run("create entry", func(t *testing.T) {
22+
err := l.Add(&AddRequest{
23+
DN: dn,
24+
Attributes: []Attribute{
25+
{
26+
Type: "objectClass",
27+
Vals: []string{"top", "person", "organizationalPerson", "inetOrgPerson"},
28+
},
29+
{
30+
Type: "cn",
31+
Vals: []string{"testuser"},
32+
},
33+
{
34+
Type: "givenName",
35+
Vals: []string{"Test User"},
36+
},
37+
{
38+
Type: "sn",
39+
Vals: []string{"Dummy"},
40+
},
41+
},
42+
})
43+
assert.NoError(t, err)
44+
})
45+
t.Run("create entry with no attributes", func(t *testing.T) {
46+
err := l.Add(&AddRequest{
47+
DN: dn,
48+
Attributes: nil,
49+
})
50+
assert.Error(t, err)
51+
assert.Truef(t, IsErrorWithCode(err, LDAPResultProtocolError), "Expected LDAPResultProtocolError, got %v", err)
52+
})
53+
t.Run("empty AddRequest", func(t *testing.T) {
54+
err := l.Add(&AddRequest{})
55+
assert.Error(t, err)
56+
})
57+
t.Run("nil AddRequest", func(t *testing.T) {
58+
err := l.Add(nil)
59+
fmt.Println("expected AddRequest, got nil")
60+
assert.Error(t, err)
61+
})
62+
}

v3/bind_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package ldap
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestConn_Bind(t *testing.T) {
10+
l, err := getTestConnection(false)
11+
if err != nil {
12+
t.Fatal(err)
13+
}
14+
defer l.Close()
15+
16+
tests := []struct {
17+
name string
18+
dn string
19+
password string
20+
wantError bool
21+
errorCode uint16
22+
}{
23+
{
24+
name: "invalid credentials",
25+
dn: "cn=admin," + baseDN,
26+
password: "AAAAAAAAAA",
27+
wantError: true,
28+
errorCode: LDAPResultInvalidCredentials,
29+
},
30+
{
31+
name: "no credentials",
32+
dn: "",
33+
password: "",
34+
wantError: true,
35+
errorCode: ErrorEmptyPassword,
36+
},
37+
{
38+
name: "valid credentials",
39+
dn: "cn=admin," + baseDN,
40+
password: "admin123",
41+
wantError: false,
42+
errorCode: LDAPResultSuccess,
43+
},
44+
}
45+
for _, tt := range tests {
46+
t.Run(tt.name, func(t *testing.T) {
47+
err := l.Bind(tt.dn, tt.password)
48+
if tt.wantError {
49+
assert.Error(t, err)
50+
assert.Truef(t, IsErrorWithCode(err, tt.errorCode), "Expected error code %v, got %d", tt.errorCode, err)
51+
} else {
52+
assert.NoError(t, err)
53+
}
54+
})
55+
}
56+
}
57+
58+
func TestConn_UnauthenticatedBind(t *testing.T) {
59+
l, err := getTestConnection(false)
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
defer l.Close()
64+
65+
err = l.UnauthenticatedBind("cn=admin," + baseDN)
66+
assert.Error(t, err)
67+
assert.Truef(t, IsErrorWithCode(err, LDAPResultUnwillingToPerform), "Expected LDAPResultUnwillingToPerform, got %v", err)
68+
}

v3/del.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package ldap
22

33
import (
4+
"errors"
45
"fmt"
6+
57
ber "github.com/go-asn1-ber/asn1-ber"
68
)
79

@@ -35,6 +37,10 @@ func NewDelRequest(DN string, Controls []Control) *DelRequest {
3537

3638
// Del executes the given delete request
3739
func (l *Conn) Del(delRequest *DelRequest) error {
40+
if delRequest == nil {
41+
return NewError(ErrorNetwork, errors.New("DelRequest cannot be nil"))
42+
}
43+
3844
msgCtx, err := l.doRequest(delRequest)
3945
if err != nil {
4046
return err

v3/del_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package ldap
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestConn_Del(t *testing.T) {
10+
l, err := getTestConnection(true)
11+
if err != nil {
12+
t.Fatal(err)
13+
}
14+
defer l.Close()
15+
16+
dn := "cn=testuser,ou=people,dc=example,dc=com"
17+
18+
// Remove the entry if it exists from previous test runs
19+
_ = l.Del(NewDelRequest(dn, nil))
20+
21+
assert.NoError(t, l.Add(&AddRequest{
22+
DN: dn,
23+
Attributes: []Attribute{
24+
{
25+
Type: "objectClass",
26+
Vals: []string{"top", "person", "organizationalPerson", "inetOrgPerson"},
27+
},
28+
{
29+
Type: "cn",
30+
Vals: []string{"testuser"},
31+
},
32+
{
33+
Type: "givenName",
34+
Vals: []string{"Test User"},
35+
},
36+
{
37+
Type: "sn",
38+
Vals: []string{"Dummy"},
39+
},
40+
},
41+
}))
42+
t.Logf("Added user")
43+
44+
tests := []struct {
45+
name string
46+
dn string
47+
wantErr bool
48+
errorCode uint16
49+
}{
50+
{
51+
name: "empty DN",
52+
dn: "",
53+
wantErr: true,
54+
errorCode: LDAPResultUnwillingToPerform,
55+
},
56+
{
57+
name: "invalid DN",
58+
dn: "AAAAAAAAAAAAAAAAAA",
59+
wantErr: true,
60+
errorCode: LDAPResultInvalidDNSyntax,
61+
},
62+
{
63+
name: "delete user",
64+
dn: dn,
65+
wantErr: false,
66+
},
67+
{
68+
name: "delete entry with children",
69+
dn: "ou=people," + baseDN,
70+
wantErr: true,
71+
errorCode: LDAPResultNotAllowedOnNonLeaf,
72+
},
73+
{
74+
name: "delete non existing entry",
75+
dn: "ou=nonexisting," + baseDN,
76+
wantErr: true,
77+
errorCode: LDAPResultNoSuchObject,
78+
},
79+
}
80+
for _, tt := range tests {
81+
t.Run(tt.name, func(t *testing.T) {
82+
delRequest := NewDelRequest(tt.dn, nil)
83+
err := l.Del(delRequest)
84+
if tt.wantErr && err != nil {
85+
assert.Error(t, err)
86+
assert.Truef(t, IsErrorWithCode(err, tt.errorCode), "Expected error with code %d, got %d", tt.errorCode, err)
87+
} else {
88+
assert.NoError(t, err)
89+
}
90+
})
91+
}
92+
93+
t.Run("nil DelRequest", func(t *testing.T) {
94+
err := l.Del(nil)
95+
assert.Error(t, err)
96+
})
97+
}

v3/extended.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package ldap
22

33
import (
4+
"errors"
45
"fmt"
6+
57
ber "github.com/go-asn1-ber/asn1-ber"
68
)
79

@@ -56,6 +58,10 @@ type ExtendedResponse struct {
5658
// Extended performs an extended request. The resulting
5759
// ExtendedResponse may return a value in the form of a *ber.Packet
5860
func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) {
61+
if er == nil {
62+
return nil, NewError(ErrorNetwork, errors.New("ExtendedRequest cannot be nil"))
63+
}
64+
5965
msgCtx, err := l.doRequest(er)
6066
if err != nil {
6167
return nil, err

0 commit comments

Comments
 (0)