Skip to content

Commit 4f57387

Browse files
author
Joshua Reed
committed
Moved unit tests to test/unit.
1 parent b8f3c6f commit 4f57387

23 files changed

+3097
-6
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ export KUBEBUILDER_ASSETS=$(PROJECT_DIR)/bin
171171
test: generate-mocks lint bin/ginkgo bin/kubectl bin/kube-apiserver bin/etcd ## Run tests. At the moment this is only unit tests.
172172
@./hack/testing_ginkgo_recover_statements.sh --add # Add ginkgo.GinkgoRecover() statements to controllers.
173173
@# The following is a slightly funky way to make sure the ginkgo statements are removed regardless the test results.
174-
@ginkgo -v ./api/... ./controllers/... ./pkg/... -coverprofile cover.out; EXIT_STATUS=$$?;\
175-
./hack/testing_ginkgo_recover_statements.sh --remove; exit $$EXIT_STATUS
174+
@cd ./test/unit && ginkgo -v ./... -coverprofile cover.out; EXIT_STATUS=$$?;\
175+
cd ../../ && ./hack/testing_ginkgo_recover_statements.sh --remove; exit $$EXIT_STATUS
176176

177177
.PHONY: generate-mocks
178178
generate-mocks: bin/mockgen generate-deepcopy pkg/mocks/mock_client.go $(shell find ./pkg/mocks -type f -name "mock*.go") ## Generate mocks needed for testing. Primarily mocks of the cloud package.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cloud_test
18+
19+
import (
20+
"errors"
21+
22+
"github.com/apache/cloudstack-go/v2/cloudstack"
23+
"github.com/aws/cluster-api-provider-cloudstack/pkg/cloud"
24+
"github.com/aws/cluster-api-provider-cloudstack/test/dummies"
25+
"github.com/golang/mock/gomock"
26+
. "github.com/onsi/ginkgo"
27+
. "github.com/onsi/gomega"
28+
)
29+
30+
var _ = Describe("AffinityGroup Unit Tests", func() {
31+
var ( // Declare shared vars.
32+
mockCtrl *gomock.Controller
33+
mockClient *cloudstack.CloudStackClient
34+
ags *cloudstack.MockAffinityGroupServiceIface
35+
client cloud.Client
36+
)
37+
38+
BeforeEach(func() {
39+
// Setup new mock services.
40+
mockCtrl = gomock.NewController(GinkgoT())
41+
mockClient = cloudstack.NewMockClient(mockCtrl)
42+
ags = mockClient.AffinityGroup.(*cloudstack.MockAffinityGroupServiceIface)
43+
client = cloud.NewClientFromCSAPIClient(mockClient)
44+
dummies.SetDummyVars()
45+
})
46+
47+
AfterEach(func() {
48+
mockCtrl.Finish()
49+
})
50+
51+
It("fetches an affinity group", func() {
52+
dummies.AffinityGroup.ID = "" // Force name fetching.
53+
ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(&cloudstack.AffinityGroup{}, 1, nil)
54+
55+
Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed())
56+
})
57+
It("creates an affinity group", func() {
58+
dummies.SetDummyDomainAndAccount()
59+
dummies.SetDummyDomainID()
60+
ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(nil, -1, errors.New("FakeError"))
61+
ags.EXPECT().NewCreateAffinityGroupParams(dummies.AffinityGroup.Name, dummies.AffinityGroup.Type).
62+
Return(&cloudstack.CreateAffinityGroupParams{})
63+
ags.EXPECT().CreateAffinityGroup(ParamMatch(And(NameEquals(dummies.AffinityGroup.Name)))).
64+
Return(&cloudstack.CreateAffinityGroupResponse{}, nil)
65+
66+
Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed())
67+
})
68+
69+
Context("AffinityGroup Integ Tests", func() {
70+
client, connectionErr := cloud.NewClient("../../cloud-config")
71+
72+
BeforeEach(func() {
73+
if connectionErr != nil { // Only do these tests if an actual ACS instance is available via cloud-config.
74+
Skip("Could not connect to ACS instance.")
75+
}
76+
dummies.AffinityGroup.ID = "" // Force name fetching.
77+
})
78+
AfterEach(func() {
79+
mockCtrl.Finish()
80+
})
81+
82+
It("Creates an affinity group.", func() {
83+
Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed())
84+
})
85+
It("Associates an affinity group.", func() {
86+
if err := client.GetOrCreateVMInstance(
87+
dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSZone1, dummies.CSAffinityGroup, "",
88+
); err != nil {
89+
Skip("Could not create VM." + err.Error())
90+
}
91+
Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed())
92+
Ω(client.AssociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(Succeed())
93+
})
94+
It("Deletes an affinity group.", func() {
95+
Ω(client.DeleteAffinityGroup(dummies.AffinityGroup)).Should(Succeed())
96+
Ω(client.FetchAffinityGroup(dummies.AffinityGroup)).ShouldNot(Succeed())
97+
})
98+
})
99+
})

test/unit/cloud/client_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cloud_test
18+
19+
import (
20+
. "github.com/onsi/ginkgo"
21+
. "github.com/onsi/gomega"
22+
"gopkg.in/ini.v1"
23+
)
24+
25+
// Example cloud-config ini structure.
26+
type Global struct {
27+
APIURL string `ini:"api-url"`
28+
VerifySSL bool `ini:"verify-ssl"`
29+
}
30+
31+
var _ = Describe("Instance", func() {
32+
33+
var ()
34+
35+
BeforeEach(func() {
36+
// This test fixture is useful for development, but the actual method of parsing is confinded to the client's
37+
// new client method. The parsing used here is more of a schema, and we don't need to test another library's
38+
// abilities to parse said schema.
39+
Skip("Dev test suite.")
40+
})
41+
42+
AfterEach(func() {
43+
})
44+
45+
Context("When fetching an INI config.", func() {
46+
It("Handles the positive case.", func() {
47+
cfg := &Global{}
48+
rawCfg, err := ini.Load("../../cloud-config")
49+
Ω(rawCfg.Section("Global")).ShouldNot(BeNil())
50+
Ω(err).ShouldNot(HaveOccurred())
51+
Ω(rawCfg.Section("Global").MapTo(cfg)).Should(Succeed())
52+
Ω(cfg.VerifySSL).Should(BeFalse())
53+
Ω(cfg.APIURL).ShouldNot(BeEmpty())
54+
})
55+
})
56+
})

test/unit/cloud/cloud_suite_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cloud_test
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/ginkgo"
23+
. "github.com/onsi/gomega"
24+
)
25+
26+
func TestCloud(t *testing.T) {
27+
RegisterFailHandler(Fail)
28+
RunSpecs(t, "Cloud Suite")
29+
}

test/unit/cloud/helpers_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cloud_test
18+
19+
import (
20+
"bytes"
21+
"compress/gzip"
22+
"encoding/base64"
23+
"fmt"
24+
"io/ioutil"
25+
"os"
26+
"path"
27+
"reflect"
28+
29+
"github.com/aws/cluster-api-provider-cloudstack/pkg/cloud"
30+
"github.com/golang/mock/gomock"
31+
. "github.com/onsi/ginkgo"
32+
. "github.com/onsi/gomega"
33+
"github.com/onsi/gomega/types"
34+
)
35+
36+
const (
37+
FixturePath = "test/fixtures/cloud-config-files"
38+
)
39+
40+
var _ = Describe("Helpers", func() {
41+
42+
Context("For a configuration with the 'Global' section missing", func() {
43+
It("Gets API configuration", func() {
44+
filepath := getConfigPath("cloud-config-no-global")
45+
46+
client, err := cloud.NewClient(filepath)
47+
48+
Ω(client).Should(BeNil())
49+
Ω(err.Error()).Should(ContainSubstring("section Global not found"))
50+
})
51+
})
52+
53+
It("should compress and encode string", func() {
54+
str := "Hello World"
55+
56+
compressedAndEncodedData, err := cloud.CompressAndEncodeString(str)
57+
58+
compressedData, _ := base64.StdEncoding.DecodeString(compressedAndEncodedData)
59+
reader, _ := gzip.NewReader(bytes.NewReader(compressedData))
60+
result, _ := ioutil.ReadAll(reader)
61+
62+
Ω(err).Should(BeNil())
63+
Ω(string(result)).Should(Equal(str))
64+
})
65+
})
66+
67+
func getConfigPath(filename string) string {
68+
dir, _ := os.Getwd()
69+
return path.Join(dir, FixturePath, filename)
70+
}
71+
72+
// This matcher is used to make gomega matching compatible with gomock parameter matching.
73+
// It's pretty awesome!
74+
//
75+
// This sort of hacks the gomock interface to inject a gomega matcher.
76+
//
77+
// Gomega matchers are far more flexible than gomock matchers, but they normally can't be used on parameters.
78+
79+
type paramMatcher struct {
80+
matcher types.GomegaMatcher
81+
}
82+
83+
func ParamMatch(matcher types.GomegaMatcher) gomock.Matcher {
84+
return paramMatcher{matcher}
85+
}
86+
87+
func (p paramMatcher) String() string {
88+
return "a gomega matcher to match, and said matcher should have panicked before this message was printed."
89+
}
90+
91+
func (p paramMatcher) Matches(x interface{}) (retVal bool) {
92+
return Ω(x).Should(p.matcher)
93+
}
94+
95+
// This generates translating matchers.
96+
//
97+
// The CloudStack Go API uses param interfaces that can't be accessed except through builtin methods.
98+
//
99+
// This generates translation matchers:
100+
//
101+
// Essentially it will generate a matcher that checks the value from p.Get<some field>() is Equal to an input String.
102+
//
103+
// DomainIDEquals = FieldMatcherGenerator("GetDomainid")
104+
// p := &CreateNewSomethingParams{Domainid: "FakeDomainID"}
105+
// Ω(p).DomainIDEquals("FakeDomainID")
106+
func FieldMatcherGenerator(fetchFunc string) func(string) types.GomegaMatcher {
107+
return (func(expected string) types.GomegaMatcher {
108+
return WithTransform(
109+
func(x interface{}) string {
110+
meth := reflect.ValueOf(x).MethodByName(fetchFunc)
111+
fmt.Println(meth.Call(nil)[0])
112+
113+
return meth.Call(nil)[0].String()
114+
}, Equal(expected))
115+
})
116+
}
117+
118+
var (
119+
DomainIDEquals = FieldMatcherGenerator("GetDomainid")
120+
AccountEquals = FieldMatcherGenerator("GetAccount")
121+
IDEquals = FieldMatcherGenerator("GetId")
122+
NameEquals = FieldMatcherGenerator("GetName")
123+
)
124+
125+
// CreateUserInSubDomain creates a sub-domain, account, and user with api-keys with a generated uuid as part of the name.
126+
func CreateUserInSubDomain(user *cloud.User) error {
127+
128+
return nil
129+
}

0 commit comments

Comments
 (0)