From dac0bbb5994ea2c96c35cc3e6a0ebb6d32eb796d Mon Sep 17 00:00:00 2001 From: Wesley Miller Date: Fri, 11 Oct 2024 13:56:20 -0400 Subject: [PATCH] BED-4908: Add unit tests (#901) * Add unit testing * Fix code conventions Add test case to collectors test * Move TestStruct into test function scope --- cmd/api/src/api/utils_test.go | 56 ++++++++++++++ cmd/api/src/api/v2/collectors_test.go | 103 ++++++++++++++++++++++++++ cmd/api/src/api/v2/version_test.go | 52 +++++++++++++ cmd/api/src/go.mod | 3 +- cmd/api/src/go.sum | 10 +++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 cmd/api/src/api/utils_test.go create mode 100644 cmd/api/src/api/v2/collectors_test.go create mode 100644 cmd/api/src/api/v2/version_test.go diff --git a/cmd/api/src/api/utils_test.go b/cmd/api/src/api/utils_test.go new file mode 100644 index 0000000000..83ddb39c6d --- /dev/null +++ b/cmd/api/src/api/utils_test.go @@ -0,0 +1,56 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package api + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestParseOptionalBool(t *testing.T) { + result, err := ParseOptionalBool("true", false) + require.NoError(t, err) + assert.Equal(t, true, result) +} + +func TestParseOptionalBoolEmptyValue(t *testing.T) { + result, _ := ParseOptionalBool("", true) + assert.Equal(t, true, result) +} + +func TestParseOptionalBoolMisspelledValue(t *testing.T) { + result, err := ParseOptionalBool("trueee", false) + assert.Error(t, err) + assert.Equal(t, false, result) +} + +func TestFilterStructSlice(t *testing.T) { + type TestStruct struct { + val int + } + + var ( + structs = []TestStruct{{val: 0}, {val: 1}, {val: 2}, {val: 3}} + expected = []TestStruct{{val: 0}, {val: 1}} + ) + + result := FilterStructSlice(structs, func(testStruct TestStruct) bool { + return testStruct.val < 2 + }) + assert.ElementsMatch(t, expected, result) +} diff --git a/cmd/api/src/api/v2/collectors_test.go b/cmd/api/src/api/v2/collectors_test.go new file mode 100644 index 0000000000..da56358cd7 --- /dev/null +++ b/cmd/api/src/api/v2/collectors_test.go @@ -0,0 +1,103 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package v2_test + +import ( + "context" + "fmt" + "github.com/gorilla/mux" + "github.com/specterops/bloodhound/headers" + "github.com/specterops/bloodhound/mediatypes" + v2 "github.com/specterops/bloodhound/src/api/v2" + "github.com/specterops/bloodhound/src/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "net/http" + "net/http/httptest" + "testing" +) + +func TestResources_GetCollectorManifest(t *testing.T) { + var ( + mockCtrl = gomock.NewController(t) + manifests = config.CollectorManifests{"sharphound": config.CollectorManifest{}, "azurehound": config.CollectorManifest{}} + resources = v2.Resources{ + CollectorManifests: manifests, + } + ) + defer mockCtrl.Finish() + + endpoint := "/api/v2/collectors/%s" + + t.Run("sharphound", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf(endpoint, "sharphound"), nil) + require.NoError(t, err) + + req.Header.Set(headers.ContentType.String(), mediatypes.ApplicationJson.String()) + + router := mux.NewRouter() + router.HandleFunc("/api/v2/collectors/{collector_type}", resources.GetCollectorManifest).Methods(http.MethodGet) + + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + require.Equal(t, http.StatusOK, response.Code) + }) + + t.Run("azurehound", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf(endpoint, "azurehound"), nil) + require.NoError(t, err) + + req.Header.Set(headers.ContentType.String(), mediatypes.ApplicationJson.String()) + + router := mux.NewRouter() + router.HandleFunc("/api/v2/collectors/{collector_type}", resources.GetCollectorManifest).Methods(http.MethodGet) + + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + assert.Equal(t, http.StatusOK, response.Code) + }) + + t.Run("invalid", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf(endpoint, "invalid"), nil) + require.NoError(t, err) + + req.Header.Set(headers.ContentType.String(), mediatypes.ApplicationJson.String()) + + router := mux.NewRouter() + router.HandleFunc("/api/v2/collectors/{collector_type}", resources.GetCollectorManifest).Methods(http.MethodGet) + + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + assert.Equal(t, http.StatusBadRequest, response.Code) + }) + + t.Run("internal error", func(t *testing.T) { + resources := v2.Resources{CollectorManifests: map[string]config.CollectorManifest{}} + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf(endpoint, "azurehound"), nil) + require.NoError(t, err) + + req.Header.Set(headers.ContentType.String(), mediatypes.ApplicationJson.String()) + + router := mux.NewRouter() + router.HandleFunc("/api/v2/collectors/{collector_type}", resources.GetCollectorManifest).Methods(http.MethodGet) + + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + assert.Equal(t, http.StatusInternalServerError, response.Code) + }) +} diff --git a/cmd/api/src/api/v2/version_test.go b/cmd/api/src/api/v2/version_test.go new file mode 100644 index 0000000000..bf3662e22b --- /dev/null +++ b/cmd/api/src/api/v2/version_test.go @@ -0,0 +1,52 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package v2_test + +import ( + "context" + "github.com/gorilla/mux" + "github.com/specterops/bloodhound/headers" + "github.com/specterops/bloodhound/mediatypes" + v2 "github.com/specterops/bloodhound/src/api/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "net/http" + "net/http/httptest" + "testing" +) + +func TestGetVersion(t *testing.T) { + var ( + mockCtrl = gomock.NewController(t) + ) + defer mockCtrl.Finish() + + endpoint := "/api/version" + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, endpoint, nil) + require.NoError(t, err) + + req.Header.Set(headers.ContentType.String(), mediatypes.ApplicationJson.String()) + + router := mux.NewRouter() + router.HandleFunc("/api/version", v2.GetVersion).Methods(http.MethodGet) + + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + require.Equal(t, http.StatusOK, response.Code) + assert.Contains(t, response.Body.String(), "v2") +} diff --git a/cmd/api/src/go.mod b/cmd/api/src/go.mod index 52ab28f14b..aaf6e2323a 100644 --- a/cmd/api/src/go.mod +++ b/cmd/api/src/go.mod @@ -28,6 +28,7 @@ require ( github.com/gobeam/stringy v0.0.6 github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang/mock v1.6.0 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.4.1 @@ -40,13 +41,13 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/russellhaering/goxmldsig v1.4.0 github.com/stretchr/testify v1.9.0 + github.com/teambition/rrule-go v1.8.2 github.com/unrolled/secure v1.13.0 github.com/zenazn/goji v1.0.1 go.uber.org/mock v0.2.0 golang.org/x/crypto v0.24.0 gorm.io/driver/postgres v1.3.8 gorm.io/gorm v1.23.8 - github.com/teambition/rrule-go v1.8.2 ) require ( diff --git a/cmd/api/src/go.sum b/cmd/api/src/go.sum index edf408c38b..6e95fee829 100644 --- a/cmd/api/src/go.sum +++ b/cmd/api/src/go.sum @@ -49,6 +49,7 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -210,8 +211,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8= github.com/unrolled/secure v1.13.0 h1:sdr3Phw2+f8Px8HE5sd1EHdj1aV3yUwed/uZXChLFsk= github.com/unrolled/secure v1.13.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= @@ -243,13 +246,16 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -262,6 +268,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= @@ -285,7 +293,9 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=