Skip to content

Commit 5620135

Browse files
authored
Merge pull request #8 from deploymenttheory/dev-jl-testing
Implemented Unit testing for non web dependant functions
2 parents 271469e + b5cfceb commit 5620135

File tree

9 files changed

+262
-121
lines changed

9 files changed

+262
-121
lines changed

jamf/jamfprointegration/auth_mock.go

Lines changed: 0 additions & 41 deletions
This file was deleted.

jamf/jamfprointegration/auth_oauth.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,50 +39,41 @@ type OAuthResponse struct {
3939

4040
// getNewToken updates the held token and expiry information
4141
func (a *oauth) getNewToken() error {
42-
a.Sugar.Warn("THREE-TWO-ONE")
4342
data := url.Values{}
4443
data.Set("client_id", a.clientId)
4544
data.Set("client_secret", a.clientSecret)
4645
data.Set("grant_type", "client_credentials")
4746

48-
a.Sugar.Warn("THREE-TWO-TWO")
4947
oauthComlpeteEndpoint := a.baseDomain + oAuthTokenEndpoint
5048
a.Sugar.Debugf("oauth endpoint constructed: %s", oauthComlpeteEndpoint)
5149

52-
a.Sugar.Warn("THREE-TWO-THREE")
5350
req, err := http.NewRequest("POST", oauthComlpeteEndpoint, strings.NewReader(data.Encode()))
5451
if err != nil {
5552
return err
5653
}
57-
a.Sugar.Warn("THREE-TWO-FOUR")
5854

5955
a.Sugar.Debugf("oauth token request constructed: %+v", req)
6056

6157
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
6258

63-
a.Sugar.Warn("THREE-TWO-FIVE")
6459
resp, err := a.httpExecutor.Do(req)
6560
if err != nil {
6661
return err
6762
}
68-
a.Sugar.Warn("THREE-TWO-SIX")
6963

7064
if resp.StatusCode < 200 || resp.StatusCode > 299 {
7165
return fmt.Errorf("bad request getting auth token: %v", resp)
7266
}
7367

7468
defer resp.Body.Close()
7569

76-
a.Sugar.Warn("THREE-TWO-SEVEN")
7770
bodyBytes, err := io.ReadAll(resp.Body)
7871
if err != nil {
7972
return err
8073
}
81-
a.Sugar.Warn("THREE-TWO-EIGHT")
8274

8375
resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
8476

85-
a.Sugar.Warn("THREE-TWO-NINE")
8677
oauthResp := &OAuthResponse{}
8778
err = json.Unmarshal(bodyBytes, oauthResp)
8879
if err != nil {

jamf/jamfprointegration/builders.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88
)
99

1010
// BuildWithOAuth is a helper function allowing the full construct of a Jamf Integration using OAuth2
11-
func BuildWithOAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferPeriod time.Duration, clientId string, clientSecret string, hideSensitiveData bool, executor *httpclient.HTTPExecutor) (*Integration, error) {
11+
func BuildWithOAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferPeriod time.Duration, clientId string, clientSecret string, hideSensitiveData bool, executor httpclient.HTTPExecutor) (*Integration, error) {
1212
integration := Integration{
1313
BaseDomain: jamfBaseDomain,
1414
Sugar: Sugar,
1515
AuthMethodDescriptor: "oauth2",
16-
httpExecutor: *executor,
16+
httpExecutor: executor,
1717
}
1818

1919
integration.BuildOAuth(clientId, clientSecret, bufferPeriod, hideSensitiveData, executor)
@@ -23,13 +23,13 @@ func BuildWithOAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferPerio
2323
}
2424

2525
// BuildWithBasicAuth is a helper function allowing the full construct of a Jamf Integration using BasicAuth
26-
func BuildWithBasicAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferPeriod time.Duration, username string, password string, hideSensitiveData bool, executor *httpclient.HTTPExecutor) (*Integration, error) {
26+
func BuildWithBasicAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferPeriod time.Duration, username string, password string, hideSensitiveData bool, executor httpclient.HTTPExecutor) (*Integration, error) {
2727

2828
integration := Integration{
2929
BaseDomain: jamfBaseDomain,
3030
Sugar: Sugar,
3131
AuthMethodDescriptor: "basic",
32-
httpExecutor: *executor,
32+
httpExecutor: executor,
3333
}
3434

3535
integration.BuildBasicAuth(username, password, bufferPeriod, hideSensitiveData, executor)
@@ -39,30 +39,30 @@ func BuildWithBasicAuth(jamfBaseDomain string, Sugar *zap.SugaredLogger, bufferP
3939
}
4040

4141
// BuildOAuth is a helper which returns just a configured OAuth interface
42-
func (j *Integration) BuildOAuth(clientId string, clientSecret string, bufferPeriod time.Duration, hideSensitiveData bool, executor *httpclient.HTTPExecutor) {
42+
func (j *Integration) BuildOAuth(clientId string, clientSecret string, bufferPeriod time.Duration, hideSensitiveData bool, executor httpclient.HTTPExecutor) {
4343
authInterface := oauth{
4444
clientId: clientId,
4545
clientSecret: clientSecret,
4646
bufferPeriod: bufferPeriod,
4747
baseDomain: j.BaseDomain,
4848
Sugar: j.Sugar,
4949
hideSensitiveData: hideSensitiveData,
50-
httpExecutor: *executor,
50+
httpExecutor: executor,
5151
}
5252

5353
j.auth = &authInterface
5454
}
5555

5656
// BuildBasicAuth is a helper which returns just a configured Basic Auth interface/
57-
func (j *Integration) BuildBasicAuth(username string, password string, bufferPeriod time.Duration, hideSensitiveData bool, executor *httpclient.HTTPExecutor) {
57+
func (j *Integration) BuildBasicAuth(username string, password string, bufferPeriod time.Duration, hideSensitiveData bool, executor httpclient.HTTPExecutor) {
5858
authInterface := basicAuth{
5959
username: username,
6060
password: password,
6161
bufferPeriod: bufferPeriod,
6262
Sugar: j.Sugar,
6363
baseDomain: j.BaseDomain,
6464
hideSensitiveData: hideSensitiveData,
65-
httpExecutor: *executor,
65+
httpExecutor: executor,
6666
}
6767

6868
j.auth = &authInterface

jamf/jamfprointegration/headers.go

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@ import (
66
"go.uber.org/zap"
77
)
88

9+
const (
10+
WeightedAcceptHeader = "application/x-x509-ca-cert;q=0.95," +
11+
"application/pkix-cert;q=0.94," +
12+
"application/pem-certificate-chain;q=0.93," +
13+
"application/octet-stream;q=0.8," + // For general binary files
14+
"image/png;q=0.75," +
15+
"image/jpeg;q=0.74," +
16+
"image/*;q=0.7," +
17+
"application/xml;q=0.65," +
18+
"text/xml;q=0.64," +
19+
"text/xml;charset=UTF-8;q=0.63," +
20+
"application/json;q=0.5," +
21+
"text/html;q=0.5," +
22+
"text/plain;q=0.4," +
23+
"*/*;q=0.05"
24+
25+
UserAgentHeader = "go-api-http-client-jamfpro-integration"
26+
)
27+
928
// getContentTypeHeader determines the appropriate Content-Type header for a given API endpoint.
1029
// It sets the Content-Type to "application/octet-stream" specifically for the endpoint "/api/v1/packages/{id}/upload".
1130
// For other endpoints, it attempts to match the Content-Type based on the endpoint pattern:
@@ -16,13 +35,15 @@ import (
1635
func (j *Integration) getContentTypeHeader(endpoint string) string {
1736
j.Sugar.Debug("Determining Content-Type for endpoint", zap.String("endpoint", endpoint))
1837

38+
// TODO change this contains to regex. We want to rule out malformed endpoints with multiple occurances.
1939
if strings.Contains(endpoint, "/api/v1/packages/") && strings.Contains(endpoint, "/upload") {
2040
j.Sugar.Debugw("Content-Type for packages upload endpoint set to application/octet-stream", "endpoint", endpoint)
2141
return "application/octet-stream"
2242
}
2343

2444
if strings.Contains(endpoint, "/JSSResource") {
2545
j.Sugar.Debugw("Content-Type for endpoint defaulting to XML for Classic API", "endpoint", endpoint)
46+
// TODO should this be application/xml or text/xml?
2647
return "application/xml"
2748
}
2849

@@ -43,25 +64,10 @@ func (j *Integration) getContentTypeHeader(endpoint string) string {
4364
// indicating a preference for XML. The specified MIME types cover common content formats like
4465
// images, JSON, XML, HTML, plain text, and certificates, with a fallback option for all other types.
4566
func (j *Integration) getAcceptHeader() string {
46-
weightedAcceptHeader := "application/x-x509-ca-cert;q=0.95," +
47-
"application/pkix-cert;q=0.94," +
48-
"application/pem-certificate-chain;q=0.93," +
49-
"application/octet-stream;q=0.8," + // For general binary files
50-
"image/png;q=0.75," +
51-
"image/jpeg;q=0.74," +
52-
"image/*;q=0.7," +
53-
"application/xml;q=0.65," +
54-
"text/xml;q=0.64," +
55-
"text/xml;charset=UTF-8;q=0.63," +
56-
"application/json;q=0.5," +
57-
"text/html;q=0.5," +
58-
"text/plain;q=0.4," +
59-
"*/*;q=0.05" // Fallback for any other types
60-
61-
return weightedAcceptHeader
67+
return WeightedAcceptHeader
6268
}
6369

6470
// getUserAgentHeader returns the User-Agent header string for the Jamf Pro API.
6571
func (j *Integration) getUserAgentHeader() string {
66-
return "go-api-http-client-jamfpro-integration"
72+
return UserAgentHeader
6773
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package jamfprointegration
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestIntegration_getContentTypeHeader(t *testing.T) {
8+
testIntegration := newIntegrationWithLogger()
9+
type fields struct {
10+
integration Integration
11+
}
12+
type args struct {
13+
endpoint string
14+
}
15+
tests := []struct {
16+
name string
17+
fields fields
18+
args args
19+
want string
20+
}{
21+
{
22+
name: "packages endpoint",
23+
fields: fields{
24+
integration: testIntegration,
25+
},
26+
args: args{
27+
endpoint: "http://yourserver.jamfcloud.com/api/v1/packages/upload/something else",
28+
},
29+
want: "application/octet-stream",
30+
},
31+
{
32+
name: "classic endpoint",
33+
fields: fields{
34+
integration: testIntegration,
35+
},
36+
args: args{
37+
endpoint: "http://yourserver.jamfcloud.com/JSSResource/somethingelse",
38+
},
39+
want: "application/xml",
40+
},
41+
{
42+
name: "pro endpoint",
43+
fields: fields{
44+
integration: testIntegration,
45+
},
46+
args: args{
47+
endpoint: "http://yourserver.jamfcloud.com/api/v100/cheeseburger",
48+
},
49+
want: "application/json",
50+
},
51+
{
52+
name: "unrecognised endpoint",
53+
fields: fields{
54+
integration: testIntegration,
55+
},
56+
args: args{
57+
endpoint: "http://yourserver.jamfcloud.com/spaghetti",
58+
},
59+
want: "application/json",
60+
},
61+
}
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
j := tt.fields.integration
65+
if got := j.getContentTypeHeader(tt.args.endpoint); got != tt.want {
66+
t.Errorf("Integration.getContentTypeHeader() = %v, want %v", got, tt.want)
67+
}
68+
})
69+
}
70+
}
71+
72+
func TestIntegration_getAcceptHeader(t *testing.T) {
73+
type fields struct {
74+
integration Integration
75+
}
76+
tests := []struct {
77+
name string
78+
fields fields
79+
want string
80+
}{
81+
{
82+
name: "singular",
83+
fields: fields{
84+
integration: Integration{},
85+
},
86+
want: WeightedAcceptHeader,
87+
},
88+
}
89+
for _, tt := range tests {
90+
t.Run(tt.name, func(t *testing.T) {
91+
j := tt.fields.integration
92+
if got := j.getAcceptHeader(); got != tt.want {
93+
t.Errorf("Integration.getAcceptHeader() = %v, want %v", got, tt.want)
94+
}
95+
})
96+
}
97+
}
98+
99+
func TestIntegration_getUserAgentHeader(t *testing.T) {
100+
type fields struct {
101+
integration Integration
102+
}
103+
tests := []struct {
104+
name string
105+
fields fields
106+
want string
107+
}{
108+
{
109+
name: "singular",
110+
fields: fields{
111+
integration: Integration{},
112+
},
113+
want: UserAgentHeader,
114+
},
115+
}
116+
for _, tt := range tests {
117+
t.Run(tt.name, func(t *testing.T) {
118+
j := tt.fields.integration
119+
if got := j.getUserAgentHeader(); got != tt.want {
120+
t.Errorf("Integration.getUserAgentHeader() = %v, want %v", got, tt.want)
121+
}
122+
})
123+
}
124+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package jamfprointegration
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func Test_chooseMostAlphabeticalString(t *testing.T) {
8+
type args struct {
9+
strings []string
10+
}
11+
tests := []struct {
12+
name string
13+
args args
14+
want string
15+
}{
16+
{
17+
name: "easy test - abc vs def and xyz",
18+
args: args{
19+
strings: []string{"abc", "def", "xyz"},
20+
},
21+
want: "abc",
22+
},
23+
{
24+
name: "ignore numbers",
25+
args: args{
26+
strings: []string{"123a", "jjj", "zzz"},
27+
},
28+
want: "123a",
29+
},
30+
{
31+
name: "hard test - azz vs rrs and byz",
32+
args: args{
33+
strings: []string{"byz", "rrs", "azz"},
34+
},
35+
want: "azz",
36+
},
37+
}
38+
for _, tt := range tests {
39+
t.Run(tt.name, func(t *testing.T) {
40+
if got := chooseMostAlphabeticalString(tt.args.strings); got != tt.want {
41+
t.Errorf("chooseMostAlphabeticalString() = %v, want %v", got, tt.want)
42+
}
43+
})
44+
}
45+
}

0 commit comments

Comments
 (0)