Skip to content

Commit

Permalink
Better testing (#56)
Browse files Browse the repository at this point in the history
* added router, and helper test methods to easily to make unit tests

* Added testMethod to check request method, comments, removed unneccessary methods

* Added guide on how to create and add new unit tests!
  • Loading branch information
fear-the-reaper authored Sep 4, 2022
1 parent 6844dbe commit d15a363
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 279 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ $ go get github.com/intelowlproject/go-intelowl
## Usage
This library was built with ease of use in mind! Here are some quick examples to get you started. If you need more example you can go to the [examples directory](./examples/)

To start using the gointelowl library you first need to import it:
To start using the go-intelowl library you first need to import it:
```
import "github.com/intelowlproject/go-intelowl/gointelowl"
```
Expand Down Expand Up @@ -117,8 +117,7 @@ func main(){
}

```
<!-- TODO: ADD THE PKG LINK-->
For complete usage of go-intelowl, see the full [package docs]().
For complete usage of go-intelowl, see the full [package docs](https://pkg.go.dev/github.com/intelowlproject/go-intelowl).

# Contribute
See our [contributor page]() for details how to contribute. If you want to follow the updates, discuss, or just chat then please join our [slack](https://honeynetpublic.slack.com/archives/C01KVGMAKL6) channel we'd love to hear your feedback!
Expand Down
55 changes: 55 additions & 0 deletions tests/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## How unit tests were written
The unit tests were written as a combination of [table driven tests](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests) and the approach used by [go-github](https://github.com/google/go-github)

Firstly we use a `TestData` struct that has the following fields:
1. `Input` - this is an `interface` as it is to be used as the input required for an endpoint
2. `Data` - this is a `string` as it'll be the `JSON` string that the endpoint is expected to return
2. `StatusCode` - this is an `int` as it is meant to be used as the expected response returned by the endpoint
3. `Want` - the expected struct that the method will return

Now the reason we made this was that these fields were needed for every endpoint hence combining them into a single struct provided us reusability and flexibility.

Now the testing suite used go's `httptest` library where we use `httptest.Server` as this setups a test server so that we can easily mock it. We also use `http.ServerMux` to mock our endpoints response.

## How to add a new test for an endpoint
Lets say IntelOwl added a new endpoint called **supercool** in `Tag`. Now you've implemented the endpoint as a method of `TagService` and now you want to add its unit tests.

First go to `tagService_test.go` in the `tests` directory and add

```Go
func TestSuperCoolEndPoint(t *testing.T) {
testCases := make(map[string]TestData)
testCases["simple"] = TestData{
Input: nil,
Data: `{ "supercool": "you're a great developer :)"}`,
StatusCode: http.StatusOK,
Want: "you're a great developer :)",
}
for name, testCase := range testCases {
// subtest
t.Run(name, func(t *testing.T) {
// setup will give you the client, mux/router, closeServer
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
// now you can use apiHandler to mock how the server will handle this endpoints request
// you can use mux/router's Handle method or HandleFunc
apiHandler.Handle("/api/tag/supercool", func(w http.ResponseWriter, r *http.Request) {
// this is a helper test to check if it is the expected request sent by the client
testMethod(t, r, "GET")
w.Write([]byte(testCase.Data))
})
expectedRespone, err := client.TagService.SuperCool(ctx)
if err != nil {
testError(t, testCase, err)
} else {
testWantData(t, testCase.Want, expectedRespone)
}
})
}
}

```

Great! Now you've added your own unit tests.

74 changes: 24 additions & 50 deletions tests/analysisService_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"path"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/intelowlproject/go-intelowl/gointelowl"
)

Expand Down Expand Up @@ -40,23 +38,17 @@ func TestCreateObservableAnalysis(t *testing.T) {
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
apiHandler.Handle("/api/analyze_observable", serverHandler(t, testCase, "POST"))
observableParams, ok := testCase.Input.(gointelowl.ObservableAnalysisParams)
if ok {
gottenAnalysisResponse, err := client.CreateObservableAnalysis(ctx, &observableParams)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, gottenAnalysisResponse)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, gottenAnalysisResponse)
}
}
})
Expand Down Expand Up @@ -98,23 +90,17 @@ func TestCreateMultipleObservableAnalysis(t *testing.T) {

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
apiHandler.Handle("/api/analyze_multiple_observables", serverHandler(t, testCase, "POST"))
ctx := context.Background()
multipleObservableParams, ok := testCase.Input.(gointelowl.MultipleObservableAnalysisParams)
if ok {
gottenMultipleAnalysisResponse, err := client.CreateMultipleObservableAnalysis(ctx, &multipleObservableParams)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, gottenMultipleAnalysisResponse)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, gottenMultipleAnalysisResponse)
}
}
})
Expand Down Expand Up @@ -154,23 +140,17 @@ func TestCreateFileAnalysis(t *testing.T) {
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
apiHandler.Handle("/api/analyze_file", serverHandler(t, testCase, "POST"))
ctx := context.Background()
fileAnalysisParams, ok := testCase.Input.(gointelowl.FileAnalysisParams)
if ok {
gottenFileAnalysisResponse, err := client.CreateFileAnalysis(ctx, &fileAnalysisParams)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, gottenFileAnalysisResponse)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, gottenFileAnalysisResponse)
}
}
})
Expand Down Expand Up @@ -216,23 +196,17 @@ func TestCreateMultipleFilesAnalysis(t *testing.T) {
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
apiHandler.Handle("/api/analyze_mutliple_files", serverHandler(t, testCase, "POST"))
ctx := context.Background()
multipleFilesAnalysisParams, ok := testCase.Input.(gointelowl.MultipleFileAnalysisParams)
if ok {
gottenMultipleFilesAnalysisResponse, err := client.CreateMultipleFileAnalysis(ctx, &multipleFilesAnalysisParams)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, gottenMultipleFilesAnalysisResponse)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, gottenMultipleFilesAnalysisResponse)
}
}
})
Expand Down
40 changes: 14 additions & 26 deletions tests/analyzerService_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package tests
import (
"context"
"encoding/json"
"fmt"
"net/http"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/intelowlproject/go-intelowl/gointelowl"
)

Expand Down Expand Up @@ -83,21 +82,15 @@ func TestAnalyzerServiceGetConfigs(t *testing.T) {
for name, testCase := range testCases {
// *Subtest
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
apiHandler.Handle("/api/get_analyzer_configs", serverHandler(t, testCase, "GET"))
gottenAnalyzerConfigList, err := client.AnalyzerService.GetConfigs(ctx)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, (*gottenAnalyzerConfigList))
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, (*gottenAnalyzerConfigList))
}
})
}
Expand All @@ -124,23 +117,18 @@ func TestAnalyzerServiceHealthCheck(t *testing.T) {
for name, testCase := range testCases {
// *Subtest
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
input, ok := testCase.Input.(string)
if ok {
testUrl := fmt.Sprintf("/api/analyzer/%s/healthcheck", input)
apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET"))
status, err := client.AnalyzerService.HealthCheck(ctx, input)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, status)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, status)
}
}
})
Expand Down
42 changes: 15 additions & 27 deletions tests/connectorService_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package tests
import (
"context"
"encoding/json"
"fmt"
"net/http"
"sort"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/intelowlproject/go-intelowl/gointelowl"
)

Expand Down Expand Up @@ -41,21 +40,15 @@ func TestConnectorServiceGetConfigs(t *testing.T) {
for name, testCase := range testCases {
// *Subtest
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
apiHandler.Handle("/api/get_connector_configs", serverHandler(t, testCase, "GET"))
gottenConnectorConfigList, err := client.ConnectorService.GetConfigs(ctx)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, (*gottenConnectorConfigList))
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, *gottenConnectorConfigList)
}
})
}
Expand All @@ -82,23 +75,18 @@ func TestConnectorServiceHealthCheck(t *testing.T) {
for name, testCase := range testCases {
// *Subtest
t.Run(name, func(t *testing.T) {
testServer := NewTestServer(&testCase)
defer testServer.Close()
client := NewTestIntelOwlClient(testServer.URL)
client, apiHandler, closeServer := setup()
defer closeServer()
ctx := context.Background()
input, ok := testCase.Input.(string)
if ok {
status, err := client.AnalyzerService.HealthCheck(ctx, input)
if testCase.StatusCode < http.StatusOK || testCase.StatusCode >= http.StatusBadRequest {
diff := cmp.Diff(testCase.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response"))
if diff != "" {
t.Fatalf(diff)
}
testUrl := fmt.Sprintf("/api/connector/%s/healthcheck", input)
apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET"))
status, err := client.ConnectorService.HealthCheck(ctx, input)
if err != nil {
testError(t, testCase, err)
} else {
diff := cmp.Diff(testCase.Want, status)
if diff != "" {
t.Fatalf(diff)
}
testWantData(t, testCase.Want, status)
}
}
})
Expand Down
Loading

0 comments on commit d15a363

Please sign in to comment.