1 package ldtestdata
2
3 import (
4 "testing"
5 "time"
6
7 "github.com/launchdarkly/go-server-sdk/v6/internal/sharedtest/mocks"
8
9 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
10 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldbuilders"
11 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
12 "github.com/launchdarkly/go-server-sdk/v6/interfaces"
13 "github.com/launchdarkly/go-server-sdk/v6/internal/datastore"
14 "github.com/launchdarkly/go-server-sdk/v6/internal/sharedtest"
15 "github.com/launchdarkly/go-server-sdk/v6/subsystems"
16 "github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoreimpl"
17 "github.com/launchdarkly/go-server-sdk/v6/testhelpers/ldservices"
18
19 th "github.com/launchdarkly/go-test-helpers/v3"
20
21 "github.com/stretchr/testify/assert"
22 "github.com/stretchr/testify/require"
23 )
24
25 var (
26 threeStringValues = []ldvalue.Value{ldvalue.String("red"), ldvalue.String("green"), ldvalue.String("blue")}
27 )
28
29 type testDataSourceTestParams struct {
30 td *TestDataSource
31 updates *mocks.MockDataSourceUpdates
32 }
33
34 func testDataSourceTest(t *testing.T, action func(testDataSourceTestParams)) {
35 t.Helper()
36 store := datastore.NewInMemoryDataStore(sharedtest.NewTestLoggers())
37 var p testDataSourceTestParams
38 p.td = DataSource()
39 p.updates = mocks.NewMockDataSourceUpdates(store)
40 action(p)
41 }
42
43 func (p testDataSourceTestParams) withDataSource(t *testing.T, action func(subsystems.DataSource)) {
44 t.Helper()
45 context := subsystems.BasicClientContext{DataSourceUpdateSink: p.updates}
46 ds, err := p.td.Build(context)
47 require.NoError(t, err)
48 defer ds.Close()
49
50 closer := make(chan struct{})
51 ds.Start(closer)
52 if !th.AssertChannelClosed(t, closer, time.Millisecond, "start did not close channel") {
53 t.FailNow()
54 }
55 p.updates.RequireStatusOf(t, interfaces.DataSourceStateValid)
56
57 action(ds)
58 }
59
60 func TestTestDataSource(t *testing.T) {
61 t.Run("initializes with empty data", func(t *testing.T) {
62 testDataSourceTest(t, func(p testDataSourceTestParams) {
63 p.withDataSource(t, func(ds subsystems.DataSource) {
64 expectedData := ldservices.NewServerSDKData()
65 p.updates.DataStore.WaitForInit(t, expectedData, time.Millisecond)
66 assert.True(t, ds.IsInitialized())
67 })
68 })
69 })
70
71 t.Run("initializes with flags", func(t *testing.T) {
72 testDataSourceTest(t, func(p testDataSourceTestParams) {
73 p.td.Update(p.td.Flag("flag1").On(true)).
74 Update(p.td.Flag("flag2").On(false))
75
76 p.withDataSource(t, func(subsystems.DataSource) {
77 initData := p.updates.DataStore.WaitForNextInit(t, time.Millisecond)
78 dataMap := sharedtest.DataSetToMap(initData)
79 require.Len(t, dataMap, 2)
80 flags := dataMap[ldstoreimpl.Features()]
81 require.Len(t, flags, 2)
82
83 assert.Equal(t, 1, flags["flag1"].Version)
84 assert.Equal(t, 1, flags["flag2"].Version)
85 assert.True(t, flags["flag1"].Item.(*ldmodel.FeatureFlag).On)
86 assert.False(t, flags["flag2"].Item.(*ldmodel.FeatureFlag).On)
87 })
88 })
89 })
90
91 t.Run("adds flag", func(t *testing.T) {
92 testDataSourceTest(t, func(p testDataSourceTestParams) {
93 p.withDataSource(t, func(subsystems.DataSource) {
94 p.td.Update(p.td.Flag("flag1").On(true))
95
96 up := p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Features(), "flag1", 1, time.Millisecond)
97 assert.True(t, up.Item.Item.(*ldmodel.FeatureFlag).On)
98 })
99 })
100 })
101
102 t.Run("updates flag", func(t *testing.T) {
103 testDataSourceTest(t, func(p testDataSourceTestParams) {
104 p.td.Update(p.td.Flag("flag1").On(false))
105
106 p.withDataSource(t, func(subsystems.DataSource) {
107 p.td.Update(p.td.Flag("flag1").On(true))
108
109 up := p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Features(), "flag1", 2, time.Millisecond)
110 assert.True(t, up.Item.Item.(*ldmodel.FeatureFlag).On)
111 })
112 })
113 })
114
115 t.Run("updates status", func(t *testing.T) {
116 testDataSourceTest(t, func(p testDataSourceTestParams) {
117 p.withDataSource(t, func(subsystems.DataSource) {
118 ei := interfaces.DataSourceErrorInfo{Kind: interfaces.DataSourceErrorKindNetworkError}
119 p.td.UpdateStatus(interfaces.DataSourceStateInterrupted, ei)
120
121 status := p.updates.RequireStatusOf(t, interfaces.DataSourceStateInterrupted)
122 assert.Equal(t, ei, status.LastError)
123 })
124 })
125 })
126
127 t.Run("adds or updates preconfigured flag", func(t *testing.T) {
128 flagv1 := ldbuilders.NewFlagBuilder("flagkey").Version(1).On(true).TrackEvents(true).Build()
129 testDataSourceTest(t, func(p testDataSourceTestParams) {
130 p.withDataSource(t, func(subsystems.DataSource) {
131 p.td.UsePreconfiguredFlag(flagv1)
132
133 up := p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Features(), flagv1.Key, 1, time.Millisecond)
134 assert.Equal(t, &flagv1, up.Item.Item.(*ldmodel.FeatureFlag))
135
136 updatedFlag := flagv1
137 updatedFlag.On = false
138 expectedFlagV2 := updatedFlag
139 expectedFlagV2.Version = 2
140 p.td.UsePreconfiguredFlag(updatedFlag)
141
142 up = p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Features(), flagv1.Key, 2, time.Millisecond)
143 assert.Equal(t, &expectedFlagV2, up.Item.Item.(*ldmodel.FeatureFlag))
144 })
145 })
146 })
147
148 t.Run("adds or updates preconfigured segment", func(t *testing.T) {
149 segmentv1 := ldbuilders.NewSegmentBuilder("segmentkey").Version(1).Included("a").Build()
150 testDataSourceTest(t, func(p testDataSourceTestParams) {
151 p.withDataSource(t, func(subsystems.DataSource) {
152 p.td.UsePreconfiguredSegment(segmentv1)
153
154 up := p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Segments(), segmentv1.Key, 1, time.Millisecond)
155 assert.Equal(t, &segmentv1, up.Item.Item.(*ldmodel.Segment))
156
157 updatedSegment := segmentv1
158 updatedSegment.Included = []string{"b"}
159 expectedSegmentV2 := updatedSegment
160 expectedSegmentV2.Version = 2
161 p.td.UsePreconfiguredSegment(updatedSegment)
162
163 up = p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Segments(), segmentv1.Key, 2, time.Millisecond)
164 assert.Equal(t, &expectedSegmentV2, up.Item.Item.(*ldmodel.Segment))
165 })
166 })
167 })
168 }
169
View as plain text