This repository has been archived by the owner on May 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
repository_test.go
148 lines (131 loc) · 4.58 KB
/
repository_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package users_test
import (
"context"
"fmt"
"github.com/neo4j-examples/golang-neo4j-realworld-example/pkg/users"
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"golang.org/x/crypto/bcrypt"
"io"
)
var _ = Describe("User repository", func() {
const username = "neo4j"
const password = "s3cr3t"
var ctx context.Context
var neo4jContainer testcontainers.Container
var driver neo4j.Driver
var repository users.UserRepository
BeforeEach(func() {
ctx = context.Background()
var err error
neo4jContainer, err = startContainer(ctx, username, password)
Expect(err).To(BeNil(), "Container should start")
port, err := neo4jContainer.MappedPort(ctx, "7687")
Expect(err).To(BeNil(), "Port should be resolved")
address := fmt.Sprintf("bolt://localhost:%d", port.Int())
driver, err = neo4j.NewDriver(address, neo4j.BasicAuth(username, password, ""))
Expect(err).To(BeNil(), "Driver should be created")
repository = &users.UserNeo4jRepository{
Driver: driver,
}
})
AfterEach(func() {
Close(driver, "Driver")
Expect(neo4jContainer.Terminate(ctx)).To(BeNil(), "Container should stop")
})
It("registers users", func() {
username := "some-user"
email := "some-user@example.com"
initialPassword := "some-password"
err := repository.RegisterUser(&users.User{
Username: username,
Email: email,
Password: initialPassword,
})
Expect(err).To(BeNil(), "User should be registered")
session := driver.NewSession(neo4j.SessionConfig{})
defer Close(session, "Session")
result, err := session.
ReadTransaction(func(tx neo4j.Transaction) (interface{}, error) {
res, err := tx.Run("MATCH (u:User {username: $username, email: $email}) "+
"RETURN u.username AS username, u.email AS email, u.password AS password",
map[string]interface{}{
"username": username,
"email": email,
})
if err != nil {
return nil, err
}
singleRecord, err := res.Single()
if err != nil {
return nil, err
}
return &users.User{
Username: singleRecord.Values[0].(string),
Email: singleRecord.Values[1].(string),
Password: singleRecord.Values[2].(string),
}, nil
})
Expect(err).To(BeNil(), "Transaction should successfully run")
persistedUser := result.(*users.User)
Expect(persistedUser.Username).To(Equal(username))
Expect(persistedUser.Email).To(Equal(email))
Expect(passwordsMatch(initialPassword, persistedUser.Password)).
To(BeTrue(), "passwords should match")
})
It("logs users in", func() {
email := "florent@example.org"
clearTextPassword := "sup3rpassw0rd"
session := driver.NewSession(neo4j.SessionConfig{})
defer Close(session, "Session")
_, err := session.WriteTransaction(func(tx neo4j.Transaction) (interface{}, error) {
_, err := tx.Run(`CREATE (:User {username: "flo", email: $email, password: $password})`,
map[string]interface{}{
"email": email,
"password": hash(clearTextPassword),
})
return nil, err
})
Expect(err).To(BeNil(), "user should be inserted")
user, err := repository.FindByEmailAndPassword(email, clearTextPassword)
Expect(err).To(BeNil(), "Login should not fail")
Expect(user.Email).To(Equal(email))
Expect(user.Username).To(Equal("flo"))
})
It("does not log non-existing user", func() {
user, err := repository.FindByEmailAndPassword(
"florent@example.org",
"sup3rpassw0rd",
)
Expect(err).To(BeNil(), "Login should not fail")
Expect(user).To(BeNil(), "User should not be found")
})
})
func hash(password string) string {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
Expect(err).To(BeNil(), "password should be hashed")
return string(hashedPassword)
}
func passwordsMatch(initialPassword string, hashedPassword string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(initialPassword))
return err == nil
}
func Close(closer io.Closer, resourceName string) {
Expect(closer.Close()).
To(BeNil(), "%s should close", resourceName)
}
func startContainer(ctx context.Context, username, password string) (testcontainers.Container, error) {
request := testcontainers.ContainerRequest{
Image: "neo4j",
ExposedPorts: []string{"7687/tcp"},
Env: map[string]string{"NEO4J_AUTH": fmt.Sprintf("%s/%s", username, password)},
WaitingFor: wait.ForLog("Bolt enabled"),
}
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: request,
Started: true,
})
}