Skip to content

Commit 0f8a02e

Browse files
authored
Merge pull request #117 from SimonBaeumer/add-docs
Add docs
2 parents bc35fc5 + 9f7549f commit 0f8a02e

9 files changed

Lines changed: 323 additions & 91 deletions

File tree

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Contributing
2+
3+
A little check list for contributions:
4+
5+
- Create an issue if you want to add a bigger feature and lets talk about it
6+
- Take a look at the documentation [development.md](docs/development.md)
7+
- Take a look at the [pull request template](.github/PULL_REQUEST_TEMPLATE.md)
8+
- If you need help don't hesitate to ask questions in pull requests or issues
9+
- Issues you can work on are marked as "Help wantend", to be sure clarify the requirements of an issue in the comments
10+
11+
Even if your code is not perfect I would also love to see pull requests or issues with your
12+
custom fork and implementations.
13+
14+
Happy coding!

README.md

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -738,49 +738,7 @@ tests:
738738
739739
### Development
740740
741-
```
742-
# Initialise dev environment
743-
$ make init
744-
745-
# Build the project binary
746-
$ make build
747-
748-
# Unit tests
749-
$ make test
750-
751-
# Coverage
752-
$ make test-coverage
753-
754-
# Coverage with more complex tests like ssh execution
755-
$ make test-coverage-all
756-
757-
# Integration tests for linux and macos
758-
$ make integration-unix
759-
760-
# Integration on linux
761-
$ make integration-linux
762-
763-
# Integration windows
764-
$ make integration-windows
765-
766-
# Add depdencies to vendor
767-
$ make deps
768-
```
769-
770-
### Unit tests
771-
772-
`COMMANDER_TEST_ALL` will enable all tests which are depending on external systems like docker or databases.
773-
Enables ssh tests in unit test suite and sets the credentials for the target host.
774-
`COMMANDER_SSH_TEST` must be set to `1` to enable ssh tests.
775-
776-
```
777-
export COMMANDER_TEST_ALL = 1
778-
export COMMANDER_TEST_SSH=1
779-
export COMMANDER_TEST_SSH_HOST=localhost:2222
780-
export COMMANDER_TEST_SSH_PASS=pass
781-
export COMMANDER_TEST_SSH_USER=root
782-
export COMMANDER_TEST_SSH_IDENTITY_FILE=integration/containers/ssh/.ssh/id_rsa
783-
```
741+
See the documentation at [development.md](docs/development.md)
784742
785743
## Misc
786744

docs/development.md

Lines changed: 257 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,260 @@
11
# Development documentation
22

3-
## Writing integration tests
3+
- [Introduction](#introduction)
4+
* [Directory overview](#directory-overview)
5+
* [Package overview](#package-overview)
6+
* [Build targets](#build-targets)
7+
* [Unit tests](#unit-tests)
8+
- [Extending commander](#extending-commander)
9+
* [Add a new field to the `YAML` suite - with a leaning-by-doing task](#add-a-new-field-to-the--yaml--suite)
10+
* [Writing integration tests](#writing-integration-tests)
11+
12+
13+
## Introduction
14+
15+
### Directory overview
16+
17+
```
18+
├── cmd # Contains the composition root and files which will be compiled to binaries.
19+
├── docs # Documentation for commander
20+
├── examples # Examples of how to use commander with to give you a little bit inspiration.
21+
├── hack # Just a directory for testing stuff and shitty dev scripts.
22+
├── integration # Integration tests for all platforms, all written as test suites for commander.
23+
├── pkg # All packages written in GoLang which are used to compose this tool.
24+
├── release # All binaries which are created on `make release` are located here. Directory is in `.gitignore`.
25+
└── vendor # Third party dependencies added by `go mod`.
26+
```
27+
28+
### Package overview
29+
30+
| **package** | **description** | **path** |
31+
|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
32+
| main | The composition root of the commander, initializes the `urfave/cli` framework. | /cmd/commander |
33+
| app | Contains all commands for commander, i.e. add or test. Will only be used by the `cmd` package located under `/cmd` | /pkg/app |
34+
| matcher | Implements the matching logic for the different assertions types in the suite like `equals, `contains`, `json` or `xml`. | /pkg/matcher |
35+
| output | Output is a package which allows to add different output types. For example it could be possible to add a new output format in `json` or for a health check. | /pkg/output |
36+
| runtime | Runtime controls the execution of the test suite. It will be initialized by the `main` package and will be a given a `Suite` which contains all tests to be executed. The runtime also contains all `executors` which are different types of ways to execute a test, i.e. on a local machine or a node viá ssh. | /pkg/runtime |
37+
| suite | Suite is responsible for parsing the defined formats of different test suites. At the moment only `yaml` is supported but it would be possible to add support for a custom DSL or formats like `toml` and `json`. The `suite.Suite` struct will be used by the `runtime.Runtime` for executing the tests. | /pkg/suite |
38+
39+
40+
### Build targets
41+
42+
```
43+
# Initialise dev environment
44+
$ make init
45+
46+
# Build the project binary
47+
$ make build
48+
49+
# Unit tests
50+
$ make test
51+
52+
# Coverage
53+
$ make test-coverage
54+
55+
# Coverage with more complex tests like ssh execution
56+
$ make test-coverage-all
57+
58+
# Integration tests for linux and macos
59+
$ make integration-unix
60+
61+
# Integration on linux
62+
$ make integration-linux
63+
64+
# Integration windows
65+
$ make integration-windows
66+
67+
# Add depdencies to vendor
68+
$ make deps
69+
```
70+
71+
### Unit tests
72+
73+
`COMMANDER_TEST_ALL` will enable all tests which are depending on external systems like docker or databases.
74+
Enables ssh tests in unit test suite and sets the credentials for the target host.
75+
`COMMANDER_SSH_TEST` must be set to `1` to enable ssh tests.
76+
77+
**Note:** I am aware that unit tests should not test external systems nor libraries, but in favour of simplicity and laziness
78+
I created simple tests inside the directory tree.
79+
80+
```bash
81+
export COMMANDER_TEST_ALL=1
82+
export COMMANDER_TEST_SSH=1
83+
export COMMANDER_TEST_SSH_HOST=localhost:2222
84+
export COMMANDER_TEST_SSH_PASS=pass
85+
export COMMANDER_TEST_SSH_USER=root
86+
export COMMANDER_TEST_SSH_IDENTITY_FILE=integration/containers/ssh/.ssh/id_rsa
87+
```
88+
89+
## Extending commander
90+
91+
### Add a new field to the `YAML` suite - with a leaning-by-doing task
92+
93+
It is a little bit annoying to add fields because it will be converted multiple times to keep the
94+
suite format abstracted from the runtime package.
95+
The idea behind this it to add support for other formats like `json`, `toml` or maybe a custom DSL.
96+
97+
**Definition of done**:
98+
99+
- Add a property `message` which always display a message while executing a test
100+
```yaml
101+
tests:
102+
echo hello:
103+
exit-code: 0
104+
config:
105+
message: this is a very special test
106+
```
107+
- Support global configurations
108+
- Create a simple test case
109+
110+
**1. Extend the conversion struct `suite.YAMLTestConfigConf` in [yaml_suite.go](../pkg/suite/yaml_suite.go) with the `Message` property.**
111+
112+
The structs types with a `Conf` suffix represent the configuration type, the `YAML` prefix the format of the suite.
113+
The naming makes it a little bit clearer in the code which type is used, i.e. a `runtime.CommandUnderTest` or a `suite.YAMLTestConfigConf`.
114+
115+
```go
116+
Message string `yaml:"message,omitempty"`
117+
```
118+
119+
**2. Add the `Message` property as a `string` to the `runtime.CommandUnderTest` struct**
120+
121+
The `runtime.CommandUnderTest` struct in [runtime.go](../pkg/runtime/runtime.go) will be used by the runtime to
122+
create the command with all its configs like `env` variables and is used for the test execution.
123+
124+
**3. Add a simple test case**
125+
126+
Open [yaml_suite_test.go](../pkg/suite/yaml_suite_test.go) and look for an existing test to add this property or create a new one.
127+
I recommend to create a simple test case before adding the properties because it is easier to debug and test.
128+
In our example we could extend the `TestYAMLSuite_ShouldPreferLocalTestConfigs` test but will add a new `TestYAMLSuite_Message` test for the simplicity.
129+
130+
```go
131+
func TestYamlSuite_Message(t *testing.T) {
132+
yaml := []byte(`
133+
tests:
134+
echo hello:
135+
exit-code: 0
136+
config:
137+
message: "This is a very special test"
138+
`)
139+
140+
got := ParseYAML(yaml)
141+
142+
assert.Equal(t, "This is a very special test", got.TestCases[0].Command.Message)
143+
}
144+
```
145+
146+
Run the unit tests with `make test`. It should print a result like this:
147+
148+
```
149+
--- FAIL: TestYamlSuite_Message (0.00s)
150+
yaml_suite_test.go:192:
151+
Error Trace: yaml_suite_test.go:192
152+
Error: Not equal:
153+
expected: "This is a very special test"
154+
actual : ""
155+
156+
Diff:
157+
--- Expected
158+
+++ Actual
159+
@@ -1 +1 @@
160+
-This is a very special test
161+
+
162+
Test: TestYamlSuite_Message
163+
FAIL
164+
```
165+
166+
**4. Parse YAML and convert it to `suite.Suite`**
167+
168+
This is a little bit complicated and error prone because it is splitted into three steps:
169+
170+
- Parse YAML file
171+
- Convert YAML config structs to runtime test structs
172+
- Assign global configuration
173+
174+
For this take a look at the `pkg/suite/yaml_suite.go:ParseYAML` function which is responsible for parsing the suite.
175+
176+
1. Parse yaml - `err := yaml.UnmarshalStrict(content, &yamlConfig)`
177+
This line parses the yaml file and will return a `suite.YAMLSuiteConf`, later it will be converted to our structs
178+
from the `runtime` package.
179+
Now we can implement our custom unmarshal logic in the `YAMLSuiteConf::UnmarshalYAML` method.
180+
Follow the code where `Config: y.mergeConfigs(v.Config, params.Config)` is called.
181+
This merges the local with global configs and overwrites the global if it is necessary.
182+
183+
Jump into the `YAMLSuiteConf::mergeConfigs` and add the `message` property like this:
184+
185+
```go
186+
if local.Message != "" {
187+
conf.Message = local.Message
188+
}
189+
190+
```
191+
192+
1. Convert test cases - `tests := convertYAMLSuiteConfToTestCases(yamlConfig)`
193+
194+
Jump into the `convertYAMLSuiteConfToTestCases` function and assign the content of our new field to the `runtime.CommandUnderTest` conversion.
195+
196+
```
197+
Command: runtime.CommandUnderTest{
198+
Cmd: t.Command,
199+
InheritEnv: t.Config.InheritEnv,
200+
[...]
201+
Message: t.Config.Message,
202+
},
203+
```
204+
205+
**5. Add the global config assignment and add the `Message` property**
206+
207+
Add a new `Message` property to the `runtime.GlobalTestConfig`.
208+
209+
```go
210+
type GlobalTestConfig struct {
211+
Env map[string]string
212+
[...]
213+
Message string
214+
}
215+
```
216+
217+
And last but not least implement the assignment of the global config.
218+
This can be done easily inside the `return` of the `ParseYAML` function statement.
219+
220+
```go
221+
return Suite{
222+
TestCases: tests,
223+
Config: runtime.TestConfig{
224+
InheritEnv: yamlConfig.Config.InheritEnv,
225+
Env: yamlConfig.Config.Env,
226+
Dir: yamlConfig.Config.Dir,
227+
Timeout: yamlConfig.Config.Timeout,
228+
Retries: yamlConfig.Config.Retries,
229+
Interval: yamlConfig.Config.Interval,
230+
Nodes: yamlConfig.Config.Nodes,
231+
},
232+
Nodes: convertNodes(yamlConfig.Nodes),
233+
}
234+
```
235+
236+
**6. Run the tests**
237+
238+
```bash
239+
$ make test
240+
INFO: Starting build test
241+
go test ./...
242+
ok github.com/SimonBaeumer/commander/cmd/commander 0.011s
243+
ok github.com/SimonBaeumer/commander/pkg/app 0.014s
244+
ok github.com/SimonBaeumer/commander/pkg/matcher (cached)
245+
ok github.com/SimonBaeumer/commander/pkg/output (cached)
246+
ok github.com/SimonBaeumer/commander/pkg/runtime 0.229s
247+
ok github.com/SimonBaeumer/commander/pkg/suite 0.008s
248+
```
249+
250+
**7. Learning by doing**
251+
252+
Two tasks are missing which you can complete on your own.
253+
254+
- Add the printing of the message (tip: Take a look into the `runtime.go` file)
255+
- Extend the test case that it is tested that local configs are preferred over global configs (tip: Take a look at the other tests).
256+
257+
### Writing integration tests
4258

5259
Commander tests itself. You can find the integration tests in `commander_unix.yaml` and `commander_windows.yaml`.
6260
More complex scenarios are stored in `integration/`.
@@ -10,4 +264,5 @@ It is always necessary to execute the test suite with a stable version of comman
10264
**Tipps:**
11265

12266
- The working directory is by default the project root, even for tests located inside `integration/*`
13-
- Execute `commander` inside the `commander_*.yaml` files with a given suite and assert the result which is returned
267+
- Execute `commander` inside the `commander_*.yaml` files with a given suite and assert the result which is returned
268+

pkg/app/add_command.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import (
1212
// command is the command which should be added to the test suite
1313
// existed holds the existing yaml content
1414
func AddCommand(command string, existed []byte) ([]byte, error) {
15-
conf := suite.YAMLConfig{
15+
conf := suite.YAMLSuiteConf{
1616
Tests: make(map[string]suite.YAMLTest),
17-
Config: suite.YAMLTestConfig{},
17+
Config: suite.YAMLTestConfigConf{},
1818
}
1919
c := cmd.NewCommand(command)
2020

@@ -64,9 +64,9 @@ func AddCommand(command string, existed []byte) ([]byte, error) {
6464
return out, nil
6565
}
6666

67-
func convertConfig(config suite.YAMLTestConfig) suite.YAMLTestConfig {
67+
func convertConfig(config suite.YAMLTestConfigConf) suite.YAMLTestConfigConf {
6868
if config.Dir == "" && len(config.Env) == 0 && config.Timeout == "" {
69-
return suite.YAMLTestConfig{}
69+
return suite.YAMLTestConfigConf{}
7070
}
7171
return config
7272
}

pkg/runtime/runtime.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ type Node struct {
7070
Privileged bool
7171
}
7272

73-
//TestConfig represents the configuration for a test
74-
type TestConfig struct {
73+
//GlobalTestConfig represents the configuration for a test
74+
type GlobalTestConfig struct {
7575
Env map[string]string
7676
Dir string
7777
Timeout string

pkg/suite/suite.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
// In example it could be possible to add more formats like XML or a custom DSL implementation.
1111
type Suite struct {
1212
TestCases []runtime.TestCase
13-
Config runtime.TestConfig
13+
Config runtime.GlobalTestConfig
1414
Nodes []runtime.Node
1515
}
1616

@@ -54,6 +54,6 @@ func (s Suite) GetTestByTitle(title string) (runtime.TestCase, error) {
5454
}
5555

5656
// GetGlobalConfig returns the global configuration which applies to the complete suite
57-
func (s Suite) GetGlobalConfig() runtime.TestConfig {
57+
func (s Suite) GetGlobalConfig() runtime.GlobalTestConfig {
5858
return s.Config
5959
}

pkg/suite/suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ func Test_GetTestByTitle(t *testing.T) {
4343
}
4444

4545
func Test_GetGlobalConfig(t *testing.T) {
46-
s := Suite{Config: runtime.TestConfig{Dir: "/tmp"}}
46+
s := Suite{Config: runtime.GlobalTestConfig{Dir: "/tmp"}}
4747
assert.Equal(t, "/tmp", s.GetGlobalConfig().Dir)
4848
}

0 commit comments

Comments
 (0)