Skip to content

Commit

Permalink
Add unit-tests coverage for dup_pattern checker (#43)
Browse files Browse the repository at this point in the history
* Change library for golden files
  • Loading branch information
mszostok committed Oct 17, 2020
1 parent c915fc0 commit cbda10c
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 13 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1
github.com/sebdah/goldie/v2 v2.3.0
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/spf13/afero v1.2.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/sebdah/goldie/v2 v2.3.0 h1:qOrofCLbWLjF2PDEL/BueSdFC8V8VyRKccKmqf/89ws=
github.com/sebdah/goldie/v2 v2.3.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
Expand Down
6 changes: 3 additions & 3 deletions internal/check/duplicated_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ func (d *DuplicatedPattern) Check(ctx context.Context, in Input) (Output, error)

for name, entries := range patterns {
if len(entries) > 1 {
msg := fmt.Sprintf("Pattern %q is defined %d times in lines: \n%s", name, len(entries), ListFormatFunc(entries))
msg := fmt.Sprintf("Pattern %q is defined %d times in lines: \n%s", name, len(entries), d.listFormatFunc(entries))
output.ReportIssue(msg)
}
}

return output, nil
}

// ListFormatFunc is a basic formatter that outputs a bullet point list of the pattern.
func ListFormatFunc(es []codeowners.Entry) string {
// listFormatFunc is a basic formatter that outputs a bullet point list of the pattern.
func (d *DuplicatedPattern) listFormatFunc(es []codeowners.Entry) string {
points := make([]string, len(es))
for i, err := range es {
points[i] = fmt.Sprintf(" * %d: with owners: %s", err.LineNo, err.Owners)
Expand Down
65 changes: 65 additions & 0 deletions internal/check/duplicated_pattern_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package check_test

import (
"context"
"testing"

"github.com/mszostok/codeowners-validator/internal/check"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDuplicatedPattern(t *testing.T) {
tests := map[string]struct {
codeownersInput string
expectedIssues []check.Issue
}{
"Should report info about duplicated entries": {
codeownersInput: `
* @global-owner1 @global-owner2
/build/logs/ @doctocat
/build/logs/ @doctocat
/script @mszostok
/script m.t@g.com
`,
expectedIssues: []check.Issue{
{
Severity: check.Error,
LineNo: nil,
Message: `Pattern "/build/logs/" is defined 2 times in lines:
* 4: with owners: [@doctocat]
* 5: with owners: [@doctocat]`,
},
{
Severity: check.Error,
LineNo: nil,
Message: `Pattern "/script" is defined 2 times in lines:
* 7: with owners: [@mszostok]
* 8: with owners: [m.t@g.com]`,
},
},
},
"Should not report any issues with correct CODEOWNERS file": {
codeownersInput: validCODEOWNERS,
expectedIssues: nil,
},
}

for tn, tc := range tests {
t.Run(tn, func(t *testing.T) {
// given
sut := check.NewDuplicatedPattern()

// when
out, err := sut.Check(context.TODO(), loadInput(tc.codeownersInput))

// then
require.NoError(t, err)
assert.ElementsMatch(t, tc.expectedIssues, out.Issues)
})
}

}
30 changes: 30 additions & 0 deletions internal/check/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package check_test

import (
"strings"

"github.com/mszostok/codeowners-validator/internal/check"
"github.com/mszostok/codeowners-validator/pkg/codeowners"
)

var validCODEOWNERS = `
# These owners will be the default owners for everything
* @global-owner1 @global-owner2
# js owner
*.js @js-owner
*.go docs@example.com
/build/logs/ @doctocat
/script m.t@g.com
`

func loadInput(in string) check.Input {
r := strings.NewReader(in)

return check.Input{
CodeownerEntries: codeowners.ParseCodeowners(r),
}
}
34 changes: 34 additions & 0 deletions internal/context/check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package context_test

import (
"context"
"testing"

contextutil "github.com/mszostok/codeowners-validator/internal/context"
"github.com/stretchr/testify/assert"
)

func TestShouldExit(t *testing.T) {
t.Run("Should notify about exit if context is canceled", func(t *testing.T) {
// given
ctx, cancel := context.WithCancel(context.Background())

// when
cancel()
shouldExit := contextutil.ShouldExit(ctx)

// then
assert.True(t, shouldExit)
})

t.Run("Should return false if context is not canceled", func(t *testing.T) {
// given
ctx := context.Background()

// when
shouldExit := contextutil.ShouldExit(ctx)

// then
assert.False(t, shouldExit)
})
}
46 changes: 46 additions & 0 deletions internal/envconfig/envconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package envconfig_test

import (
"os"
"testing"

"github.com/mszostok/codeowners-validator/internal/envconfig"

"github.com/stretchr/testify/require"
"gotest.tools/assert"
)

type testConfig struct {
Key1 string
}

func TestInit(t *testing.T) {
t.Run("Should read env variable without prefix", func(t *testing.T) {
// given
var cfg testConfig

require.NoError(t, os.Setenv("KEY1", "test-value"))

// when
err := envconfig.Init(&cfg)

// then
require.NoError(t, err)
assert.Equal(t, "test-value", cfg.Key1)
})

t.Run("Should read env variable with prefix", func(t *testing.T) {
// given
var cfg testConfig

require.NoError(t, os.Setenv("ENVS_PREFIX", "TEST_PREFIX"))
require.NoError(t, os.Setenv("TEST_PREFIX_KEY1", "test-value"))

// when
err := envconfig.Init(&cfg)

// then
require.NoError(t, err)
assert.Equal(t, "test-value", cfg.Key1)
})
}
9 changes: 6 additions & 3 deletions internal/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package github
import (
"context"
"net/http"
"time"

"github.com/mszostok/codeowners-validator/pkg/url"

Expand All @@ -11,9 +12,10 @@ import (
)

type ClientConfig struct {
AccessToken string `envconfig:"optional"`
BaseURL string `envconfig:"optional"`
UploadURL string `envconfig:"optional"`
AccessToken string `envconfig:"optional"`
BaseURL string `envconfig:"optional"`
UploadURL string `envconfig:"optional"`
HTTPRequestTimeout time.Duration `envconfig:"default=30s"`
}

func NewClient(ctx context.Context, cfg ClientConfig) (ghClient *github.Client, err error) {
Expand All @@ -23,6 +25,7 @@ func NewClient(ctx context.Context, cfg ClientConfig) (ghClient *github.Client,
&oauth2.Token{AccessToken: cfg.AccessToken},
))
}
httpClient.Timeout = cfg.HTTPRequestTimeout

baseURL, uploadURL := cfg.BaseURL, cfg.UploadURL

Expand Down
4 changes: 2 additions & 2 deletions pkg/codeowners/owners.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func NewFromPath(path string) ([]Entry, error) {
return nil, err
}

entries := parseCodeowners(r)
entries := ParseCodeowners(r)
return entries, nil
}

Expand Down Expand Up @@ -66,7 +66,7 @@ func openCodeownersFile(dir string) (io.Reader, error) {
return nil, fmt.Errorf("No CODEOWNERS found in the root, docs/, or .github/ directory of the repository %s", dir)
}

func parseCodeowners(r io.Reader) []Entry {
func ParseCodeowners(r io.Reader) []Entry {
var e []Entry
s := bufio.NewScanner(r)
no := uint64(0)
Expand Down
14 changes: 9 additions & 5 deletions tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"testing"
"time"

"github.com/sebdah/goldie/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gotest.tools/golden"
)

const (
Expand All @@ -24,7 +24,7 @@ const (
// to the golden file.
//
// To update golden file, run:
// go test ./tests/integration/... -v -test.update-golden -tags=integration -run=^TestCheckHappyPath$
// go test ./tests/integration/... -v -update -tags=integration -run=^TestCheckHappyPath$
func TestCheckSuccess(t *testing.T) {
type Envs map[string]string
tests := []struct {
Expand Down Expand Up @@ -83,7 +83,9 @@ func TestCheckSuccess(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, result.ExitCode, 0)
normalizedOutput := normalizeTimeDurations(result.Stdout)
golden.Assert(t, normalizedOutput, t.Name()+".golden.txt")

g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
g.Assert(t, t.Name(), []byte(normalizedOutput))
})
}
}
Expand All @@ -95,7 +97,7 @@ func TestCheckSuccess(t *testing.T) {
// to the golden file.
//
// To update golden file, run:
// go test ./tests/integration/... -v -test.update-golden -tags=integration -run=^TestCheckFailures$
// go test ./tests/integration/... -v -update -tags=integration -run=^TestCheckFailures$
func TestCheckFailures(t *testing.T) {
type Envs map[string]string
tests := []struct {
Expand Down Expand Up @@ -157,7 +159,9 @@ func TestCheckFailures(t *testing.T) {
assert.Equal(t, 3, result.ExitCode)

normalizedOutput := normalizeTimeDurations(result.Stdout)
golden.Assert(t, normalizedOutput, t.Name()+".golden.txt")

g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
g.Assert(t, t.Name(), []byte(normalizedOutput))
})
}
}
Expand Down

0 comments on commit cbda10c

Please sign in to comment.