1 package storetest
2
3 import (
4 "reflect"
5 "testing"
6
7 "github.com/launchdarkly/go-sdk-common/v3/ldtime"
8 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
9 "github.com/launchdarkly/go-server-sdk/v6/subsystems"
10 "github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoreimpl"
11 "github.com/launchdarkly/go-server-sdk/v6/testhelpers"
12
13 "github.com/launchdarkly/go-test-helpers/v3/testbox"
14
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 )
18
19 const fakeUserHash = "userhash"
20
21
22
23 type BigSegmentStoreTestSuite struct {
24 storeFactoryFn func(string) subsystems.ComponentConfigurer[subsystems.BigSegmentStore]
25 clearDataFn func(string) error
26 setMetadataFn func(string, subsystems.BigSegmentStoreMetadata) error
27 setSegmentsFn func(prefix, userHashKey string, included []string, excluded []string) error
28 }
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 func NewBigSegmentStoreTestSuite(
47 storeFactoryFn func(prefix string) subsystems.ComponentConfigurer[subsystems.BigSegmentStore],
48 clearDataFn func(prefix string) error,
49 setMetadataFn func(prefix string, metadata subsystems.BigSegmentStoreMetadata) error,
50 setSegmentsFn func(prefix string, userHashKey string, included []string, excluded []string) error,
51 ) *BigSegmentStoreTestSuite {
52 return &BigSegmentStoreTestSuite{
53 storeFactoryFn: storeFactoryFn,
54 clearDataFn: clearDataFn,
55 setMetadataFn: setMetadataFn,
56 setSegmentsFn: setSegmentsFn,
57 }
58 }
59
60
61 func (s *BigSegmentStoreTestSuite) Run(t *testing.T) {
62 s.runInternal(testbox.RealTest(t))
63 }
64
65 func (s *BigSegmentStoreTestSuite) runInternal(t testbox.TestingT) {
66 t.Run("GetMetadata", s.runMetadataTests)
67 t.Run("GetMembership", s.runMembershipTests)
68 }
69
70 func (s *BigSegmentStoreTestSuite) runMetadataTests(t testbox.TestingT) {
71 t.Run("valid value", func(t testbox.TestingT) {
72 expected := subsystems.BigSegmentStoreMetadata{LastUpToDate: ldtime.UnixMillisecondTime(1234567890)}
73
74 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
75 require.NoError(t, s.setMetadataFn("", expected))
76
77 meta, err := store.GetMetadata()
78 require.NoError(t, err)
79 assert.Equal(t, expected, meta)
80 })
81 })
82
83 t.Run("no value", func(t testbox.TestingT) {
84 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
85 meta, err := store.GetMetadata()
86
87
88 assert.Equal(t, ldtime.UnixMillisecondTime(0), meta.LastUpToDate)
89 assert.NoError(t, err)
90 })
91 })
92 }
93
94 func (s *BigSegmentStoreTestSuite) runMembershipTests(t testbox.TestingT) {
95 t.Run("not found", func(t testbox.TestingT) {
96 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
97 um, err := store.GetMembership(fakeUserHash)
98 require.NoError(t, err)
99 assertEqualMembership(t, nil, nil, um)
100 })
101 })
102
103 t.Run("includes only", func(t testbox.TestingT) {
104 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
105 require.NoError(t, s.setSegmentsFn("", fakeUserHash, []string{"key1", "key2"}, nil))
106
107 um, err := store.GetMembership(fakeUserHash)
108 require.NoError(t, err)
109 assertEqualMembership(t, []string{"key1", "key2"}, nil, um)
110 })
111 })
112
113 t.Run("excludes only", func(t testbox.TestingT) {
114 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
115 require.NoError(t, s.setSegmentsFn("", fakeUserHash, nil, []string{"key1", "key2"}))
116
117 um, err := store.GetMembership(fakeUserHash)
118 require.NoError(t, err)
119 assertEqualMembership(t, nil, []string{"key1", "key2"}, um)
120 })
121 })
122
123 t.Run("includes and excludes", func(t testbox.TestingT) {
124 s.withStoreAndEmptyData(t, func(store subsystems.BigSegmentStore) {
125 require.NoError(t, s.setSegmentsFn("", fakeUserHash, []string{"key1", "key2"}, []string{"key2", "key3"}))
126
127
128 um, err := store.GetMembership(fakeUserHash)
129 require.NoError(t, err)
130 assertEqualMembership(t, []string{"key1", "key2"}, []string{"key3"}, um)
131 })
132 })
133 }
134
135 func (s *BigSegmentStoreTestSuite) withStoreAndEmptyData(
136 t testbox.TestingT,
137 action func(subsystems.BigSegmentStore),
138 ) {
139 require.NoError(t, s.clearDataFn(""))
140
141 testhelpers.WithMockLoggingContext(t, func(context subsystems.ClientContext) {
142 store, err := s.storeFactoryFn("").Build(context)
143 require.NoError(t, err)
144 defer func() {
145 _ = store.Close()
146 }()
147
148 action(store)
149 })
150 }
151
152 func assertEqualMembership(
153 t assert.TestingT,
154 expectedIncludes []string,
155 expectedExcludes []string,
156 actual subsystems.BigSegmentMembership,
157 ) {
158
159
160
161
162 expected := ldstoreimpl.NewBigSegmentMembershipFromSegmentRefs(expectedIncludes, expectedExcludes)
163 if reflect.TypeOf(actual) == reflect.TypeOf(expected) {
164 assert.Equal(t, expected, actual)
165 } else {
166 for _, inc := range expectedIncludes {
167 assert.Equal(t, ldvalue.NewOptionalBool(true), actual.CheckMembership(inc), "for key %q", inc)
168 }
169 for _, exc := range expectedIncludes {
170 assert.Equal(t, ldvalue.NewOptionalBool(false), actual.CheckMembership(exc), "for key %q", exc)
171 }
172
173 assert.Equal(t, ldvalue.OptionalBool{}, actual.CheckMembership("unused-key"), `for key "unused-key"`)
174 }
175 }
176
View as plain text