diff --git a/.golangci.yml b/.golangci.yml index ca0e978..8f2ad79 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,6 +29,8 @@ linters: - time - github.com/spf13/cobra - github.com/spf13/afero + - github.com/stretchr/testify/assert + - github.com/stretchr/testify/require - gopkg.in/yaml.v3 - backup-rsync/backup/internal - backup-rsync/backup/cmd diff --git a/backup/internal/check_test.go b/backup/internal/check_test.go index a755049..ae88a36 100644 --- a/backup/internal/check_test.go +++ b/backup/internal/check_test.go @@ -5,15 +5,15 @@ import ( "log" "path/filepath" "sort" - "strings" "testing" "backup-rsync/backup/internal" "github.com/spf13/afero" + "github.com/stretchr/testify/assert" ) -func TestIsExcludedGlobally(t *testing.T) { +func TestIsExcludedGlobally_PathGloballyExcluded(t *testing.T) { sources := []internal.Path{ { Path: "/home/data/", @@ -25,48 +25,62 @@ func TestIsExcludedGlobally(t *testing.T) { }, } - tests := []struct { - name string - path string - expectsError bool - expectedLog string - }{ + var logBuffer bytes.Buffer + log.SetOutput(&logBuffer) + + path := "/home/data/projects/P1" + expectsError := true + expectedLog := "Path '/home/data/projects/P1' is globally excluded by '/projects/P1/' in source '/home/data/'" + + result := internal.IsExcludedGlobally(path, sources) + assert.Equal(t, expectsError, result) + assert.Contains(t, logBuffer.String(), expectedLog) +} + +func TestIsExcludedGlobally_PathNotExcluded(t *testing.T) { + sources := []internal.Path{ { - name: "Path is globally excluded", - path: "/home/data/projects/P1", - expectsError: true, - expectedLog: "Path '/home/data/projects/P1' is globally excluded by '/projects/P1/' in source '/home/data/'", + Path: "/home/data/", + Exclusions: []string{"/projects/P1/", "/media/"}, }, { - name: "Path is not excluded", - path: "/home/data/projects/Other", - expectsError: false, + Path: "/home/user/", + Exclusions: []string{"/cache/", "/npm/"}, }, + } + + var logBuffer bytes.Buffer + log.SetOutput(&logBuffer) + + path := "/home/data/projects/Other" + expectsError := false + + result := internal.IsExcludedGlobally(path, sources) + assert.Equal(t, expectsError, result) +} + +func TestIsExcludedGlobally_PathExcludedInAnotherSource(t *testing.T) { + sources := []internal.Path{ { - name: "Path is excluded in another source", - path: "/home/user/cache", - expectsError: true, - expectedLog: "Path '/home/user/cache' is globally excluded by '/cache/' in source '/home/user/'", + Path: "/home/data/", + Exclusions: []string{"/projects/P1/", "/media/"}, + }, + { + Path: "/home/user/", + Exclusions: []string{"/cache/", "/npm/"}, }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var logBuffer bytes.Buffer - log.SetOutput(&logBuffer) - - result := internal.IsExcludedGlobally(test.path, sources) - if result != test.expectsError { - t.Errorf("Expected exclusion result %v, got %v", test.expectsError, result) - } - - if test.expectsError { - if !strings.Contains(logBuffer.String(), test.expectedLog) { - t.Errorf("Expected log message '%s', but got '%s'", test.expectedLog, logBuffer.String()) - } - } - }) - } + var logBuffer bytes.Buffer + log.SetOutput(&logBuffer) + + path := "/home/user/cache" + expectsError := true + expectedLog := "Path '/home/user/cache' is globally excluded by '/cache/' in source '/home/user/'" + + result := internal.IsExcludedGlobally(path, sources) + assert.Equal(t, expectsError, result) + assert.Contains(t, logBuffer.String(), expectedLog) } func runListUncoveredPathsTest( @@ -95,24 +109,8 @@ func runListUncoveredPathsTest( sort.Strings(uncoveredPaths) sort.Strings(expectedUncoveredPaths) - if len(uncoveredPaths) != len(expectedUncoveredPaths) { - t.Errorf("Expected uncovered paths length %d, got %d. Expected: %v, Got: %v", - len(expectedUncoveredPaths), len(uncoveredPaths), expectedUncoveredPaths, uncoveredPaths) - - return - } - - for count, path := range uncoveredPaths { - if count >= len(expectedUncoveredPaths) { - t.Errorf("Got more uncovered paths than expected. Got: %v", uncoveredPaths) - - return - } - - if path != expectedUncoveredPaths[count] { - t.Errorf("Expected uncovered path '%s', got '%s'", expectedUncoveredPaths[count], path) - } - } + assert.Len(t, uncoveredPaths, len(expectedUncoveredPaths)) + assert.ElementsMatch(t, expectedUncoveredPaths, uncoveredPaths) } // Variation: all paths used. diff --git a/backup/internal/config_test.go b/backup/internal/config_test.go index a6058ee..b7356fc 100644 --- a/backup/internal/config_test.go +++ b/backup/internal/config_test.go @@ -2,9 +2,10 @@ package internal_test import ( "bytes" - "strings" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" "backup-rsync/backup/internal" @@ -24,30 +25,15 @@ jobs: reader := bytes.NewReader([]byte(yamlData)) cfg, err := internal.LoadConfig(reader) - if err != nil { - t.Fatalf("Failed to load config: %v", err) - } + require.NoError(t, err) - if cfg.Variables["target_base"] != "/mnt/backup1" { - t.Errorf("Expected /mnt/backup1, got %s", cfg.Variables["target_base"]) - } - - if len(cfg.Jobs) != 1 { - t.Fatalf("Expected 1 job, got %d", len(cfg.Jobs)) - } + assert.Equal(t, "/mnt/backup1", cfg.Variables["target_base"]) + assert.Len(t, cfg.Jobs, 1) job := cfg.Jobs[0] - if job.Name != "test_job" { - t.Errorf("Expected job name test_job, got %s", job.Name) - } - - if job.Source != "/home/test/" { - t.Errorf("Expected source /home/test/, got %s", job.Source) - } - - if job.Target != "${target_base}/test/" { - t.Errorf("Expected target ${target_base}/test/, got %s", job.Target) - } + assert.Equal(t, "test_job", job.Name) + assert.Equal(t, "/home/test/", job.Source) + assert.Equal(t, "${target_base}/test/", job.Target) } func TestLoadConfig2(t *testing.T) { @@ -63,13 +49,10 @@ jobs: enabled: false ` - // Use a reader instead of a mock file reader := bytes.NewReader([]byte(yamlData)) cfg, err := internal.LoadConfig(reader) - if err != nil { - t.Fatalf("Failed to load config: %v", err) - } + require.NoError(t, err) expected := []internal.Job{ { @@ -89,7 +72,7 @@ jobs: } for i, job := range cfg.Jobs { - assertJobEqual(t, job, expected[i]) + assert.Equal(t, expected[i], job, "Job mismatch at index %d", i) } } @@ -110,11 +93,8 @@ target: "/target" var job internal.Job err := yaml.Unmarshal([]byte(yamlData), &job) - if err != nil { - t.Fatalf("Failed to unmarshal YAML: %v", err) - } - - assertJobEqual(t, job, expected) + require.NoError(t, err) + assert.Equal(t, expected, job) } func TestYAMLUnmarshalingDefaults_ExplicitFalseValues(t *testing.T) { @@ -136,11 +116,8 @@ enabled: false var job internal.Job err := yaml.Unmarshal([]byte(yamlData), &job) - if err != nil { - t.Fatalf("Failed to unmarshal YAML: %v", err) - } - - assertJobEqual(t, job, expected) + require.NoError(t, err) + assert.Equal(t, expected, job) } func TestYAMLUnmarshalingDefaults_MixedValues(t *testing.T) { @@ -161,11 +138,8 @@ delete: false var job internal.Job err := yaml.Unmarshal([]byte(yamlData), &job) - if err != nil { - t.Fatalf("Failed to unmarshal YAML: %v", err) - } - - assertJobEqual(t, job, expected) + require.NoError(t, err) + assert.Equal(t, expected, job) } func TestSubstituteVariables(t *testing.T) { @@ -176,87 +150,50 @@ func TestSubstituteVariables(t *testing.T) { expected := "/mnt/backup1/user/music/home" result := internal.SubstituteVariables(input, variables) - if result != expected { - t.Errorf("Expected %s, got %s", expected, result) - } + assert.Equal(t, expected, result, "SubstituteVariables result mismatch") } -func TestValidateJobNames(t *testing.T) { - tests := []struct { - name string - jobs []internal.Job - expectsError bool - errorMessage string - }{ - { - name: "Valid job names", - jobs: []internal.Job{ - {Name: "job1"}, - {Name: "job2"}, - }, - expectsError: false, - }, - { - name: "Duplicate job names", - jobs: []internal.Job{ - {Name: "job1"}, - {Name: "job1"}, - }, - expectsError: true, - errorMessage: "duplicate job name: job1", - }, - { - name: "Invalid characters in job name", - jobs: []internal.Job{ - {Name: "job 1"}, - }, - expectsError: true, - errorMessage: "invalid characters in job name: job 1", - }, - { - name: "Mixed errors", - jobs: []internal.Job{ - {Name: "job1"}, - {Name: "job 1"}, - {Name: "job1"}, - }, - expectsError: true, - errorMessage: "duplicate job name: job1", - }, +func TestValidateJobNames_ValidJobNames(t *testing.T) { + jobs := []internal.Job{ + {Name: "job1"}, + {Name: "job2"}, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := internal.ValidateJobNames(test.jobs) - if test.expectsError { - if err == nil { - t.Errorf("Expected error but got none") - } else if !strings.Contains(err.Error(), test.errorMessage) { - t.Errorf("Expected error message to contain '%s', but got '%s'", test.errorMessage, err.Error()) - } - } else { - expectNoError(t, err) - } - }) - } + err := internal.ValidateJobNames(jobs) + assert.NoError(t, err) } -func expectNoError(t *testing.T, err error) { - t.Helper() - - if err != nil { - t.Errorf("Expected no error but got: %v", err) +func TestValidateJobNames_DuplicateJobNames(t *testing.T) { + jobs := []internal.Job{ + {Name: "job1"}, + {Name: "job1"}, } + + err := internal.ValidateJobNames(jobs) + require.Error(t, err) + assert.Contains(t, err.Error(), "duplicate job name: job1") } -func expectError(t *testing.T, err error, expectedMessage string) { - t.Helper() +func TestValidateJobNames_InvalidCharactersInJobName(t *testing.T) { + jobs := []internal.Job{ + {Name: "job 1"}, + } - if err == nil { - t.Errorf("Expected error but got none") - } else if err.Error() != expectedMessage { - t.Errorf("Expected error message '%s', but got '%s'", expectedMessage, err.Error()) + err := internal.ValidateJobNames(jobs) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid characters in job name: job 1") +} + +func TestValidateJobNames_MixedErrors(t *testing.T) { + jobs := []internal.Job{ + {Name: "job1"}, + {Name: "job 1"}, + {Name: "job1"}, } + + err := internal.ValidateJobNames(jobs) + require.Error(t, err) + assert.Contains(t, err.Error(), "duplicate job name: job1") } func TestValidatePath_ValidSourcePath(t *testing.T) { @@ -271,7 +208,8 @@ func TestValidatePath_ValidSourcePath(t *testing.T) { } err := internal.ValidatePath(test.jobPath, test.paths, test.pathType, "job1") - expectNoError(t, err) + + assert.NoError(t, err) } func TestValidatePath_InvalidSourcePath(t *testing.T) { @@ -286,7 +224,9 @@ func TestValidatePath_InvalidSourcePath(t *testing.T) { } err := internal.ValidatePath(test.jobPath, test.paths, test.pathType, "job1") - expectError(t, err, "invalid path for job 'job1': source /invalid/source") + + require.Error(t, err) + assert.EqualError(t, err, "invalid path for job 'job1': source /invalid/source") } func TestValidatePath_ValidTargetPath(t *testing.T) { @@ -301,7 +241,8 @@ func TestValidatePath_ValidTargetPath(t *testing.T) { } err := internal.ValidatePath(test.jobPath, test.paths, test.pathType, "job1") - expectNoError(t, err) + + assert.NoError(t, err) } func TestValidatePath_InvalidTargetPath(t *testing.T) { @@ -316,83 +257,64 @@ func TestValidatePath_InvalidTargetPath(t *testing.T) { } err := internal.ValidatePath(test.jobPath, test.paths, test.pathType, "job1") - expectError(t, err, "invalid path for job 'job1': target /invalid/target") + + require.Error(t, err) + assert.EqualError(t, err, "invalid path for job 'job1': target /invalid/target") } -func TestValidatePaths(t *testing.T) { - tests := []struct { +func TestValidatePaths_ValidPaths(t *testing.T) { + test := struct { name string cfg internal.Config expectsError bool - errorMessage string }{ - { - name: "Valid paths", - cfg: internal.Config{ - Sources: []internal.Path{ - {Path: "/home/user"}, - }, - Targets: []internal.Path{ - {Path: "/mnt/backup"}, - }, - Jobs: []internal.Job{ - {Name: "job1", Source: "/home/user/documents", Target: "/mnt/backup/documents"}, - }, + name: "Valid paths", + cfg: internal.Config{ + Sources: []internal.Path{ + {Path: "/home/user"}, }, - expectsError: false, - }, - { - name: "Invalid paths", - cfg: internal.Config{ - Sources: []internal.Path{ - {Path: "/home/user"}, - }, - Targets: []internal.Path{ - {Path: "/mnt/backup"}, - }, - Jobs: []internal.Job{ - {Name: "job1", Source: "/invalid/source", Target: "/invalid/target"}, - }, + Targets: []internal.Path{ + {Path: "/mnt/backup"}, + }, + Jobs: []internal.Job{ + {Name: "job1", Source: "/home/user/documents", Target: "/mnt/backup/documents"}, }, - expectsError: true, - errorMessage: "path validation failed: [" + - "invalid path for job 'job1': source /invalid/source " + - "invalid path for job 'job1': target /invalid/target]", }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := internal.ValidatePaths(test.cfg) - if test.expectsError { - expectError(t, err, test.errorMessage) - } else { - expectNoError(t, err) - } - }) - } + t.Run(test.name, func(t *testing.T) { + err := internal.ValidatePaths(test.cfg) + assert.NoError(t, err) + }) } -func assertJobEqual(t *testing.T, got, expected internal.Job) { - t.Helper() - - if got.Name != expected.Name { - t.Errorf("Job name mismatch: got %s, want %s", got.Name, expected.Name) - } - - if got.Source != expected.Source { - t.Errorf("Job source mismatch: got %s, want %s", got.Source, expected.Source) - } - - if got.Target != expected.Target { - t.Errorf("Job target mismatch: got %s, want %s", got.Target, expected.Target) - } - - if got.Delete != expected.Delete { - t.Errorf("Job delete flag mismatch: got %v, want %v", got.Delete, expected.Delete) +func TestValidatePaths_InvalidPaths(t *testing.T) { + test := struct { + name string + cfg internal.Config + expectsError bool + errorMessage string + }{ + name: "Invalid paths", + cfg: internal.Config{ + Sources: []internal.Path{ + {Path: "/home/user"}, + }, + Targets: []internal.Path{ + {Path: "/mnt/backup"}, + }, + Jobs: []internal.Job{ + {Name: "job1", Source: "/invalid/source", Target: "/invalid/target"}, + }, + }, + errorMessage: "path validation failed: [" + + "invalid path for job 'job1': source /invalid/source " + + "invalid path for job 'job1': target /invalid/target]", } - if got.Enabled != expected.Enabled { - t.Errorf("Job enabled flag mismatch: got %v, want %v", got.Enabled, expected.Enabled) - } + t.Run(test.name, func(t *testing.T) { + err := internal.ValidatePaths(test.cfg) + require.Error(t, err) + assert.EqualError(t, err, test.errorMessage) + }) } diff --git a/backup/internal/helper_test.go b/backup/internal/helper_test.go index e5d2559..69ce92a 100644 --- a/backup/internal/helper_test.go +++ b/backup/internal/helper_test.go @@ -1,8 +1,11 @@ package internal_test import ( - "backup-rsync/backup/internal" "testing" + + "backup-rsync/backup/internal" + + "github.com/stretchr/testify/assert" ) func TestNormalizePath(t *testing.T) { @@ -18,8 +21,6 @@ func TestNormalizePath(t *testing.T) { for _, test := range tests { result := internal.NormalizePath(test.input) - if result != test.expected { - t.Errorf("NormalizePath(%q) = %q; want %q", test.input, result, test.expected) - } + assert.Equal(t, test.expected, result) } } diff --git a/backup/internal/job_test.go b/backup/internal/job_test.go index 18490d5..b461e36 100644 --- a/backup/internal/job_test.go +++ b/backup/internal/job_test.go @@ -6,6 +6,8 @@ import ( "testing" "backup-rsync/backup/internal" + + "github.com/stretchr/testify/assert" ) // Static error for testing. @@ -101,7 +103,6 @@ func (m *MockCommandExecutor) Execute(name string, args ...string) ([]byte, erro } func TestBuildRsyncCmd(t *testing.T) { - // This test doesn't need mocking since it only builds args job := *NewJob( WithSource("/home/user/Music/"), WithTarget("/target/user/music/home"), @@ -115,9 +116,7 @@ func TestBuildRsyncCmd(t *testing.T) { "/home/user/Music/", "/target/user/music/home", } - if strings.Join(args, " ") != strings.Join(expectedArgs, " ") { - t.Errorf("Expected %v, got %v", expectedArgs, args) - } + assert.Equal(t, strings.Join(expectedArgs, " "), strings.Join(args, " ")) } func TestExecuteJob(t *testing.T) { @@ -133,7 +132,7 @@ func TestExecuteJob(t *testing.T) { simulate := true status := internal.ExecuteJobWithExecutor(job, simulate, false, "", mockExecutor) - expectStatus(t, status, statusSuccess) + assert.Equal(t, statusSuccess, status) disabledJob := *NewJob( WithName("disabled_job"), @@ -143,7 +142,7 @@ func TestExecuteJob(t *testing.T) { ) status = internal.ExecuteJobWithExecutor(disabledJob, simulate, false, "", mockExecutor) - expectStatus(t, status, "SKIPPED") + assert.Equal(t, "SKIPPED", status) // Test case for failure (simulate by providing invalid source path) invalidJob := *NewJob( @@ -153,7 +152,7 @@ func TestExecuteJob(t *testing.T) { ) status = internal.ExecuteJobWithExecutor(invalidJob, false, false, "", mockExecutor) - expectStatus(t, status, "FAILURE") + assert.Equal(t, "FAILURE", status) } // Ensure all references to ExecuteJob are prefixed with internal. @@ -168,7 +167,7 @@ func TestJobSkippedEnabledTrue(t *testing.T) { ) status := internal.ExecuteJobWithExecutor(job, true, false, "", mockExecutor) - expectStatus(t, status, statusSuccess) + assert.Equal(t, statusSuccess, status) } func TestJobSkippedEnabledFalse(t *testing.T) { @@ -183,7 +182,7 @@ func TestJobSkippedEnabledFalse(t *testing.T) { ) status := internal.ExecuteJobWithExecutor(disabledJob, true, false, "", mockExecutor) - expectStatus(t, status, "SKIPPED") + assert.Equal(t, "SKIPPED", status) } func TestJobSkippedEnabledOmitted(t *testing.T) { @@ -197,7 +196,7 @@ func TestJobSkippedEnabledOmitted(t *testing.T) { ) status := internal.ExecuteJobWithExecutor(job, true, false, "", mockExecutor) - expectStatus(t, status, statusSuccess) + assert.Equal(t, statusSuccess, status) } func TestExecuteJobWithMockedRsync(t *testing.T) { @@ -212,29 +211,11 @@ func TestExecuteJobWithMockedRsync(t *testing.T) { ) status := internal.ExecuteJobWithExecutor(job, true, false, "", mockExecutor) - expectStatus(t, status, statusSuccess) - - // Check that rsync was called with the expected arguments - if len(mockExecutor.CapturedCommands) == 0 { - t.Errorf("Expected at least one command to be executed") - - return - } + assert.Equal(t, statusSuccess, status) + assert.NotEmpty(t, mockExecutor.CapturedCommands) cmd := mockExecutor.CapturedCommands[0] - if cmd.Name != "rsync" { - t.Errorf("Expected command to be 'rsync', got %s", cmd.Name) - } - if len(cmd.Args) == 0 || cmd.Args[0] != "--dry-run" { - t.Errorf("Expected --dry-run flag, got %v", cmd.Args) - } -} - -func expectStatus(t *testing.T, status, expectedStatus string) { - t.Helper() - - if status != expectedStatus { - t.Errorf("Expected status %s, got %s", expectedStatus, status) - } + assert.Equal(t, "rsync", cmd.Name, "Command name mismatch") + assert.Contains(t, cmd.Args, "--dry-run", "Expected --dry-run flag in command arguments") } diff --git a/go.mod b/go.mod index b77173c..4a11214 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,14 @@ go 1.24.9 require ( github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.1 + github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.9 // indirect golang.org/x/text v0.28.0 // indirect ) diff --git a/go.sum b/go.sum index ec5d064..0bfa0c4 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= @@ -8,6 +12,8 @@ github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=