Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prepare 2.0.0-beta.2 release #8

Merged
merged 124 commits into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
75f29a1
implement Value type for arbitrary JSON values
eli-darkly Dec 12, 2019
14cea8b
force CI rebuild
eli-darkly Dec 12, 2019
529f2a1
add go get
eli-darkly Dec 12, 2019
25fdf9e
fix go get
eli-darkly Dec 13, 2019
7478aa3
fix lint in CI
eli-darkly Dec 13, 2019
845020e
add Equal()
eli-darkly Dec 13, 2019
30fcb62
add EvaluationDetail and EvaluationReason
eli-darkly Dec 13, 2019
82923f6
fix import path
eli-darkly Dec 13, 2019
141acc1
readme fixes
eli-darkly Dec 13, 2019
0f673b0
update for v2
eli-darkly Dec 13, 2019
0342b4b
Merge branch 'v2' into eb/ch49035/detail-reason
eli-darkly Dec 13, 2019
d327ff8
fix doc comments
eli-darkly Dec 13, 2019
681a43d
add OptionalString
eli-darkly Dec 17, 2019
3220b69
misc cleanup, method renaming, better test coverage
eli-darkly Jan 23, 2020
44328ab
package comments
eli-darkly Jan 23, 2020
4cfc041
changed repo name
eli-darkly Jan 23, 2020
62437a9
numbers can't have leading decimal point
eli-darkly Jan 23, 2020
4dd29c8
Merge pull request #1 from launchdarkly/eb/ch49035/value-type
eli-darkly Jan 24, 2020
fd5ee91
actually we decided on go-sdk-common, not go-sdk-core
eli-darkly Jan 24, 2020
4a686f5
add license file
eli-darkly Jan 24, 2020
293c8b2
Merge branch 'public-v1' into v1
eli-darkly Jan 24, 2020
16fc315
OptionalString improvements + misc fixes
eli-darkly Jan 24, 2020
412e802
add OrElse
eli-darkly Jan 24, 2020
5e07050
add tests
eli-darkly Jan 24, 2020
7f9a2eb
comment
eli-darkly Jan 24, 2020
871db18
Merge branch 'v2' of github.com:launchdarkly/go-sdk-common-private in…
eli-darkly Jan 24, 2020
ec60b93
remove unsafe interface{} usage
eli-darkly Jan 24, 2020
bfb9c44
copy User and related types from go-server-sdk
eli-darkly Jan 24, 2020
7a88acd
OptionalString improvements + misc fixes (#2)
eli-darkly Jan 24, 2020
a626c5e
(common v2/SDK v5) don't use pointers or interface{}s in User
eli-darkly Jan 28, 2020
b57319b
lint
eli-darkly Jan 28, 2020
e5a23b3
lint
eli-darkly Jan 28, 2020
16c7079
Merge branch 'eb/ch58941/copy-user' into eb/ch58941/common-v2-user
eli-darkly Jan 28, 2020
312476d
rm references to obsolete function
eli-darkly Jan 28, 2020
87d4910
Merge branch 'eb/ch58941/copy-user' into eb/ch58941/common-v2-user
eli-darkly Jan 28, 2020
576fd88
(v2 - #1) copy User and related types from go-server-sdk (#3)
eli-darkly Jan 28, 2020
558a682
Merge branch 'v2' into eb/ch58941/common-v2-user
eli-darkly Jan 28, 2020
e63561b
Merge pull request #4 from launchdarkly/eb/ch58941/common-v2-user
eli-darkly Jan 28, 2020
88414be
hide all User fields, improve internal structure, add getters
eli-darkly Jan 28, 2020
b0924fd
lint
eli-darkly Jan 28, 2020
67e75cc
Merge pull request #5 from launchdarkly/eb/ch58941/hide-user-fields
eli-darkly Jan 28, 2020
a9f1f8a
Merge branch 'v1' into v2
eli-darkly Jan 29, 2020
7ccd883
Merge branch 'eb/ch63370/remove-unsafe' into eb/ch58941/user-accessors
eli-darkly Jan 29, 2020
7a480b8
add convenience methods for enumerable types
eli-darkly Jan 29, 2020
4058727
User model improvements related to custom/private attributes
eli-darkly Jan 29, 2020
6ad0ad0
lint
eli-darkly Jan 29, 2020
5db5e14
Merge branch 'v2' into eb/ch63370/remove-unsafe
eli-darkly Jan 29, 2020
8459cbe
Merge branch 'eb/ch63370/remove-unsafe' into eb/ch58941/value-enumera…
eli-darkly Jan 29, 2020
4df7f25
Merge branch 'eb/ch58941/value-enumeration' into eb/ch58941/user-acce…
eli-darkly Jan 29, 2020
2284162
Merge pull request #6 from launchdarkly/eb/ch63370/remove-unsafe
eli-darkly Jan 30, 2020
ec4cad1
Merge pull request #7 from launchdarkly/eb/ch58941/value-enumeration
eli-darkly Jan 30, 2020
3e63beb
Merge pull request #8 from launchdarkly/eb/ch58941/user-accessors
eli-darkly Jan 30, 2020
e588112
Merge branch 'public-v1' into v1
eli-darkly Feb 1, 2020
b1956fd
update release config
eli-darkly Feb 1, 2020
3f638e7
Merge branch 'public-v1' into v1
eli-darkly Feb 1, 2020
50130e8
Merge branch 'v1' into v2
eli-darkly Feb 1, 2020
7688db6
update release config
eli-darkly Feb 1, 2020
aece760
Merge branch 'public-v1' into v1
eli-darkly Feb 1, 2020
4ef5401
Merge branch 'v2' into eb/ch63866/detail-reason
eli-darkly Feb 1, 2020
256b653
fix package references
eli-darkly Feb 1, 2020
0b761e2
Merge pull request #9 from launchdarkly/eb/ch63866/detail-reason
eli-darkly Feb 3, 2020
547cd23
Merge branch 'v1' of github.com:launchdarkly/go-sdk-common into v1
eli-darkly Feb 4, 2020
065bb63
Merge branch 'v1' into v2
eli-darkly Feb 4, 2020
357513b
add ldlog package to go-sdk-common
eli-darkly Feb 4, 2020
1f08c69
lint
eli-darkly Feb 4, 2020
f7d094e
drop Go 1.8-1.11 and update linter
eli-darkly Feb 4, 2020
fb76722
gitignore
eli-darkly Feb 4, 2020
085fb8b
Merge pull request #11 from launchdarkly/eb/ch62105/drop-old-go
eli-darkly Feb 4, 2020
ebb06d6
Merge branch 'v2' into eb/ch64207/ldlog-common
eli-darkly Feb 4, 2020
0490d52
lint
eli-darkly Feb 4, 2020
08c2cf5
Merge pull request #10 from launchdarkly/eb/ch64207/ldlog-common
eli-darkly Feb 4, 2020
0e5056e
add ldtime package for time helpers
eli-darkly Feb 23, 2020
9ce753b
Merge pull request #12 from launchdarkly/eb/ch64206/time-type
eli-darkly Feb 24, 2020
ecc98c6
add Go 1.14 in CI
eli-darkly Mar 2, 2020
d551adb
Merge pull request #13 from launchdarkly/eb/ch67936/go-1.14
eli-darkly Mar 2, 2020
e9701ec
Merge branch 'v1' into v2
eli-darkly Mar 2, 2020
24c7e66
desupport Go 1.12
eli-darkly Mar 2, 2020
bdf9ef7
makefile improvement
eli-darkly Mar 2, 2020
82ce336
Merge pull request #14 from launchdarkly/eb/ch67936/no-go-1.12
eli-darkly Mar 2, 2020
e085a3a
Merge branch 'public-v2' into v2
eli-darkly Apr 1, 2020
7b18d72
make repo a module
eli-darkly Jun 4, 2020
343917d
don't install dep
eli-darkly Jun 4, 2020
6798fbe
yaml fix
eli-darkly Jun 4, 2020
0e0d49f
update golangci-lint + misc code cleanup
eli-darkly Jun 4, 2020
a9985bd
Merge branch 'eb/ch78747/linter-update' into eb/ch78731/modules
eli-darkly Jun 4, 2020
79fddc6
add methods for detecting current log level
eli-darkly Jun 4, 2020
a4bcae8
enforce consistent import groups
eli-darkly Jun 4, 2020
37676bf
enable stylecheck, fix comment
eli-darkly Jun 4, 2020
c1a52d2
add prerelease readme note
eli-darkly Jun 4, 2020
8e0ba06
Merge pull request #17 from launchdarkly/eb/ch77304/detect-debug-level
eli-darkly Jun 4, 2020
9bca369
Merge pull request #15 from launchdarkly/eb/ch78747/linter-update
eli-darkly Jun 4, 2020
09af655
Merge pull request #16 from launchdarkly/eb/ch78731/modules
eli-darkly Jun 4, 2020
3252370
Merge branch 'v2' of github.com:launchdarkly/go-sdk-common into v2
eli-darkly Jun 4, 2020
4547f29
improve test coverage and enforce coverage goals (#18)
eli-darkly Jun 11, 2020
306b165
update readme note about import path, add godoc badge
eli-darkly Jun 12, 2020
78993dd
better badge
eli-darkly Jun 12, 2020
d882b28
omit unset properties in user JSON instead of serializing null value
eli-darkly Jun 16, 2020
4c03ea3
don't allocate extra objects when setting user builder properties
eli-darkly Jun 16, 2020
5dbe2b1
Merge pull request #19 from launchdarkly/eb/ch79884/compact-user-json
eli-darkly Jun 16, 2020
c3da53a
Merge pull request #20 from launchdarkly/eb/ch77304/single-user-build…
eli-darkly Jun 16, 2020
2608fda
add generic user attr setter and more Value helper methods
eli-darkly Jun 16, 2020
d61956f
add benchmarks and enforce zero-allocation goals
eli-darkly Jun 16, 2020
5065348
Merge pull request #21 from launchdarkly/eb/ch79888/set-any-user-attr
eli-darkly Jun 16, 2020
fbe1e72
Merge pull request #22 from launchdarkly/eb/ch77270/benchmarks
eli-darkly Jun 16, 2020
467c837
implement streaming JSON serialization (#23)
eli-darkly Jun 17, 2020
ea7567b
fix bug in JSONBuffer state when writing zeroes
eli-darkly Jun 17, 2020
201d583
Merge pull request #24 from launchdarkly/eb/ch79944/jsonstream-zero-bug
eli-darkly Jun 17, 2020
503da9b
fix note about import path
eli-darkly Jun 18, 2020
2422e4e
go mod tidy
eli-darkly Jun 18, 2020
50ddf12
fix JSONBuffer string escaping, + slight refactor
eli-darkly Jun 18, 2020
0e2db4d
remove accidental inclusions from another branch
eli-darkly Jun 18, 2020
b33a319
lint
eli-darkly Jun 18, 2020
cc049b5
remove inapplicable comments
eli-darkly Jun 18, 2020
454b10c
add ability to send buffered JSON output to another writer
eli-darkly Jun 18, 2020
7aec220
Merge pull request #25 from launchdarkly/eb/ch79944/json-string-escaping
eli-darkly Jun 19, 2020
660a5ba
Merge pull request #26 from launchdarkly/eb/ch79944/stream-to-writer
eli-darkly Jun 19, 2020
2d6f974
Merge branch 'v2' of github.com:launchdarkly/go-sdk-common into v2
eli-darkly Jun 24, 2020
1bfadb3
Merge branch 'v2' of github.com:launchdarkly/go-sdk-common into v2
eli-darkly Jun 24, 2020
cc24b10
add log test helper package
eli-darkly Jun 25, 2020
2360388
Merge branch 'v2' of github.com:launchdarkly/go-sdk-common into v2
eli-darkly Jul 1, 2020
d755157
Merge pull request #27 from launchdarkly/eb/ch80986/mock-log
eli-darkly Jul 6, 2020
68c56ca
add UnmarshalText and other helper methods to OptionalString
eli-darkly Jul 6, 2020
e4e7b0f
Merge pull request #29 from launchdarkly/eb/ch81912/opt-string-parsing
eli-darkly Jul 6, 2020
290b7ab
use go-test-helpers v2 (removes unwanted eventsource dependency) (#28)
eli-darkly Jul 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ module gopkg.in/launchdarkly/go-sdk-common.v2
go 1.13

require (
github.com/launchdarkly/go-test-helpers v1.1.3
github.com/launchdarkly/go-test-helpers/v2 v2.2.0
github.com/stretchr/testify v1.6.1
)
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/launchdarkly/eventsource v0.0.0-20190617224151-6b837031c9ca/go.mod h1:y6+/PY1Az/AQyjCMsFt5mOZq1tfM0/KOQRS4f0pVe3M=
github.com/launchdarkly/go-test-helpers v1.1.3 h1:SgliO/iURt4AgQwuu27HFsDCqRenUvpM/5Um+Op+1hw=
github.com/launchdarkly/go-test-helpers v1.1.3/go.mod h1:bdm93TgmHJ/CbNIN5BpbZsaoIbd9ZVexRrINSBPz+DE=
github.com/launchdarkly/go-test-helpers/v2 v2.2.0 h1:L3kGILP/6ewikhzhdNkHy1b5y4zs50LueWenVF0sBbs=
github.com/launchdarkly/go-test-helpers/v2 v2.2.0/go.mod h1:L7+th5govYp5oKU9iN7To5PgznBuIjBPn+ejqKR0avw=
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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
Expand All @@ -14,7 +13,6 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/launchdarkly/go-sdk-common.v1 v1.0.0-20200204015611-d48d1b4f4e70/go.mod h1:2kE5FCTDQ53bqRSOfJpE5fdiAQYiN9LESONDNyEBFjI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
2 changes: 2 additions & 0 deletions ldlog/package_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
//
// These types and methods allow you to customize the logging behavior of the SDK, such as
// filtering log output by level or redirecting it to another destination.
//
// For a test implementation that captures output, see the ldlogtest package.
package ldlog
130 changes: 130 additions & 0 deletions ldlogtest/mock_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package ldlogtest

import (
"fmt"
"regexp"
"strings"
"sync"
"testing"

"github.com/stretchr/testify/assert"

"gopkg.in/launchdarkly/go-sdk-common.v2/ldlog"
)

// MockLogItem represents a log message captured by MockLog.
type MockLogItem struct {
Level ldlog.LogLevel
Message string
}

// MockLog provides the ability to capture log output.
//
// It contains a Loggers instance which can be used like any other Loggers, but all of the output is
// captured by the enclosing MockLog and can be retrieved with the MockLog methods.
//
// mockLog := ldlogtest.NewMockLog()
// mockLog.Loggers.Warn("message")
// mockLog.Loggers.Warnf("also: %t", true)
// warnMessages := mockLog.GetOutput(ldlog.Warn) // returns {"message", "also: true"}
type MockLog struct {
// Loggers is the ldlog.Loggers instance to be used for tests.
Loggers ldlog.Loggers
// Output is a map containing all of the lines logged for each level. The level prefix is removed from the text.
output map[ldlog.LogLevel][]string
// AllOutput is a list of all the log output for any level in order. The level prefix is removed from the text.
allOutput []MockLogItem
lock sync.Mutex
}

// NewMockLog creates a log-capturing object.
func NewMockLog() *MockLog {
ret := &MockLog{output: make(map[ldlog.LogLevel][]string)}
for _, level := range []ldlog.LogLevel{ldlog.Debug, ldlog.Info, ldlog.Warn, ldlog.Error} {
ret.Loggers.SetBaseLoggerForLevel(level, mockBaseLogger{owner: ret, level: level})
}
return ret
}

// GetOutput returns the captured output for a specific log level.
func (ml *MockLog) GetOutput(level ldlog.LogLevel) []string {
ml.lock.Lock()
defer ml.lock.Unlock()
lines := ml.output[level]
ret := make([]string, len(lines))
copy(ret, lines)
return ret
}

// GetAllOutput returns the captured output for all log levels.
func (ml *MockLog) GetAllOutput() []MockLogItem {
ml.lock.Lock()
defer ml.lock.Unlock()
ret := make([]MockLogItem, len(ml.allOutput))
copy(ret, ml.allOutput)
return ret
}

// HasMessageMatch tests whether there is a log message of this level that matches this regex.
func (ml *MockLog) HasMessageMatch(level ldlog.LogLevel, pattern string) bool {
_, found := ml.findMessageMatching(level, pattern)
return found
}

// AssertMessageMatch asserts whether there is a log message of this level that matches this regex.
// This is equivalent to using assert.True or assert.False with HasMessageMatch, except that if the
// test fails, it includes the actual log messages in the failure message.
func (ml *MockLog) AssertMessageMatch(t *testing.T, shouldMatch bool, level ldlog.LogLevel, pattern string) {
line, hasMatch := ml.findMessageMatching(level, pattern)
if hasMatch != shouldMatch {
if shouldMatch {
assert.Fail(
t,
"log did not contain expected message",
"level: %s, pattern: /%s/, messages: %v",
level,
pattern,
ml.GetOutput(level),
)
} else {
assert.Fail(
t,
"log contained unexpected message",
"level: %s, message: [%s]",
level,
line,
)
}
}
}

func (ml *MockLog) logLine(level ldlog.LogLevel, line string) {
ml.lock.Lock()
defer ml.lock.Unlock()
message := strings.TrimPrefix(line, strings.ToUpper(level.String())+": ")
ml.output[level] = append(ml.output[level], message)
ml.allOutput = append(ml.allOutput, MockLogItem{level, message})
}

func (ml *MockLog) findMessageMatching(level ldlog.LogLevel, pattern string) (string, bool) {
r := regexp.MustCompile(pattern)
for _, line := range ml.GetOutput(level) {
if r.MatchString(line) {
return line, true
}
}
return "", false
}

type mockBaseLogger struct {
owner *MockLog
level ldlog.LogLevel
}

func (l mockBaseLogger) Println(values ...interface{}) {
l.owner.logLine(l.level, strings.TrimSuffix(fmt.Sprintln(values...), "\n"))
}

func (l mockBaseLogger) Printf(format string, values ...interface{}) {
l.owner.logLine(l.level, fmt.Sprintf(format, values...))
}
76 changes: 76 additions & 0 deletions ldlogtest/mock_log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ldlogtest

import (
"testing"

"github.com/stretchr/testify/assert"

"gopkg.in/launchdarkly/go-sdk-common.v2/ldlog"
)

type mockLogTestValues struct {
level ldlog.LogLevel
nextLevel ldlog.LogLevel
println func(ldlog.Loggers, ...interface{})
printf func(ldlog.Loggers, string, ...interface{})
}

var allTestValues = []mockLogTestValues{
{ldlog.Debug, ldlog.Info, ldlog.Loggers.Debug, ldlog.Loggers.Debugf},
{ldlog.Info, ldlog.Warn, ldlog.Loggers.Info, ldlog.Loggers.Infof},
{ldlog.Warn, ldlog.Error, ldlog.Loggers.Warn, ldlog.Loggers.Warnf},
{ldlog.Error, ldlog.None, ldlog.Loggers.Error, ldlog.Loggers.Errorf},
}

func TestMockLogCapturesMessagesForLevel(t *testing.T) {
for _, v := range allTestValues {
t.Run(v.level.String(), func(t *testing.T) {
m := NewMockLog()
m.Loggers.SetMinLevel(v.level)
v.println(m.Loggers, "hello")
v.printf(m.Loggers, "yes: %t", true)

m.Loggers.SetMinLevel(v.nextLevel)
v.println(m.Loggers, "shouldn't see this")

o1 := m.GetOutput(v.level)
assert.Equal(t, []string{"hello", "yes: true"}, o1)

o2 := m.GetAllOutput()
assert.Equal(t, []MockLogItem{
{v.level, "hello"},
{v.level, "yes: true"},
}, o2)
})
}
}

func TestMessageMatching(t *testing.T) {
m := NewMockLog()
m.Loggers.Info("first")
m.Loggers.Info("second")
m.Loggers.Warn("third")

testShouldFail := func(t *testing.T, action func(*testing.T)) {
var tt testing.T
action(&tt)
assert.True(t, tt.Failed(), "test should have failed")
}

shouldMatch := func(t *testing.T, level ldlog.LogLevel, pattern string) {
assert.True(t, m.HasMessageMatch(level, pattern))
m.AssertMessageMatch(t, true, level, pattern)
testShouldFail(t, func(tt *testing.T) { m.AssertMessageMatch(tt, false, level, pattern) })
}

shouldNotMatch := func(t *testing.T, level ldlog.LogLevel, pattern string) {
assert.False(t, m.HasMessageMatch(level, pattern))
m.AssertMessageMatch(t, false, level, pattern)
testShouldFail(t, func(tt *testing.T) { m.AssertMessageMatch(tt, true, level, pattern) })
}

shouldMatch(t, ldlog.Info, ".econd")
shouldMatch(t, ldlog.Warn, "t.*")
shouldNotMatch(t, ldlog.Info, "third")
shouldNotMatch(t, ldlog.Error, ".")
}
7 changes: 7 additions & 0 deletions ldlogtest/package_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Package ldlogtest contains test helpers for use with the ldlog package.
//
// This package provides the MockLog type, which allows you to capture output that is sent to the
// ldlog.Loggers API. This can be useful in test code for verifying that some component produces the
// log output you expect it to. It is separate from the ldlog package because production code
// normally will not use this tool.
package ldlogtest
38 changes: 36 additions & 2 deletions ldvalue/optional_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ func NewOptionalString(value string) OptionalString {
}

// NewOptionalStringFromPointer constructs an OptionalString from a string pointer. If the pointer
// is non-nil, then the OptionalString copies its value; otherwise the OptionalString is empty.
// is non-nil, then the OptionalString copies its value; otherwise the OptionalString has no value.
func NewOptionalStringFromPointer(valuePointer *string) OptionalString {
if valuePointer == nil {
return OptionalString{hasValue: false}
}
return OptionalString{value: *valuePointer, hasValue: true}
}

// IsDefined returns true if the OptionalString contains a string value, or false if it is empty.
// IsDefined returns true if the OptionalString contains a string value, or false if it has no value.
func (o OptionalString) IsDefined() bool {
return o.hasValue
}
Expand All @@ -71,6 +71,15 @@ func (o OptionalString) OrElse(valueIfEmpty string) string {
return valueIfEmpty
}

// OnlyIfNonEmptyString returns the same OptionalString unless it contains an empty string (""), in
// which case it returns an OptionalString that has no value.
func (o OptionalString) OnlyIfNonEmptyString() OptionalString {
if o.hasValue && o.value == "" {
return OptionalString{}
}
return o
}

// AsPointer returns the OptionalString's value as a string pointer if it has a value, or
// nil otherwise.
//
Expand Down Expand Up @@ -163,3 +172,28 @@ func (o OptionalString) WriteToJSONBuffer(j *jsonstream.JSONBuffer) {
j.WriteNull()
}
}

// MarshalText implements the encoding.TextMarshaler interface.
//
// Marshaling an empty OptionalString{} will return nil, rather than a zero-length slice.
func (o OptionalString) MarshalText() ([]byte, error) {
if o.hasValue {
return []byte(o.value), nil
}
return nil, nil
}

// UnmarshalText implements the encoding.TextUnmarshaler interface.
//
// This allows OptionalString to be used with packages that can parse text content, such as gcfg.
//
// If the byte slice is nil, rather than zero-length, it will set the target value to an empty
// OptionalString{}. Otherwise, it will set it to a string value.
func (o *OptionalString) UnmarshalText(text []byte) error {
if text == nil {
*o = OptionalString{}
} else {
*o = NewOptionalString(string(text))
}
return nil
}
38 changes: 36 additions & 2 deletions ldvalue/optional_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,19 @@ func TestOptionalStringFromNonNilPointer(t *testing.T) {
assert.False(t, p == o.AsPointer()) // should not be the same pointer, just the same underlying string
}

func TestOptionalStringOnlyIfNonEmptyString(t *testing.T) {
assert.Equal(t, OptionalString{}, OptionalString{}.OnlyIfNonEmptyString())
assert.Equal(t, OptionalString{}, NewOptionalString("").OnlyIfNonEmptyString())
assert.Equal(t, NewOptionalString("x"), NewOptionalString("x").OnlyIfNonEmptyString())
}

func TestOptionalStringAsStringer(t *testing.T) {
assert.Equal(t, "[none]", OptionalString{}.String())
assert.Equal(t, "[empty]", NewOptionalString("").String())
assert.Equal(t, "x", NewOptionalString("x").String())
}

func TestOptionalStringMarshalling(t *testing.T) {
func TestOptionalStringJSONMarshalling(t *testing.T) {
bytes, err := json.Marshal(OptionalString{})
assert.NoError(t, err)
assert.Equal(t, nullAsJSON, string(bytes))
Expand All @@ -85,7 +91,7 @@ func TestOptionalStringMarshalling(t *testing.T) {
assert.Equal(t, `"a \"good\" string",null`, string(bytes))
}

func TestOptionalStringUnmarshalling(t *testing.T) {
func TestOptionalStringJSONUnmarshalling(t *testing.T) {
var o OptionalString
err := json.Unmarshal([]byte(nullAsJSON), &o)
assert.NoError(t, err)
Expand Down Expand Up @@ -113,3 +119,31 @@ type structWithOptionalStrings struct {
S2 OptionalString `json:"s2"`
S3 OptionalString `json:"s3"`
}

func TestOptionalStringTextMarshalling(t *testing.T) {
b, e := NewOptionalString("x").MarshalText()
assert.NoError(t, e)
assert.Equal(t, []byte("x"), b)

b, e = NewOptionalString("").MarshalText()
assert.NoError(t, e)
assert.Equal(t, []byte{}, b)

b, e = OptionalString{}.MarshalText()
assert.NoError(t, e)
assert.Nil(t, b)
}

func TestOptionalStringTextUnmarshalling(t *testing.T) {
var o1 OptionalString
assert.NoError(t, o1.UnmarshalText([]byte("x")))
assert.Equal(t, NewOptionalString("x"), o1)

var o2 OptionalString
assert.NoError(t, o2.UnmarshalText([]byte("")))
assert.Equal(t, NewOptionalString(""), o2)

var o3 OptionalString
assert.NoError(t, o3.UnmarshalText(nil))
assert.Equal(t, OptionalString{}, o3)
}
2 changes: 1 addition & 1 deletion ldvalue/value_base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/stretchr/testify/assert"

helpers "github.com/launchdarkly/go-test-helpers"
helpers "github.com/launchdarkly/go-test-helpers/v2"
)

func TestValueTypes(t *testing.T) {
Expand Down