Skip to content

Commit

Permalink
fix: panic if the same JSON is used twice
Browse files Browse the repository at this point in the history
Unless both time it was sent the same pointer then we must
panic as one will not be written successfully.
  • Loading branch information
jameshartig committed Aug 19, 2024
1 parent 6996163 commit 0550455
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 17 deletions.
14 changes: 8 additions & 6 deletions lflag.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Package lflag handles defining configuration providers as well as
// coordinating initialization code.
//
// Configuration
// # Configuration
//
// Parameters are defined much like they are in the standard flag package. A
// pointer is returned from a method like String, which will then be filled once
Expand All @@ -13,7 +13,7 @@
// lflag.Parse(lflag.NewSourceCLI())
// db, err := NewDB(*addr, *poolSize)
//
// Sources
// # Sources
//
// lflag supports multiple Sources, which are places from which configuration
// information is obtained. For example, NewSourceCLI returns a Source which
Expand All @@ -22,21 +22,20 @@
// together using the Sources type, which handles merging the different Sources
// into a single one.
//
// Initialization
// # Initialization
//
// In addition to handling configuration parameters lflag also manages
// initialization based on their values, via the Do function. When lflag.Parse
// is called, after all parameters have been filled in, all functions passed
// into Do are called, in the order in which they were passed in.
//
// Miscellaneous
// # Miscellaneous
//
// lflag handles a couple of other behaviors which are related to initialization
// and runtime.
//
// Build information can be compiled into binaries using lflag using the build
// variables like BuildCommit. See their doc string for how exactly to do that.
//
package lflag

import (
Expand Down Expand Up @@ -349,7 +348,10 @@ func JSON(rcv interface{}, name string, value interface{}, usage string) {
Default: string(jValue),
Usage: usage,
}
newParam(p, rcv)
ptr := newParam(p, rcv)
if ptr != rcv {
panic(fmt.Sprintf("param named %q already exists and differs from this new one", p.Name))
}
}

// RequiredJSON is like JSON, but it has no default and must be set
Expand Down
40 changes: 29 additions & 11 deletions lflag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ package lflag

import (
"sync"
. "testing"
"testing"
"time"

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

// these are used in the cli and env tests
var testParams = []Param{
Param{ParamType: ParamTypeString, Name: "foo"},
Param{ParamType: ParamTypeString, Name: "bar", Usage: "wut"},
Param{ParamType: ParamTypeString, Name: "baz", Usage: "wut", Default: "wat"},
Param{ParamType: ParamTypeBool, Name: "flag1"},
Param{ParamType: ParamTypeBool, Name: "flag2"},
Param{ParamType: ParamTypeString, Name: "foo-bar"},
{ParamType: ParamTypeString, Name: "foo"},
{ParamType: ParamTypeString, Name: "bar", Usage: "wut"},
{ParamType: ParamTypeString, Name: "baz", Usage: "wut", Default: "wat"},
{ParamType: ParamTypeBool, Name: "flag1"},
{ParamType: ParamTypeBool, Name: "flag2"},
{ParamType: ParamTypeString, Name: "foo-bar"},
}

type jstruct struct {
Foo string `json:"foo"`
Bar int `json:"bar"`
}

func TestParse(t *T) {
func TestParse(t *testing.T) {
s := String("str", "default", "Some string")
i := Int("int", 5, "Some int")
b := Bool("bool", false, "Some bool")
Expand All @@ -49,7 +49,7 @@ func TestParse(t *T) {
assert.Equal(t, jstruct{Foo: "FOO", Bar: 10}, j)
}

func TestParseDefault(t *T) {
func TestParseDefault(t *testing.T) {
s := String("str", "default", "Some string")
i := Int("int", 5, "Some int")
b := Bool("bool", true, "Some bool")
Expand All @@ -66,7 +66,7 @@ func TestParseDefault(t *T) {
assert.Equal(t, jstruct{Foo: "foo", Bar: 5}, j)
}

func TestParseRequired(t *T) {
func TestParseRequired(t *testing.T) {
s := RequiredString("str", "Some string")
i := RequiredInt("int", "Some int")
b := RequiredBool("bool", "Some bool")
Expand All @@ -89,7 +89,25 @@ func TestParseRequired(t *T) {
assert.Equal(t, jstruct{Foo: "FOO", Bar: 10}, j)
}

func TestDo(t *T) {
func TestDuplicateJSON(t *testing.T) {
var j jstruct
JSON(&j, "json", jstruct{Foo: "foo", Bar: 5}, "Some json")
JSON(&j, "json", jstruct{Foo: "foo", Bar: 5}, "Some json")

Parse(SourceStub{
"json": `{"bar":10}`,
})
assert.Equal(t, jstruct{Foo: "", Bar: 10}, j)

assert.Panics(t, func() {
var j jstruct
JSON(&j, "json", jstruct{}, "Some json")
var k jstruct
JSON(&k, "json", jstruct{}, "Some json")
})
}

func TestDo(t *testing.T) {
Reset()

// we shouldn't need a lock but we do because the race detector is sensitive
Expand Down

0 comments on commit 0550455

Please sign in to comment.