Compare commits
3 commits
6f06adc37d
...
fe860fbc82
| Author | SHA1 | Date | |
|---|---|---|---|
| fe860fbc82 | |||
| 917f919401 | |||
| d7de194a90 |
10 changed files with 219 additions and 22 deletions
10
Taskfile.yml
10
Taskfile.yml
|
|
@ -43,12 +43,20 @@ tasks:
|
||||||
cmds:
|
cmds:
|
||||||
- staticcheck ./...
|
- staticcheck ./...
|
||||||
|
|
||||||
analyze:
|
vuln:
|
||||||
|
desc: Check for vulnerabilities
|
||||||
|
sources:
|
||||||
|
- '**/*.go'
|
||||||
|
cmds:
|
||||||
|
- govulncheck ./...
|
||||||
|
|
||||||
|
lint:
|
||||||
desc: Do static analysis
|
desc: Do static analysis
|
||||||
deps:
|
deps:
|
||||||
- vet
|
- vet
|
||||||
- critic
|
- critic
|
||||||
- staticcheck
|
- staticcheck
|
||||||
|
- vuln
|
||||||
|
|
||||||
test:
|
test:
|
||||||
desc: Run unit tests
|
desc: Run unit tests
|
||||||
|
|
|
||||||
1
app.go
1
app.go
|
|
@ -33,6 +33,7 @@ func NewApp(ver string, conf config.Config, db store.Store) (*App, error) {
|
||||||
app := App{
|
app := App{
|
||||||
version: ver,
|
version: ver,
|
||||||
conf: conf,
|
conf: conf,
|
||||||
|
storage: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
selfIRI := boxap.DefaultServiceIRI(conf.BaseURL())
|
selfIRI := boxap.DefaultServiceIRI(conf.BaseURL())
|
||||||
|
|
|
||||||
25
app_test.go
25
app_test.go
|
|
@ -78,6 +78,31 @@ func TestService(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppStorage(t *testing.T) {
|
||||||
|
th := setupAppTest(t)
|
||||||
|
base := "http://localhost:1234/"
|
||||||
|
th.conf.EXPECT().BaseURL().Return(base).MinTimes(1)
|
||||||
|
th.conf.EXPECT().Name().Return("").AnyTimes()
|
||||||
|
a, er := NewApp("0.0.0.0", th.conf, th.store)
|
||||||
|
assert.NoError(t, er)
|
||||||
|
if assert.NotNil(t, a) {
|
||||||
|
assert.Equal(t, th.store, a.Storage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppUser(t *testing.T) {
|
||||||
|
// TODO: Actually fill this out at some point
|
||||||
|
th := setupAppTest(t)
|
||||||
|
base := "http://localhost:1234/"
|
||||||
|
th.conf.EXPECT().BaseURL().Return(base).MinTimes(1)
|
||||||
|
th.conf.EXPECT().Name().Return("").AnyTimes()
|
||||||
|
a, er := NewApp("0.0.0.0", th.conf, th.store)
|
||||||
|
assert.NoError(t, er)
|
||||||
|
if assert.NotNil(t, a) {
|
||||||
|
assert.Zero(t, a.User())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStrings(t *testing.T) {
|
func TestStrings(t *testing.T) {
|
||||||
cases := [...]struct {
|
cases := [...]struct {
|
||||||
given string
|
given string
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ func getTomlFile() string {
|
||||||
|
|
||||||
var confStr = `
|
var confStr = `
|
||||||
base_url = "http://localhost:4523/"
|
base_url = "http://localhost:4523/"
|
||||||
|
env = "dev"
|
||||||
|
|
||||||
[stores]
|
[stores]
|
||||||
store = "sqlite"
|
store = "sqlite"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -12,7 +13,7 @@ func TestEnvDefaultsToDev(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidEnvReturnsDev(t *testing.T) {
|
func TestInvalidEnvReturnsDev(t *testing.T) {
|
||||||
c := config{env: Env("foobar")}
|
c := config{env: Env(255)}
|
||||||
assert.Equal(t, Dev, c.Env())
|
assert.Equal(t, Dev, c.Env())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,3 +25,40 @@ func TestValidEnvReturnsCorrect(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigBaseURL(t *testing.T) {
|
||||||
|
c := config{baseURL: "https://me.goodevilgenius.org"}
|
||||||
|
assert.Equal(t, c.baseURL, c.BaseURL())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigStoreName(t *testing.T) {
|
||||||
|
c := config{stores: stores{store: "cockroachdb"}}
|
||||||
|
assert.Equal(t, c.stores.store, c.StoreName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoresMissingStore(t *testing.T) {
|
||||||
|
ss := stores{}
|
||||||
|
st, er := ss.GetStore("cockroachdb")
|
||||||
|
assert.Nil(t, st)
|
||||||
|
assert.ErrorIs(t, er, ErrMissingStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mockToml = `
|
||||||
|
[cockroachdb]
|
||||||
|
|
||||||
|
dsn = "cockroachdb://user:pass@127.0.0.1:26257/combluotion"
|
||||||
|
`
|
||||||
|
|
||||||
|
type mockConn struct {
|
||||||
|
CockroachDB toml.Primitive
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreMap(t *testing.T) {
|
||||||
|
var conn mockConn
|
||||||
|
md, _ := toml.Decode(mockToml, &conn)
|
||||||
|
st := store{"cockroachdb", conn.CockroachDB, md}
|
||||||
|
|
||||||
|
m, er := st.Map()
|
||||||
|
assert.NoError(t, er)
|
||||||
|
assert.Equal(t, "cockroachdb://user:pass@127.0.0.1:26257/combluotion", m["dsn"])
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
type Env string
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type Env
|
||||||
|
|
||||||
|
type Env uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Dev Env = "dev"
|
Dev Env = iota + 1
|
||||||
Prod Env = "prod"
|
Prod
|
||||||
Qa Env = "qa"
|
Qa
|
||||||
Test Env = "test"
|
Test
|
||||||
)
|
)
|
||||||
|
|
||||||
var Envs = [...]Env{
|
var Envs = [...]Env{
|
||||||
|
|
@ -16,6 +24,28 @@ var Envs = [...]Env{
|
||||||
Test,
|
Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e Env) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(e.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrEnvUnmarshal = errors.New("unable to unmarshal value")
|
||||||
|
|
||||||
|
func (e *Env) UnmarshalText(text []byte) error {
|
||||||
|
if e == nil {
|
||||||
|
return fmt.Errorf("%w: nil pointer", ErrEnvUnmarshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
val := strings.ToUpper(string(text))
|
||||||
|
|
||||||
|
for _, ee := range Envs {
|
||||||
|
if val == strings.ToUpper(ee.String()) {
|
||||||
|
*e = ee
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%w: invalid value: %s", ErrEnvUnmarshal, text)
|
||||||
|
}
|
||||||
|
|
||||||
func ValidEnvOrDev(e Env) Env {
|
func ValidEnvOrDev(e Env) Env {
|
||||||
if ValidEnv(e) {
|
if ValidEnv(e) {
|
||||||
return e
|
return e
|
||||||
|
|
@ -31,7 +61,3 @@ func ValidEnv(env Env) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Env) String() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
27
config/env_string.go
Normal file
27
config/env_string.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Code generated by "stringer -type Env"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Dev-1]
|
||||||
|
_ = x[Prod-2]
|
||||||
|
_ = x[Qa-3]
|
||||||
|
_ = x[Test-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Env_name = "DevProdQaTest"
|
||||||
|
|
||||||
|
var _Env_index = [...]uint8{0, 3, 7, 9, 13}
|
||||||
|
|
||||||
|
func (i Env) String() string {
|
||||||
|
i -= 1
|
||||||
|
if i >= Env(len(_Env_index)-1) {
|
||||||
|
return "Env(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Env_name[_Env_index[i]:_Env_index[i+1]]
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,81 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ encoding.TextMarshaler = Env(0)
|
||||||
|
var _ encoding.TextUnmarshaler = new(Env)
|
||||||
|
var _ fmt.Stringer = Env(0)
|
||||||
|
|
||||||
func TestEnvString(t *testing.T) {
|
func TestEnvString(t *testing.T) {
|
||||||
cases := Envs[:]
|
testcases := []struct {
|
||||||
cases = append(cases, Env("foobar"), Env(""), Env("42"), Env("✨"))
|
expected string
|
||||||
for _, e := range cases {
|
val Env
|
||||||
t.Run(string(e), func(t *testing.T) {
|
}{
|
||||||
assert.Equal(t, string(e), e.String())
|
{"Dev", Dev},
|
||||||
|
{"Prod", Prod},
|
||||||
|
{"Qa", Qa},
|
||||||
|
{"Test", Test},
|
||||||
|
{"Env(0)", Env(0)},
|
||||||
|
{"Env(255)", Env(255)},
|
||||||
|
}
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.expected, func(sub *testing.T) {
|
||||||
|
assert.Equal(t, testcase.expected, testcase.val.String())
|
||||||
|
by, er := testcase.val.MarshalText()
|
||||||
|
assert.NoError(t, er)
|
||||||
|
assert.Equal(t, []byte(testcase.expected), by)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unmarshaltest struct {
|
||||||
|
name string
|
||||||
|
in []byte
|
||||||
|
exp Env
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvUnmarshalText(t *testing.T) {
|
||||||
|
testcases := []unmarshaltest{
|
||||||
|
{"empty", []byte{}, Env(0), "invalid value: "},
|
||||||
|
{"bad value", []byte("foobar"), Env(0), "invalid value: foobar"},
|
||||||
|
}
|
||||||
|
for _, e := range Envs {
|
||||||
|
testcases = append(testcases, unmarshaltest{e.String() + "-lower", []byte(strings.ToLower(e.String())), e, ""})
|
||||||
|
testcases = append(testcases, unmarshaltest{e.String() + "-upper", []byte(strings.ToUpper(e.String())), e, ""})
|
||||||
|
testcases = append(testcases, unmarshaltest{e.String(), []byte(e.String()), e, ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.name, func(sub *testing.T) {
|
||||||
|
var e Env
|
||||||
|
er := (&e).UnmarshalText(testcase.in)
|
||||||
|
if testcase.err == "" {
|
||||||
|
assert.NoError(t, er)
|
||||||
|
assert.Equal(t, testcase.exp, e)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, e)
|
||||||
|
assert.ErrorContains(t, er, testcase.err)
|
||||||
|
assert.ErrorIs(t, er, ErrEnvUnmarshal)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvUnmarshalTextNil(t *testing.T) {
|
||||||
|
var e *Env
|
||||||
|
er := e.UnmarshalText([]byte("prod"))
|
||||||
|
assert.ErrorContains(t, er, "nil pointer")
|
||||||
|
assert.ErrorIs(t, er, ErrEnvUnmarshal)
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidEnv(t *testing.T) {
|
func TestValidEnv(t *testing.T) {
|
||||||
cases := [...]struct {
|
cases := [...]struct {
|
||||||
e Env
|
e Env
|
||||||
|
|
@ -25,9 +85,8 @@ func TestValidEnv(t *testing.T) {
|
||||||
{Prod, true},
|
{Prod, true},
|
||||||
{Qa, true},
|
{Qa, true},
|
||||||
{Test, true},
|
{Test, true},
|
||||||
{Env("foobar"), false},
|
{Env(0), false},
|
||||||
{Env(""), false},
|
{Env(255), false},
|
||||||
{Env("✨"), false},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|
@ -46,9 +105,8 @@ func TestValidEnvOrDev(t *testing.T) {
|
||||||
{Prod, Prod},
|
{Prod, Prod},
|
||||||
{Qa, Qa},
|
{Qa, Qa},
|
||||||
{Test, Test},
|
{Test, Test},
|
||||||
{Env("foobar"), Dev},
|
{Env(0), Dev},
|
||||||
{Env(""), Dev},
|
{Env(255), Dev},
|
||||||
{Env("✨"), Dev},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ func TestLoadTomlGood(t *testing.T) {
|
||||||
defer os.Remove(tmp.Name())
|
defer os.Remove(tmp.Name())
|
||||||
defer tmp.Close()
|
defer tmp.Close()
|
||||||
fmt.Fprintln(tmp, `name = "Cool"`)
|
fmt.Fprintln(tmp, `name = "Cool"`)
|
||||||
|
fmt.Fprintln(tmp, `env = "prod"`)
|
||||||
fmt.Fprintln(tmp, "[stores]")
|
fmt.Fprintln(tmp, "[stores]")
|
||||||
fmt.Fprintln(tmp, `store = "sqlite"`)
|
fmt.Fprintln(tmp, `store = "sqlite"`)
|
||||||
fmt.Fprintln(tmp, "[stores.settings.sqlite]")
|
fmt.Fprintln(tmp, "[stores.settings.sqlite]")
|
||||||
|
|
@ -31,6 +32,7 @@ func TestLoadTomlGood(t *testing.T) {
|
||||||
c, e := LoadFromToml(tmp.Name())
|
c, e := LoadFromToml(tmp.Name())
|
||||||
assert.NoError(t, e)
|
assert.NoError(t, e)
|
||||||
assert.Equal(t, "Cool", c.Name())
|
assert.Equal(t, "Cool", c.Name())
|
||||||
|
assert.Equal(t, Prod, c.Env())
|
||||||
|
|
||||||
st, err := c.Store("")
|
st, err := c.Store("")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -92,3 +93,13 @@ func TestMakeStoreNoName(t *testing.T) {
|
||||||
assert.NotNil(t, s)
|
assert.NotNil(t, s)
|
||||||
assert.NoError(t, e)
|
assert.NoError(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMakeStoreConfError(t *testing.T) {
|
||||||
|
th := setupFactoryTest(t)
|
||||||
|
mockError := errors.New("leave me alone")
|
||||||
|
th.conf.EXPECT().Store("mock").Return(nil, mockError)
|
||||||
|
s, e := MakeStore("mock", th.conf)
|
||||||
|
assert.Zero(t, s)
|
||||||
|
assert.ErrorIs(t, e, mockError)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue