Skip to content

Commit 850a1c1

Browse files
committed
initial commit
0 parents  commit 850a1c1

12 files changed

+1231
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coverage

LICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <https://unlicense.org>

README.md

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# 🥼 DFT
2+
3+
**D**ocker **F**or **T**esting is a zero dependency wrapper around the `docker` command. It is solely based on the std lib.
4+
5+
Only requirement: A running docker daemon.
6+
7+
The package is intended to be used in various testing setups from local testing to CI/CD pipelines. It's main goals are to reduce the need for mocks (especially database ones), and to lower the amount of packages required for testing.
8+
9+
Containers can be spun up with options for ports, environment variables or [CMD] overwrites.
10+
11+
## 👓 Example
12+
13+
Testing a user service backed by mongoDB
14+
15+
```go
16+
package myawesomepkg_test
17+
18+
import (
19+
"context"
20+
"testing"
21+
"time"
22+
23+
"my/awesome/pkg/repository"
24+
"my/awesome/pkg/user"
25+
26+
"github.com/abecodes/dft"
27+
)
28+
29+
func TestUserService(tt *testing.T) {
30+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
31+
defer cancel()
32+
33+
// start a mongoDB container
34+
ctr, err := dft.StartContainer(
35+
ctx,
36+
"mongo:7-jammy",
37+
dft.WithRandomPort(27017),
38+
)
39+
if err != nil {
40+
tt.Errorf("[dft.StartContainer] unexpected error: %v", err)
41+
tt.FailNow()
42+
43+
return
44+
}
45+
46+
// wait for the database
47+
err = ctr.WaitCmd(
48+
ctx,
49+
[]string{
50+
"mongosh",
51+
"--norc",
52+
"--quiet",
53+
"--host=localhost:27017",
54+
"--eval",
55+
"'db.getMongo()'",
56+
},
57+
func(stdOut string, stdErr string, code int) bool {
58+
tt.Logf("got:\n\tcode:%d\n\tout:%s\n\terr:%s\n", code, stdOut, stdErr)
59+
60+
return code == 0
61+
},
62+
// since we use a random port in the example we want to execute the
63+
// command inside of the container
64+
dft.WithExecuteInsideContainer(true),
65+
)
66+
if err != nil {
67+
tt.Errorf("[dft.WaitCmd] wait error: %v", err)
68+
tt.FailNow()
69+
70+
return
71+
}
72+
73+
// let's make sure we clean up after us
74+
defer func() {
75+
if ctr != nil {
76+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
77+
ctr.Stop(ctx)
78+
cancel()
79+
}
80+
}()
81+
82+
// since we use a random port in the example we want to know its
83+
// address on the host. Because we can expose an internal port on multiple host ports,
84+
// this method will return a list of addresses
85+
addrs, ok := ctr.ExposedPortAddr(27017)
86+
if !ok {
87+
tt.Error("[ctr.ExposedPortAddr] did not return any addresses")
88+
tt.FailNow()
89+
90+
return
91+
}
92+
93+
// get a connection
94+
conn := createNewMongoClient(addrs[0])
95+
96+
// create a repo
97+
userRepo := repository.User(conn)
98+
99+
// start the service
100+
userSvc := user.New(userRepo)
101+
defer userSvc.Close()
102+
103+
tt.Run(
104+
"it can store a new user in the database",
105+
func(t *testing.T) {
106+
// create a new user
107+
userSvc.New("awesome", "user")
108+
109+
// validate the write via the repository or DB client
110+
users := userRepo.GetAll()
111+
if len(users) != 1 &&
112+
users[0].FirstName != "awesome" &&
113+
users[0].LastName != "user" (
114+
t.Error("[userSvc.New] unable to create user")
115+
tt.FailNow()
116+
117+
return
118+
)
119+
},
120+
)
121+
}
122+
```
123+
124+
## 🤖 API
125+
126+
### StartContainer options
127+
128+
| Option | Info | Example |
129+
| --- | --- | --- |
130+
| WithCmd | Overwrite [CMD]. | `WithCmd([]string{"--tlsCAFile", "/run/tls/ca.crt"})` |
131+
| WithEnvVar | Set an envvar inside the container.<br>Can be called multiple times.<br>If two options use the same key the latest one will overwrite existing ones. | `WithEnvVar("intent", "prod")` |
132+
| WithPort | Expose an internal port on a specific host port. | `WithPort(27017,8080)` |
133+
| WithRandomPort | Expose an internal port on a random host port.<br>Use `ExposedPorts` or `ExposedPortAddresses` to get the correct host port. | `WithRandomPort(27017)` |
134+
135+
### Wait options
136+
137+
| Option | Info | Example |
138+
| --- | --- | --- |
139+
| WithExecuteInsideContainer | If the given command should be executed inside of the container (default: false).<br> This is useful if we want to use a command only present in the container. | `WithExecuteInsideContainer(true)` |

consants.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dft
2+
3+
const (
4+
base10 = 10
5+
bit64 = 64
6+
)

0 commit comments

Comments
 (0)