Skip to content

Commit

Permalink
test: added more tests and implemented mocks
Browse files Browse the repository at this point in the history
Signed-off-by: Bruno Bressi <[email protected]>
  • Loading branch information
puffitos committed Dec 14, 2023
1 parent e19ccd4 commit 97a48e2
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 87 deletions.
100 changes: 54 additions & 46 deletions pkg/sparrow/gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"

Expand All @@ -31,9 +32,9 @@ type Client struct {
client *http.Client
}

func New(url, token string, pid int) Gitlab {
func New(baseURL, token string, pid int) Gitlab {
return &Client{
baseUrl: url,
baseUrl: baseURL,
token: token,
projectID: pid,
client: &http.Client{},
Expand All @@ -49,60 +50,66 @@ func (g *Client) FetchFiles(ctx context.Context) ([]checks.GlobalTarget, error)
return nil, err
}

result, err := g.fetchFiles(ctx, fl)
if err != nil {
log.Error("Failed to fetch files", "error", err)
return nil, err
}
log.Info("Successfully fetched all target files", "files", len(result))
return result, nil
}

// fetchFiles fetches the files from the global targets repository from the configured gitlab repository
func (g *Client) fetchFiles(ctx context.Context, fl []string) ([]checks.GlobalTarget, error) {
var result []checks.GlobalTarget
log := logger.FromContext(ctx).With("name", "fetchFiles")
log.Debug("Fetching global files")
for _, f := range fl {
// URL encode the name
n := url.PathEscape(f)
req, err := http.NewRequestWithContext(ctx,
http.MethodGet,
fmt.Sprintf("%s/api/v4/projects/%d/repository/files/%s/raw?ref=main", g.baseUrl, g.projectID, n),
http.NoBody,
)
gl, err := g.fetchFile(ctx, f)
if err != nil {
log.Error("Failed to create request", "error", err)
log.Error("Failed fetching files", f, "error", err)
return nil, err
}
req.Header.Add("PRIVATE-TOKEN", g.token)
req.Header.Add("Content-Type", "application/json")
result = append(result, gl)
}
log.Info("Successfully fetched all target files", "files", len(result))
return result, nil
}

res, err := g.client.Do(req)
if err != nil {
log.Error("Failed to fetch file", "file", f, "error", err)
return nil, err
}
if res.StatusCode != http.StatusOK {
log.Error("Failed to fetch file", "status", res.Status)
return nil, fmt.Errorf("request failed, status is %s", res.Status)
}
// fetchFile fetches the file from the global targets repository from the configured gitlab repository
func (g *Client) fetchFile(ctx context.Context, f string) (checks.GlobalTarget, error) {
log := logger.FromContext(ctx)
var res checks.GlobalTarget
// URL encode the name
n := url.PathEscape(f)
req, err := http.NewRequestWithContext(ctx,
http.MethodGet,
fmt.Sprintf("%s/api/v4/projects/%d/repository/files/%s/raw?ref=main", g.baseUrl, g.projectID, n),
http.NoBody,
)
if err != nil {
log.Error("Failed to create request", "error", err)
return res, err
}
req.Header.Add("PRIVATE-TOKEN", g.token)
req.Header.Add("Content-Type", "application/json")

defer res.Body.Close()
var gt checks.GlobalTarget
err = json.NewDecoder(res.Body).Decode(&gt)
resp, err := g.client.Do(req) //nolint:bodyclose // closed in defer
if err != nil {
log.Error("Failed to fetch file", "file", f, "error", err)
return res, err
}
if resp.StatusCode != http.StatusOK {
log.Error("Failed to fetch file", "status", resp.Status)
return res, fmt.Errorf("request failed, status is %s", resp.Status)
}

defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
log.Error("Failed to decode file after fetching", "file", f, "error", err)
return nil, err
log.Error("Failed to close response body", "error", err)
}
}(resp.Body)

log.Debug("Successfully fetched file", "file", f)
result = append(result, gt)
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
log.Error("Failed to decode file after fetching", "file", f, "error", err)
return res, err
}
return result, nil

log.Debug("Successfully fetched file", "file", f)
return res, nil
}

// fetchFileList fetches the files from the global targets repository from the configured gitlab repository
// fetchFileList fetches the filenames from the global targets repository from the configured gitlab repository,
// so they may be fetched individually
func (g *Client) fetchFileList(ctx context.Context) ([]string, error) {
log := logger.FromContext(ctx).With("name", "fetchFileList")
log.Debug("Fetching global files")
Expand Down Expand Up @@ -152,7 +159,7 @@ func (g *Client) fetchFileList(ctx context.Context) ([]string, error) {

// PutFile commits the current instance to the configured gitlab repository
// as a global target for other sparrow instances to discover
func (g *Client) PutFile(ctx context.Context, body File) error {
func (g *Client) PutFile(ctx context.Context, body File) error { //nolint: dupl,gocritic // no need to refactor yet
log := logger.FromContext(ctx).With("name", "AddRegistration")
log.Debug("Registering sparrow instance to gitlab")

Expand Down Expand Up @@ -194,7 +201,7 @@ func (g *Client) PutFile(ctx context.Context, body File) error {

// PostFile commits the current instance to the configured gitlab repository
// as a global target for other sparrow instances to discover
func (g *Client) PostFile(ctx context.Context, body File) error {
func (g *Client) PostFile(ctx context.Context, body File) error { //nolint:dupl,gocritic // no need to refactor yet
log := logger.FromContext(ctx).With("name", "AddRegistration")
log.Debug("Registering sparrow instance to gitlab")

Expand Down Expand Up @@ -234,6 +241,7 @@ func (g *Client) PostFile(ctx context.Context, body File) error {
return nil
}

// File represents a File manipulation operation via the Gitlab API
type File struct {
Branch string `json:"branch"`
AuthorEmail string `json:"author_email"`
Expand All @@ -244,7 +252,7 @@ type File struct {
}

// Bytes returns the bytes of the File
func (g File) Bytes() ([]byte, error) {
func (g *File) Bytes() ([]byte, error) {
b, err := json.Marshal(g)
return b, err
}
61 changes: 41 additions & 20 deletions pkg/sparrow/gitlab/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,15 @@ func Test_gitlab_fetchFileList(t *testing.T) {

// The filelist and url are the same, so we HTTP responders can
// be created without much hassle
func Test_gitlab_fetchFiles(t *testing.T) {
func Test_gitlab_FetchFiles(t *testing.T) {
type file struct {
Name string `json:"name"`
}

tests := []struct {
name string
want []checks.GlobalTarget
fileList []string
fileList []file
wantErr bool
mockCode int
}{
Expand All @@ -120,8 +124,10 @@ func Test_gitlab_fetchFiles(t *testing.T) {
LastSeen: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
fileList: []string{
"test",
fileList: []file{
{
Name: "test",
},
},
wantErr: false,
mockCode: http.StatusOK,
Expand All @@ -138,9 +144,13 @@ func Test_gitlab_fetchFiles(t *testing.T) {
LastSeen: time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC),
},
},
fileList: []string{
"test",
"test2",
fileList: []file{
{
Name: "test",
},
{
Name: "test2",
},
},
wantErr: false,
mockCode: http.StatusOK,
Expand All @@ -164,10 +174,16 @@ func Test_gitlab_fetchFiles(t *testing.T) {
if err != nil {
t.Fatalf("error creating mock response: %v", err)
}
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i]), resp)
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i].Name), resp)
}

got, err := g.fetchFiles(context.Background(), tt.fileList)
resp, err := httpmock.NewJsonResponder(tt.mockCode, tt.fileList)
if err != nil {
t.Fatalf("error creating mock response: %v", err)
}
httpmock.RegisterResponder("GET", "http://test/api/v4/projects/1/repository/tree?ref=main", resp)

got, err := g.FetchFiles(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("FetchFiles() error = %v, wantErr %v", err, tt.wantErr)
}
Expand All @@ -179,6 +195,9 @@ func Test_gitlab_fetchFiles(t *testing.T) {
}

func Test_gitlab_fetchFiles_error_cases(t *testing.T) {
type file struct {
Name string `json:"name"`
}
type mockResponses struct {
response checks.GlobalTarget
err bool
Expand All @@ -187,7 +206,7 @@ func Test_gitlab_fetchFiles_error_cases(t *testing.T) {
tests := []struct {
name string
mockResponses []mockResponses
fileList []string
fileList []file
}{
{
name: "failure - direct API error",
Expand All @@ -196,8 +215,10 @@ func Test_gitlab_fetchFiles_error_cases(t *testing.T) {
err: true,
},
},
fileList: []string{
"test",
fileList: []file{
{
Name: "test",
},
},
},
{
Expand All @@ -215,9 +236,9 @@ func Test_gitlab_fetchFiles_error_cases(t *testing.T) {
err: true,
},
},
fileList: []string{
"test",
"test2-will-fail",
fileList: []file{
{Name: "test"},
{Name: "test2-will-fail"},
},
},
}
Expand All @@ -236,25 +257,25 @@ func Test_gitlab_fetchFiles_error_cases(t *testing.T) {
for i, target := range tt.mockResponses {
if target.err {
errResp := httpmock.NewStringResponder(http.StatusInternalServerError, "")
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i]), errResp)
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i].Name), errResp)
continue
}
resp, err := httpmock.NewJsonResponder(http.StatusOK, target)
if err != nil {
t.Fatalf("error creating mock response: %v", err)
}
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i]), resp)
httpmock.RegisterResponder("GET", fmt.Sprintf("http://test/api/v4/projects/1/repository/files/%s/raw?ref=main", tt.fileList[i].Name), resp)
}

_, err := g.fetchFiles(context.Background(), tt.fileList)
_, err := g.FetchFiles(context.Background())
if err == nil {
t.Fatalf("Expected error but got none.")
}
})
}
}

func TestClient_PutFile(t *testing.T) {
func TestClient_PutFile(t *testing.T) { //nolint:dupl // no need to refactor yet
now := time.Now()
tests := []struct {
name string
Expand Down Expand Up @@ -328,7 +349,7 @@ func TestClient_PutFile(t *testing.T) {
}
}

func TestClient_PostFile(t *testing.T) {
func TestClient_PostFile(t *testing.T) { //nolint:dupl // no need to refactor yet
now := time.Now()
tests := []struct {
name string
Expand Down
48 changes: 33 additions & 15 deletions pkg/sparrow/gitlab/test/mockclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,55 @@ package gitlabmock

import (
"context"
"fmt"

"github.com/caas-team/sparrow/internal/logger"
"github.com/caas-team/sparrow/pkg/checks"
"github.com/caas-team/sparrow/pkg/sparrow/gitlab"
)

type MockClient struct {
targets []checks.GlobalTarget
err error
targets []checks.GlobalTarget
fetchFilesErr error
putFileErr error
postFileErr error
}

func (m MockClient) PutFile(ctx context.Context, file gitlab.File) error {
panic("implement me")
func (m *MockClient) PutFile(ctx context.Context, _ gitlab.File) error { //nolint: gocritic // irrelevant
log := logger.FromContext(ctx).With("name", "MockPutFile")
log.Debug("MockPutFile called", "err", m.putFileErr)
return m.putFileErr
}

func (m MockClient) PostFile(ctx context.Context, f gitlab.File) error {
panic("implement me")
func (m *MockClient) PostFile(ctx context.Context, _ gitlab.File) error { //nolint: gocritic // irrelevant
log := logger.FromContext(ctx).With("name", "MockPostFile")
log.Debug("MockPostFile called", "err", m.postFileErr)
return m.postFileErr
}

func (m MockClient) FetchFiles(ctx context.Context) ([]checks.GlobalTarget, error) {
return m.targets, m.err
func (m *MockClient) FetchFiles(ctx context.Context) ([]checks.GlobalTarget, error) {
log := logger.FromContext(ctx).With("name", "MockFetchFiles")
log.Debug("MockFetchFiles called", "targets", len(m.targets), "err", m.fetchFilesErr)
return m.targets, m.fetchFilesErr
}

// SetFetchFilesErr sets the error returned by FetchFiles
func (m *MockClient) SetFetchFilesErr(err error) {
m.fetchFilesErr = err
}

// SetPutFileErr sets the error returned by PutFile
func (m *MockClient) SetPutFileErr(err error) {
m.putFileErr = err
}

// SetPostFileErr sets the error returned by PostFile
func (m *MockClient) SetPostFileErr(err error) {
m.postFileErr = err
}

// New creates a new MockClient to mock Gitlab interaction
func New(targets []checks.GlobalTarget, err bool) gitlab.Gitlab {
var e error
if err {
e = fmt.Errorf("error")
}
func New(targets []checks.GlobalTarget) *MockClient {
return &MockClient{
targets: targets,
err: e,
}
}
Loading

0 comments on commit 97a48e2

Please sign in to comment.