Skip to content

Commit 31055fe

Browse files
committed
test: add unit tests for utility functions & data processing
1 parent c5fb6fb commit 31055fe

File tree

4 files changed

+600
-0
lines changed

4 files changed

+600
-0
lines changed

mirroring/instance_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package mirroring
2+
3+
import (
4+
"testing"
5+
6+
gitlab "gitlab.com/gitlab-org/api/client-go"
7+
)
8+
9+
func TestNewGitlabInstance(t *testing.T) {
10+
gitlabURL := "https://gitlab.example.com"
11+
gitlabToken := "test-token"
12+
13+
instance, err := newGitlabInstance(gitlabURL, gitlabToken)
14+
if err != nil {
15+
t.Fatalf("expected no error, got %v", err)
16+
}
17+
18+
if instance.Gitlab == nil {
19+
t.Error("expected Gitlab client to be initialized")
20+
}
21+
22+
if instance.Projects == nil {
23+
t.Error("expected Projects map to be initialized")
24+
}
25+
26+
if instance.Groups == nil {
27+
t.Error("expected Groups map to be initialized")
28+
}
29+
30+
if instance.GraphQLClient == nil {
31+
t.Error("expected GraphQLClient to be initialized")
32+
}
33+
}
34+
35+
func TestAddProject(t *testing.T) {
36+
instance := &GitlabInstance{
37+
Projects: make(map[string]*gitlab.Project),
38+
}
39+
40+
projectPath := "test/project"
41+
project := &gitlab.Project{Name: "Test Project"}
42+
43+
instance.addProject(projectPath, project)
44+
45+
if got, exists := instance.Projects[projectPath]; !exists {
46+
t.Fatalf("expected project %s to be added", projectPath)
47+
} else if got != project {
48+
t.Errorf("expected project %v, got %v", project, got)
49+
}
50+
}
51+
52+
func TestGetProject(t *testing.T) {
53+
instance := &GitlabInstance{
54+
Projects: make(map[string]*gitlab.Project),
55+
}
56+
57+
projectPath := "test/project"
58+
project := &gitlab.Project{Name: "Test Project"}
59+
instance.Projects[projectPath] = project
60+
61+
got := instance.getProject(projectPath)
62+
if got != project {
63+
t.Errorf("expected project %v, got %v", project, got)
64+
}
65+
66+
nonExistentPath := "non/existent"
67+
if got := instance.getProject(nonExistentPath); got != nil {
68+
t.Errorf("expected nil, got %v", got)
69+
}
70+
}
71+
72+
func TestAddGroup(t *testing.T) {
73+
instance := &GitlabInstance{
74+
Groups: make(map[string]*gitlab.Group),
75+
}
76+
77+
groupPath := "test/group"
78+
group := &gitlab.Group{Name: "Test Group"}
79+
80+
instance.addGroup(groupPath, group)
81+
82+
if got, exists := instance.Groups[groupPath]; !exists {
83+
t.Fatalf("expected group %s to be added", groupPath)
84+
} else if got != group {
85+
t.Errorf("expected group %v, got %v", group, got)
86+
}
87+
}
88+
89+
func TestGetGroup(t *testing.T) {
90+
instance := &GitlabInstance{
91+
Groups: make(map[string]*gitlab.Group),
92+
}
93+
94+
groupPath := "test/group"
95+
group := &gitlab.Group{Name: "Test Group"}
96+
instance.Groups[groupPath] = group
97+
98+
got := instance.getGroup(groupPath)
99+
if got != group {
100+
t.Errorf("expected group %v, got %v", group, got)
101+
}
102+
103+
nonExistentPath := "non/existent"
104+
if got := instance.getGroup(nonExistentPath); got != nil {
105+
t.Errorf("expected nil, got %v", got)
106+
}
107+
}

mirroring/main_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package mirroring
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/boxboxjason/gitlab-sync/utils"
8+
)
9+
10+
func TestProcessFilters(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
mirrorMapping *utils.MirrorMapping
14+
expectedSourceProjectFilters map[string]bool
15+
expectedSourceGroupFilters map[string]bool
16+
expectedDestinationProjectFilters map[string]bool
17+
expectedDestinationGroupFilters map[string]bool
18+
}{
19+
{
20+
name: "EmptyMirrorMapping",
21+
mirrorMapping: &utils.MirrorMapping{
22+
Projects: make(map[string]*utils.ProjectMirroringOptions),
23+
Groups: make(map[string]*utils.GroupMirroringOptions),
24+
},
25+
expectedSourceProjectFilters: map[string]bool{},
26+
expectedSourceGroupFilters: map[string]bool{},
27+
expectedDestinationProjectFilters: map[string]bool{},
28+
expectedDestinationGroupFilters: map[string]bool{},
29+
},
30+
{
31+
name: "SingleProjectAndGroup",
32+
mirrorMapping: &utils.MirrorMapping{
33+
Projects: map[string]*utils.ProjectMirroringOptions{
34+
"sourceProject": {
35+
DestinationPath: "destinationGroupPath/destinationProjectPath",
36+
CI_CD_Catalog: true,
37+
Issues: true,
38+
},
39+
},
40+
Groups: map[string]*utils.GroupMirroringOptions{
41+
"sourceGroup": {
42+
DestinationPath: "destinationGroupPath",
43+
CI_CD_Catalog: true,
44+
Issues: true,
45+
},
46+
},
47+
},
48+
expectedSourceProjectFilters: map[string]bool{
49+
"sourceProject": true,
50+
},
51+
expectedSourceGroupFilters: map[string]bool{
52+
"sourceGroup": true,
53+
},
54+
expectedDestinationProjectFilters: map[string]bool{
55+
"destinationGroupPath/destinationProjectPath": true,
56+
},
57+
expectedDestinationGroupFilters: map[string]bool{
58+
"destinationGroupPath": true,
59+
},
60+
},
61+
{
62+
name: "MultipleProjectsAndGroups",
63+
mirrorMapping: &utils.MirrorMapping{
64+
Projects: map[string]*utils.ProjectMirroringOptions{
65+
"sourceProject1": {
66+
DestinationPath: "destinationGroupPath1/destinationProjectPath1",
67+
CI_CD_Catalog: true,
68+
Issues: true,
69+
},
70+
"sourceProject2": {
71+
DestinationPath: "destinationGroupPath2/destinationProjectPath2",
72+
CI_CD_Catalog: false,
73+
Issues: false,
74+
},
75+
},
76+
Groups: map[string]*utils.GroupMirroringOptions{
77+
"sourceGroup1": {
78+
DestinationPath: "destinationGroupPath3",
79+
CI_CD_Catalog: true,
80+
Issues: true,
81+
},
82+
"sourceGroup2": {
83+
DestinationPath: "destinationGroupPath4",
84+
CI_CD_Catalog: false,
85+
Issues: false,
86+
},
87+
},
88+
},
89+
expectedSourceProjectFilters: map[string]bool{
90+
"sourceProject1": true,
91+
"sourceProject2": true,
92+
},
93+
expectedSourceGroupFilters: map[string]bool{
94+
"sourceGroup1": true,
95+
"sourceGroup2": true,
96+
},
97+
expectedDestinationProjectFilters: map[string]bool{
98+
"destinationGroupPath1/destinationProjectPath1": true,
99+
"destinationGroupPath2/destinationProjectPath2": true,
100+
},
101+
expectedDestinationGroupFilters: map[string]bool{
102+
"destinationGroupPath1": true,
103+
"destinationGroupPath2": true,
104+
"destinationGroupPath3": true,
105+
"destinationGroupPath4": true,
106+
},
107+
},
108+
}
109+
110+
for _, tt := range tests {
111+
t.Run(tt.name, func(t *testing.T) {
112+
sourceProjectFilters, sourceGroupFilters, destinationProjectFilters, destinationGroupFilters := processFilters(tt.mirrorMapping)
113+
114+
if !reflect.DeepEqual(sourceProjectFilters, tt.expectedSourceProjectFilters) {
115+
t.Errorf("expected sourceProjectFilters %v, got %v", tt.expectedSourceProjectFilters, sourceProjectFilters)
116+
}
117+
118+
if !reflect.DeepEqual(sourceGroupFilters, tt.expectedSourceGroupFilters) {
119+
t.Errorf("expected sourceGroupFilters %v, got %v", tt.expectedSourceGroupFilters, sourceGroupFilters)
120+
}
121+
122+
if !reflect.DeepEqual(destinationProjectFilters, tt.expectedDestinationProjectFilters) {
123+
t.Errorf("expected destinationProjectFilters %v, got %v", tt.expectedDestinationProjectFilters, destinationProjectFilters)
124+
}
125+
126+
if !reflect.DeepEqual(destinationGroupFilters, tt.expectedDestinationGroupFilters) {
127+
t.Errorf("expected destinationGroupFilters %v, got %v", tt.expectedDestinationGroupFilters, destinationGroupFilters)
128+
}
129+
})
130+
}
131+
}

utils/logger_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package utils
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"log"
7+
"strings"
8+
"sync"
9+
"testing"
10+
)
11+
12+
// TestSetGetVerbose tests setting and getting the verbose flag
13+
func TestSetGetVerbose(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
verbose bool
17+
expected bool
18+
}{
19+
{name: "SetVerboseTrue", verbose: true, expected: true},
20+
{name: "SetVerboseFalse", verbose: false, expected: false},
21+
}
22+
23+
for _, tt := range tests {
24+
t.Run(tt.name, func(t *testing.T) {
25+
SetVerbose(tt.verbose)
26+
if got := getVerbose(); got != tt.expected {
27+
t.Errorf("getVerbose() = %v, want %v", got, tt.expected)
28+
}
29+
})
30+
}
31+
}
32+
33+
// TestLogVerbose tests logging a message if verbose logging is enabled
34+
func TestLogVerbose(t *testing.T) {
35+
tests := []struct {
36+
name string
37+
verbose bool
38+
message string
39+
shouldLog bool
40+
expectedLog string
41+
}{
42+
{name: "VerboseEnabled", verbose: true, message: "Test message", shouldLog: true, expectedLog: "Test message"},
43+
{name: "VerboseDisabled", verbose: false, message: "Test message", shouldLog: false, expectedLog: ""},
44+
}
45+
46+
var mu sync.Mutex
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
SetVerbose(tt.verbose)
50+
51+
var buf bytes.Buffer
52+
logger := log.New(&buf, "", 0)
53+
log.SetOutput(logger.Writer())
54+
defer log.SetOutput(nil)
55+
56+
LogVerbose(tt.message)
57+
58+
mu.Lock()
59+
defer mu.Unlock()
60+
if tt.shouldLog && !strings.Contains(buf.String(), tt.expectedLog) {
61+
t.Errorf("LogVerbose() did not log expected message. got = %v, want %v", buf.String(), tt.expectedLog)
62+
}
63+
if !tt.shouldLog && buf.String() != tt.expectedLog {
64+
t.Errorf("LogVerbose() logged message when it shouldn't have. got = %v, want %v", buf.String(), tt.expectedLog)
65+
}
66+
})
67+
}
68+
}
69+
70+
// TestLogVerbosef tests logging a formatted message if verbose logging is enabled
71+
func TestLogVerbosef(t *testing.T) {
72+
tests := []struct {
73+
name string
74+
verbose bool
75+
format string
76+
args []interface{}
77+
shouldLog bool
78+
expectedLog string
79+
}{
80+
{name: "VerboseEnabled", verbose: true, format: "Test %s", args: []interface{}{"message"}, shouldLog: true, expectedLog: "Test message"},
81+
{name: "VerboseDisabled", verbose: false, format: "Test %s", args: []interface{}{"message"}, shouldLog: false, expectedLog: ""},
82+
}
83+
84+
var mu sync.Mutex
85+
for _, tt := range tests {
86+
t.Run(tt.name, func(t *testing.T) {
87+
SetVerbose(tt.verbose)
88+
89+
var buf bytes.Buffer
90+
logger := log.New(&buf, "", 0)
91+
log.SetOutput(logger.Writer())
92+
defer log.SetOutput(nil)
93+
94+
LogVerbosef(tt.format, tt.args...)
95+
96+
mu.Lock()
97+
defer mu.Unlock()
98+
if tt.shouldLog && !strings.Contains(buf.String(), tt.expectedLog) {
99+
t.Errorf("LogVerbosef() did not log expected message. got = %v, want %v", buf.String(), tt.expectedLog)
100+
}
101+
if !tt.shouldLog && buf.String() != tt.expectedLog {
102+
t.Errorf("LogVerbosef() logged message when it shouldn't have. got = %v, want %v", buf.String(), tt.expectedLog)
103+
}
104+
})
105+
}
106+
}
107+
108+
// TestMergeErrors tests merging errors from a channel
109+
func TestMergeErrors(t *testing.T) {
110+
tests := []struct {
111+
name string
112+
errors []error
113+
indent int
114+
expectedErr string
115+
}{
116+
{name: "NoErrors", errors: nil, indent: 2, expectedErr: ""},
117+
{name: "SingleError", errors: []error{errors.New("error 1")}, indent: 2, expectedErr: "\n - error 1\n"},
118+
{name: "MultipleErrors", errors: []error{errors.New("error 1"), errors.New("error 2")}, indent: 2, expectedErr: "\n - error 1\n - error 2\n"},
119+
}
120+
121+
for _, tt := range tests {
122+
t.Run(tt.name, func(t *testing.T) {
123+
errorsChan := make(chan error, len(tt.errors))
124+
for _, err := range tt.errors {
125+
errorsChan <- err
126+
}
127+
close(errorsChan)
128+
129+
err := MergeErrors(errorsChan, tt.indent)
130+
if err != nil && err.Error() != tt.expectedErr {
131+
t.Errorf("MergeErrors() error = %v, want %v", err.Error(), tt.expectedErr)
132+
}
133+
if err == nil && tt.expectedErr != "" {
134+
t.Errorf("MergeErrors() error = nil, want %v", tt.expectedErr)
135+
}
136+
})
137+
}
138+
}

0 commit comments

Comments
 (0)