Skip to content

Commit

Permalink
Add schema validation (#41)
Browse files Browse the repository at this point in the history
Added schema valdiaiton using go-playground/validator.
  • Loading branch information
retr0h committed Nov 28, 2023
1 parent b95ec77 commit 4d16497
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 11 deletions.
8 changes: 8 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ func initConfig() {
os.Exit(1)
}

err := config.Validate(&appConfig)
if err != nil {
logger.Error("validation failed",
slog.String("err", err.Error()),
)
os.Exit(1)
}

repos = repositories.New(
appConfig,
logger,
Expand Down
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.21.0

require (
github.com/danjacques/gofslock v0.0.0-20230728142113-ae8f59f9e88b
github.com/go-playground/validator/v10 v10.16.0
github.com/golang/mock v1.6.0
github.com/lmittmann/tint v1.0.3
github.com/spf13/afero v1.10.0
Expand All @@ -16,8 +17,12 @@ require (
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
Expand All @@ -30,7 +35,9 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
Expand Down
17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,23 @@ github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
Expand Down Expand Up @@ -179,6 +189,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lmittmann/tint v1.0.3 h1:W5PHeA2D8bBJVvabNfQD/XW9HPLZK1XoPZH0cq8NouQ=
github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
Expand Down Expand Up @@ -253,6 +265,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
Expand Down Expand Up @@ -293,6 +306,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -365,6 +380,8 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down
49 changes: 49 additions & 0 deletions pkg/config/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2023 John Dewey

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

package config

import (
"regexp"

"github.com/go-playground/validator/v10"
)

// registerValidators register customer validators.
func registerValidators(v *validator.Validate) error {
return v.RegisterValidation("git_commit_id", func(fl validator.FieldLevel) bool {
re := regexp.MustCompile("^[0-9a-f]{5,40}$")
return re.MatchString(fl.Field().String())
})
}

// Validate validates a structs exposed fields.
func Validate(
c *Repositories,
) error {
validate := validator.New()

err := registerValidators(validate)
if err != nil {
return err
}

return validate.Struct(c)
}
215 changes: 215 additions & 0 deletions pkg/config/schema_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Copyright (c) 2023 John Dewey

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

package config

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/go-playground/validator/v10"
"github.com/stretchr/testify/suite"
)

type SchemaTestSuite struct {
suite.Suite

v *validator.Validate
}

func (suite *SchemaTestSuite) SetupTest() {
suite.v = validator.New()
err := registerValidators(suite.v)
assert.NoError(suite.T(), err)
}

func (suite *SchemaTestSuite) TestRepositories() {
tests := []struct {
param *Repositories
expected string
}{
{&Repositories{
GiltFile: "giltFile",
GiltDir: "giltDir",
Repositories: []Repository{
{
Git: "gitURL",
Version: "abc1234",
DstDir: "dstDir",
},
},
}, ""},
{&Repositories{
GiltFile: "giltFile",
GiltDir: "",
Repositories: []Repository{
{
Git: "gitURL",
Version: "abc1234",
DstDir: "dstDir",
},
},
}, "Key: 'Repositories.GiltDir' Error:Field validation for 'GiltDir' failed on the 'required' tag"},
{&Repositories{
GiltFile: "",
GiltDir: "giltDir",
Repositories: []Repository{
{
Git: "gitURL",
Version: "abc1234",
DstDir: "dstDir",
},
},
}, "Key: 'Repositories.GiltFile' Error:Field validation for 'GiltFile' failed on the 'required' tag"},
{&Repositories{
GiltFile: "giltFile",
GiltDir: "giltDir",
}, "Key: 'Repositories.Repositories' Error:Field validation for 'Repositories' failed on the 'required' tag"},
}

for _, test := range tests {
err := suite.v.Struct(test.param)
if test.expected != "" {
assert.EqualError(suite.T(), err, test.expected)
} else {
assert.NoError(suite.T(), err)
}
}
}

func (suite *SchemaTestSuite) TestSourceSchema() {
tests := []struct {
param *Source
expected string
}{
{&Source{
Src: "src",
DstFile: "dstFile",
}, ""},
{&Source{
Src: "src",
DstDir: "dstDir",
}, ""},
{&Source{
Src: "",
}, "Key: 'Source.Src' Error:Field validation for 'Src' failed on the 'required' tag\nKey: 'Source.DstFile' Error:Field validation for 'DstFile' failed on the 'required_without' tag\nKey: 'Source.DstDir' Error:Field validation for 'DstDir' failed on the 'required_without' tag"},
{&Source{
Src: "src",
DstFile: "dstFile",
DstDir: "dstDir",
}, "Key: 'Source.DstFile' Error:Field validation for 'DstFile' failed on the 'excluded_with' tag\nKey: 'Source.DstDir' Error:Field validation for 'DstDir' failed on the 'excluded_with' tag"},
}

for _, test := range tests {
err := suite.v.Struct(test.param)
if test.expected != "" {
assert.EqualError(suite.T(), err, test.expected)
} else {
assert.NoError(suite.T(), err)
}
}
}

func (suite *SchemaTestSuite) TestCommandSchema() {
tests := []struct {
param *Command
expected string
}{
{&Command{
Cmd: "foo",
}, ""},
{&Command{
Cmd: "foo",
Args: []string{"bar", "baz"},
}, ""},
{&Command{
Cmd: "",
}, "Key: 'Command.Cmd' Error:Field validation for 'Cmd' failed on the 'required' tag"},
{&Command{
Args: []string{"bar", "baz"},
}, "Key: 'Command.Cmd' Error:Field validation for 'Cmd' failed on the 'required' tag"},
}

for _, test := range tests {
err := suite.v.Struct(test.param)

if test.expected != "" {
assert.EqualError(suite.T(), err, test.expected)
} else {
assert.NoError(suite.T(), err)
}
}
}

func (suite *SchemaTestSuite) TestRepositorySchema() {
tests := []struct {
param *Repository
expected string
}{
{&Repository{
Git: "gitURL",
Version: "abc1234",
DstDir: "dstDir",
}, ""},
{&Repository{
Git: "gitURL",
Version: "abc1234",
Sources: []Source{
{
Src: "src",
DstFile: "dstFile",
},
},
}, ""},
{&Repository{
Git: "",
Version: "foo",
DstDir: "",
}, "Key: 'Repository.Git' Error:Field validation for 'Git' failed on the 'required' tag\nKey: 'Repository.Version' Error:Field validation for 'Version' failed on the 'git_commit_id' tag\nKey: 'Repository.DstDir' Error:Field validation for 'DstDir' failed on the 'required_without' tag"},
{&Repository{
Git: "gitURL",
Version: "abc1234",
DstDir: "dstDir",
Sources: []Source{
{
Src: "src",
DstFile: "dstFile",
},
},
}, "Key: 'Repository.DstDir' Error:Field validation for 'DstDir' failed on the 'excluded_with' tag\nKey: 'Repository.Sources[0]' Error:Field validation for 'Sources[0]' failed on the 'excluded_with' tag"},
}

for _, test := range tests {
err := suite.v.Struct(test.param)

if test.expected != "" {
assert.EqualError(suite.T(), err, test.expected)
} else {
assert.NoError(suite.T(), err)
}
}
}

// In order for `go test` to run this suite, we need to create
// a normal test function and pass our suite to suite.Run.
func TestSchemaTestSuite(t *testing.T) {
suite.Run(t, new(SchemaTestSuite))
}
Loading

0 comments on commit 4d16497

Please sign in to comment.