refactor: major refactor of unifi.go to split it into multiple files for cohesiveness and readability.

Even more documentation
This commit is contained in:
Mateusz Filipowicz
2025-02-10 22:59:39 +01:00
parent 473a5d0d5c
commit c6e20b675c
7 changed files with 762 additions and 574 deletions

View File

@@ -46,10 +46,10 @@ func verifyInterceptorPresence(a *assert.Assertions, c *Client, interceptors []i
}
}
func TestNewClient(t *testing.T) {
func TestNewBareClient(t *testing.T) {
t.Parallel()
a := assert.New(t)
c, err := NewClient(&ClientConfig{
c, err := NewBareClient(&ClientConfig{
URL: localUrl,
User: "admin",
Pass: "password",
@@ -58,8 +58,8 @@ func TestNewClient(t *testing.T) {
require.Error(t, err)
a.EqualValues(localUrl, c.BaseURL.String())
a.Contains(err.Error(), "connection refused", "an invalid destination should produce a connection error.")
verifyInterceptorPresence(a, c, []interface{}{&CsrfInterceptor{}, &DefaultHeadersInterceptor{}}, true)
verifyInterceptorPresence(a, c, []interface{}{&ApiKeyAuthInterceptor{}}, false)
verifyInterceptorPresence(a, c, []interface{}{&CSRFInterceptor{}, &DefaultHeadersInterceptor{}}, true)
verifyInterceptorPresence(a, c, []interface{}{&APIKeyAuthInterceptor{}}, false)
}
func TestNewClientWithApiKey(t *testing.T) {
@@ -76,8 +76,8 @@ func TestNewClientWithApiKey(t *testing.T) {
require.Error(t, err)
a.EqualValues(localUrl, c.BaseURL.String())
a.Contains(err.Error(), "connection refused", "an invalid destination should produce a connection error.")
verifyInterceptorPresence(a, c, []interface{}{&ApiKeyAuthInterceptor{}, &DefaultHeadersInterceptor{}}, true)
verifyInterceptorPresence(a, c, []interface{}{&CsrfInterceptor{}}, false)
verifyInterceptorPresence(a, c, []interface{}{&APIKeyAuthInterceptor{}, &DefaultHeadersInterceptor{}}, true)
verifyInterceptorPresence(a, c, []interface{}{&CSRFInterceptor{}}, false)
}
func TestCustomizeHttpClient(t *testing.T) {
@@ -418,23 +418,27 @@ func TestAuthConfigurationValidation(t *testing.T) {
{"test", "test", "test", true},
}
v, err := newValidator()
require.NoError(t, err)
for _, tc := range testCases {
t.Run(fmt.Sprintf("user:%s-pass:%s-apikey:%s", tc.User, tc.Pass, tc.APIKey), func(t *testing.T) {
t.Parallel()
// given
_, err := NewClient(&ClientConfig{
cc := &ClientConfig{
URL: testUrl,
User: tc.User,
Pass: tc.Pass,
APIKey: tc.APIKey,
})
}
// when
err = v.Validate(cc)
// then
if tc.shouldFail {
require.ErrorContains(t, err, "validation failed")
return
}
require.ErrorContains(t, err, "dial tcp") // error will anyway exist, but it will be not related to config
require.NoError(t, err)
})
}
}
@@ -461,22 +465,75 @@ func TestUrlValidation(t *testing.T) {
t.Run(tc.URL, func(t *testing.T) {
t.Parallel()
// given
_, err := NewClient(&ClientConfig{
cc := &ClientConfig{
URL: tc.URL,
APIKey: "test-key",
})
}
v, err := newValidator()
require.NoError(t, err)
// when
err = v.Validate(cc)
// then
if tc.shouldFail {
require.ErrorContains(t, err, "validation failed")
require.ErrorContains(t, err, tc.errorString)
return
}
require.ErrorContains(t, err, "dial tcp") // error will anyway exist, but it will be not related to config
require.NoError(t, err)
})
}
}
func TestValidationModeValidation(t *testing.T) {
t.Parallel()
testCases := []struct {
validationMode validationMode
expectedError string
}{
{SoftValidation, ""},
{HardValidation, ""},
{DisableValidation, ""},
{"invalid", "must be one of"},
}
for _, tc := range testCases {
t.Run(string(tc.validationMode), func(t *testing.T) {
t.Parallel()
// given
cc := &ClientConfig{
URL: testUrl,
APIKey: "test-key",
ValidationMode: tc.validationMode,
}
v, err := newValidator()
require.NoError(t, err)
// when
err = v.Validate(cc)
// then
if tc.expectedError != "" {
require.ErrorContains(t, err, tc.expectedError)
return
}
require.NoError(t, err)
})
}
}
func TestClientConfigValidationExecutedOnNewClient(t *testing.T) {
t.Parallel()
a := assert.New(t)
// given
cc := &ClientConfig{URL: "invalid URL"}
// when
c, err := NewClient(cc)
// then
require.ErrorContains(t, err, "validation failed")
a.Nil(c)
}
type validateableBody struct {
Data string `json:"data" validate:"required"`
}
@@ -584,7 +641,7 @@ func TestGetSystemInformation(t *testing.T) {
VerifySSL: false,
})
sysInfo, err := c.getSystemInformation()
sysInfo, err := c.GetSystemInformation()
if tc.expectedError != "" {
require.ErrorContains(t, err, tc.expectedError)
@@ -602,17 +659,17 @@ func TestParseBaseUrl(t *testing.T) {
a := assert.New(t)
// Valid URL without /api in the path.
base, err := parseBaseUrl("http://localhost")
base, err := parseBaseURL("http://localhost")
require.NoError(t, err)
a.Equal("http", base.Scheme)
a.Equal("", base.Path)
// URL with trailing slash /api/
_, err = parseBaseUrl("http://localhost/api/")
_, err = parseBaseURL("http://localhost/api/")
require.ErrorContains(t, err, "expected a base URL without the `/api`")
// URL with /api in path (no trailing slash).
_, err = parseBaseUrl("http://localhost/api")
_, err = parseBaseURL("http://localhost/api")
require.ErrorContains(t, err, "expected a base URL without the `/api`")
}
@@ -642,10 +699,10 @@ func TestRegisterInterceptor(t *testing.T) {
// Create a dummy interceptor (using TestInterceptor already defined in the file).
var dummy ClientInterceptor = &TestInterceptor{}
initialCount := len(client.interceptors)
client.RegisterInterceptor(&dummy)
client.AddInterceptor(&dummy)
assert.Len(t, client.interceptors, initialCount+1)
// Attempt to add the same interceptor again.
client.RegisterInterceptor(&dummy)
client.AddInterceptor(&dummy)
assert.Len(t, client.interceptors, initialCount+1)
}
@@ -730,7 +787,7 @@ func TestCreateRequestURLInvalid(t *testing.T) {
BaseURL: &url.URL{Scheme: "http", Host: "localhost"},
apiPaths: &NewStyleAPI,
}
_, err := c.createRequestURL("://bad-url")
_, err := c.buildRequestURL("://bad-url")
require.Error(t, err)
assert.Contains(t, err.Error(), "parse")
}
@@ -741,7 +798,7 @@ func TestCreateRequestURLAbsolute(t *testing.T) {
BaseURL: &url.URL{Scheme: "http", Host: "localhost"},
apiPaths: &NewStyleAPI,
}
reqURL, err := c.createRequestURL("http://example.com/test")
reqURL, err := c.buildRequestURL("http://example.com/test")
require.NoError(t, err)
assert.Equal(t, "http://example.com/test", reqURL.String())
}
@@ -749,9 +806,9 @@ func TestCreateRequestURLAbsolute(t *testing.T) {
func TestCreateRequestContextTimeout(t *testing.T) {
t.Parallel()
c := &Client{
config: &ClientConfig{Timeout: 100 * time.Millisecond},
timeout: 100 * time.Millisecond,
}
ctx, cancel := c.createRequestContext()
ctx, cancel := c.newRequestContext()
defer cancel()
_, ok := ctx.Deadline()
require.True(t, ok)
@@ -787,9 +844,7 @@ func TestLoginWithAPIKeyDirect(t *testing.T) {
t.Parallel()
// Create a client manually with the APIKey set.
c := &Client{
config: &ClientConfig{
APIKey: "abc",
},
credentials: APIKeyCredentials{APIKey: "abc"},
}
err := c.Login()
assert.NoError(t, err)