1 package mocks
2
3 import (
4 "encoding/json"
5 "sync"
6 "testing"
7 "time"
8
9 "github.com/launchdarkly/go-server-sdk/v6/internal/datakinds"
10 "github.com/launchdarkly/go-server-sdk/v6/subsystems"
11 "github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoretypes"
12 "github.com/launchdarkly/go-server-sdk/v6/testhelpers/ldservices"
13
14 th "github.com/launchdarkly/go-test-helpers/v3"
15
16 "github.com/stretchr/testify/assert"
17 )
18
19
20 type CapturingDataStore struct {
21 realStore subsystems.DataStore
22 statusMonitoringEnabled bool
23 fakeError error
24 inits chan []ldstoretypes.Collection
25 upserts chan UpsertParams
26 lock sync.Mutex
27 }
28
29
30 type UpsertParams struct {
31 Kind ldstoretypes.DataKind
32 Key string
33 Item ldstoretypes.ItemDescriptor
34 }
35
36
37 func NewCapturingDataStore(realStore subsystems.DataStore) *CapturingDataStore {
38 return &CapturingDataStore{
39 realStore: realStore,
40 inits: make(chan []ldstoretypes.Collection, 10),
41 upserts: make(chan UpsertParams, 10),
42 statusMonitoringEnabled: true,
43 }
44 }
45
46
47 func (d *CapturingDataStore) Init(allData []ldstoretypes.Collection) error {
48 for _, coll := range allData {
49 AssertNotNil(coll.Kind)
50 }
51 d.inits <- allData
52 _ = d.realStore.Init(allData)
53 d.lock.Lock()
54 defer d.lock.Unlock()
55 return d.fakeError
56 }
57
58
59 func (d *CapturingDataStore) Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.ItemDescriptor, error) {
60 AssertNotNil(kind)
61 if d.fakeError != nil {
62 return ldstoretypes.ItemDescriptor{}.NotFound(), d.fakeError
63 }
64 return d.realStore.Get(kind, key)
65 }
66
67
68 func (d *CapturingDataStore) GetAll(kind ldstoretypes.DataKind) ([]ldstoretypes.KeyedItemDescriptor, error) {
69 AssertNotNil(kind)
70 if d.fakeError != nil {
71 return nil, d.fakeError
72 }
73 return d.realStore.GetAll(kind)
74 }
75
76
77 func (d *CapturingDataStore) Upsert(
78 kind ldstoretypes.DataKind,
79 key string,
80 newItem ldstoretypes.ItemDescriptor,
81 ) (bool, error) {
82 AssertNotNil(kind)
83 d.upserts <- UpsertParams{kind, key, newItem}
84 updated, _ := d.realStore.Upsert(kind, key, newItem)
85 d.lock.Lock()
86 defer d.lock.Unlock()
87 return updated, d.fakeError
88 }
89
90
91 func (d *CapturingDataStore) IsInitialized() bool {
92 return true
93 }
94
95
96
97 func (d *CapturingDataStore) IsStatusMonitoringEnabled() bool {
98 d.lock.Lock()
99 defer d.lock.Unlock()
100 return d.statusMonitoringEnabled
101 }
102
103
104 func (d *CapturingDataStore) Close() error {
105 return nil
106 }
107
108
109 func (d *CapturingDataStore) SetStatusMonitoringEnabled(statusMonitoringEnabled bool) {
110 d.lock.Lock()
111 defer d.lock.Unlock()
112 d.statusMonitoringEnabled = statusMonitoringEnabled
113 }
114
115
116 func (d *CapturingDataStore) SetFakeError(fakeError error) {
117 d.lock.Lock()
118 defer d.lock.Unlock()
119 d.fakeError = fakeError
120 }
121
122
123 func (d *CapturingDataStore) WaitForNextInit(
124 t *testing.T,
125 timeout time.Duration,
126 ) []ldstoretypes.Collection {
127 return th.RequireValue(t, d.inits, timeout, "timed out before receiving expected init")
128 }
129
130
131 func (d *CapturingDataStore) WaitForInit(
132 t *testing.T,
133 data *ldservices.ServerSDKData,
134 timeout time.Duration,
135 ) {
136 inited := th.RequireValue(t, d.inits, timeout, "timed out before receiving expected init")
137 assertReceivedInitDataEquals(t, data, inited)
138 }
139
140
141 func (d *CapturingDataStore) WaitForNextUpsert(
142 t *testing.T,
143 timeout time.Duration,
144 ) UpsertParams {
145 return th.RequireValue(t, d.upserts, timeout, "timed out before receiving expected update")
146 }
147
148
149 func (d *CapturingDataStore) WaitForUpsert(
150 t *testing.T,
151 kind ldstoretypes.DataKind,
152 key string,
153 version int,
154 timeout time.Duration,
155 ) UpsertParams {
156 upserted := d.WaitForNextUpsert(t, timeout)
157 assert.Equal(t, kind, upserted.Kind)
158 assert.Equal(t, key, upserted.Key)
159 assert.Equal(t, version, upserted.Item.Version)
160 assert.NotNil(t, upserted.Item.Item)
161 return upserted
162 }
163
164
165 func (d *CapturingDataStore) WaitForDelete(
166 t *testing.T,
167 kind ldstoretypes.DataKind,
168 key string,
169 version int,
170 timeout time.Duration,
171 ) {
172 upserted := d.WaitForNextUpsert(t, timeout)
173 assert.Equal(t, kind, upserted.Kind)
174 assert.Equal(t, key, upserted.Key)
175 assert.Equal(t, version, upserted.Item.Version)
176 assert.Nil(t, upserted.Item.Item)
177 }
178
179 func assertReceivedInitDataEquals(
180 t *testing.T,
181 expected *ldservices.ServerSDKData,
182 received []ldstoretypes.Collection,
183 ) {
184 assert.Equal(t, 2, len(received))
185 for _, coll := range received {
186 var itemsMap map[string]interface{}
187 switch coll.Kind {
188 case datakinds.Features:
189 itemsMap = expected.FlagsMap
190 case datakinds.Segments:
191 itemsMap = expected.SegmentsMap
192 default:
193 assert.Fail(t, "received unknown data kind: %s", coll.Kind)
194 }
195 assert.Equal(t, len(itemsMap), len(coll.Items))
196 for _, item := range coll.Items {
197 found, ok := itemsMap[item.Key]
198 assert.True(t, ok, item.Key)
199 bytes, _ := json.Marshal(found)
200 var props map[string]interface{}
201 assert.NoError(t, json.Unmarshal(bytes, &props))
202 assert.Equal(t, props["version"].(float64), float64(item.Item.Version))
203 }
204 }
205 }
206
View as plain text