Skip to content

Commit de408af

Browse files
authored
chore: Update data sources to send FDv2-compatible payloads (#229)
This initial commit is quite limited in scope. It updates the existing server-side contract tests to send the FDv2 equivalent payloads to SDKs under tests. These changes do not include **any new tests**. Additional commits are required to expand test coverage to include FDv2 specific handling (e.g. xfer-changes, state handling). It should also be noted these changes have likely broken the client, mobile, PHP, and Roku integrations. That's okay because we will be updating each of those in subsequent commits as well as the FDv2 work progresses.
1 parent 1179052 commit de408af

18 files changed

+481
-202
lines changed

framework/helpers/channels_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import (
44
"testing"
55
"time"
66

7-
"github.com/launchdarkly/sdk-test-harness/v2/framework/opt"
87
"github.com/stretchr/testify/assert"
8+
9+
"github.com/launchdarkly/sdk-test-harness/v2/framework/opt"
910
)
1011

1112
func TestNonBlockingSend(t *testing.T) {

framework/ldtest/errors_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package ldtest
33
import (
44
"testing"
55

6-
"github.com/launchdarkly/sdk-test-harness/v2/framework/ldtest/internal"
76
"github.com/stretchr/testify/assert"
87
"github.com/stretchr/testify/require"
8+
9+
"github.com/launchdarkly/sdk-test-harness/v2/framework/ldtest/internal"
910
)
1011

1112
func TestStacktrace(t *testing.T) {

framework/types.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package framework
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/launchdarkly/eventsource"
7+
)
8+
9+
type PollingPayload struct {
10+
Events []PayloadEvent `json:"events"`
11+
}
12+
13+
type PayloadEvent struct {
14+
Name string `json:"name"`
15+
EventData interface{} `json:"data"`
16+
}
17+
18+
type ChangeSet struct {
19+
intent *ServerIntent //nolint:unused
20+
events []eventsource.Event //nolint:unused
21+
}
22+
23+
type ServerIntent struct {
24+
Payloads []Payload `json:"payloads"`
25+
}
26+
27+
type Payload struct {
28+
// The id here doesn't seem to match the state that is included in the
29+
// payload transferred object.
30+
31+
// It would be nice if we had the same value available in both so we could
32+
// use that as the key consistently throughout the process.
33+
ID string `json:"id"`
34+
Target int `json:"target"`
35+
Code string `json:"code"`
36+
Reason string `json:"reason"`
37+
}
38+
39+
// BaseObject is is the general shape of a put-object event. The delete-object
40+
// is the same, with the object field being nil.
41+
type BaseObject struct {
42+
Version int `json:"version"`
43+
Kind string `json:"kind"`
44+
Key string `json:"key"`
45+
Object json.RawMessage `json:"object,omitempty"`
46+
}
47+
48+
type PayloadTransferred struct {
49+
State string `json:"state"`
50+
Version int `json:"version"`
51+
}
52+
53+
//nolint:godox
54+
// TODO: Todd doesn't have this in his spec. What are we going to do here?
55+
56+
type ErrorEvent struct {
57+
PayloadID string `json:"payloadId"`
58+
Reason string `json:"reason"`
59+
}
60+
61+
// type heartBeat struct{}
62+
63+
type Goodbye struct {
64+
Reason string `json:"reason"`
65+
Silent bool `json:"silent"`
66+
Catastrophe bool `json:"catastrophe"`
67+
//nolint:godox
68+
// TODO: Might later include some advice or backoff information
69+
}

mockld/polling_service.go

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,29 +152,89 @@ func (p *PollingService) pollingHandler(getDataFn func(*PollingService, *http.Re
152152

153153
func (p *PollingService) standardPollingHandler() http.Handler {
154154
return p.pollingHandler(func(p *PollingService, r *http.Request) []byte {
155-
return p.currentData.Serialize()
155+
fdv2SdkData, ok := p.currentData.(FDv2SDKData)
156+
if !ok {
157+
p.debugLogger.Println("poller cannot handle non-fdv2 sdk data at this time")
158+
return nil
159+
}
160+
161+
// QUESTION: How dynamic do we need to make this?
162+
serverIntent := framework.ServerIntent{
163+
Payloads: []framework.Payload{
164+
{
165+
ID: "payloadID",
166+
Target: 1,
167+
Code: "xfer-full",
168+
Reason: "payload-missing",
169+
},
170+
},
171+
}
172+
173+
payloadTransferred := framework.PayloadTransferred{
174+
//nolint:godox
175+
// TODO: Need to replace this with a valid state value
176+
State: "state",
177+
Version: 1,
178+
}
179+
180+
events := make([]framework.PayloadEvent, 0, len(fdv2SdkData)+2)
181+
events = append(events, framework.PayloadEvent{
182+
Name: "server-intent",
183+
EventData: serverIntent,
184+
})
185+
for _, obj := range fdv2SdkData {
186+
events = append(events, framework.PayloadEvent{
187+
Name: "put-object",
188+
EventData: obj,
189+
})
190+
}
191+
events = append(events, framework.PayloadEvent{
192+
Name: "payload-transferred",
193+
EventData: payloadTransferred,
194+
})
195+
196+
payload := framework.PollingPayload{
197+
Events: events,
198+
}
199+
200+
data, err := json.Marshal(payload)
201+
if err != nil {
202+
p.debugLogger.Printf("failed to marshal polling data: %v", err)
203+
return nil
204+
}
205+
206+
return data
156207
})
157208
}
158209

159210
func (p *PollingService) phpFlagHandler() http.Handler {
160211
return p.pollingHandler(func(p *PollingService, r *http.Request) []byte {
161-
data, _ := p.currentData.(ServerSDKData)
162-
return data["flags"][mux.Vars(r)["key"]]
212+
//nolint:godox
213+
// TODO: Update this logic
214+
return []byte("UNSUPPORTED")
215+
// data, _ := p.currentData.(ServerSDKData)
216+
// return data["flags"][mux.Vars(r)["key"]]
163217
})
164218
}
165219

166220
func (p *PollingService) phpSegmentHandler() http.Handler {
167221
return p.pollingHandler(func(p *PollingService, r *http.Request) []byte {
168-
data, _ := p.currentData.(ServerSDKData)
169-
return data["segments"][mux.Vars(r)["key"]]
222+
//nolint:godox
223+
// TODO: Update this logic
224+
return []byte("UNSUPPORTED")
225+
// data, _ := p.currentData.(ServerSDKData)
226+
// return data["segments"][mux.Vars(r)["key"]]
170227
})
171228
}
172229

173230
func (p *PollingService) phpAllFlagsHandler() http.Handler {
174231
return p.pollingHandler(func(p *PollingService, r *http.Request) []byte {
175-
data, _ := p.currentData.(ServerSDKData)
176-
flagsJSON, _ := json.Marshal(data["flags"])
177-
return flagsJSON
232+
//nolint:godox
233+
// TODO: Update this logic
234+
return []byte("UNSUPPORTED")
235+
// data, _ := p.currentData.(ServerSDKData)
236+
// flagsJSON, _ := json.Marshal(data["flags"])
237+
// return flagsJSON
178238
})
179239
}
180240

mockld/polling_service_test.go

Lines changed: 62 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,84 @@
11
package mockld
22

33
import (
4-
"encoding/json"
5-
"fmt"
64
"io"
75
"net/http"
86
"net/http/httptest"
97
"testing"
108

119
"github.com/launchdarkly/go-test-helpers/v2/httphelpers"
1210
m "github.com/launchdarkly/go-test-helpers/v2/matchers"
13-
h "github.com/launchdarkly/sdk-test-harness/v2/framework/helpers"
1411

1512
"github.com/launchdarkly/go-sdk-common/v3/ldlog"
1613
"github.com/launchdarkly/go-sdk-common/v3/ldlogtest"
17-
"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
1814

1915
"github.com/stretchr/testify/require"
2016
)
2117

22-
func TestPollingServiceServerSide(t *testing.T) {
23-
doPollingServiceTests(
24-
t,
25-
ServerSideSDK,
26-
EmptyServerSDKData(),
27-
NewServerSDKDataBuilder().RawFlag("flag1", json.RawMessage(`{"key": "flag1"}`)).Build(),
28-
"GET",
29-
"/sdk/latest-all",
30-
)
31-
}
18+
// TODO: Re-enable these tests
3219

33-
func TestPollingServiceMobile(t *testing.T) {
34-
for _, oldUserPaths := range []bool{false, true} {
35-
userOrContext := h.IfElse(oldUserPaths, "user", "context")
36-
t.Run(userOrContext, func(t *testing.T) {
37-
for _, useReport := range []bool{true, false} {
38-
method := h.IfElse(useReport, "REPORT", "GET")
39-
endpoint := h.IfElse(
40-
useReport,
41-
fmt.Sprintf("/msdk/evalx/%s", userOrContext),
42-
fmt.Sprintf("/msdk/evalx/%ss/fakeuserdata", userOrContext),
43-
)
44-
t.Run(method, func(t *testing.T) {
45-
doPollingServiceTests(
46-
t,
47-
MobileSDK,
48-
EmptyClientSDKData(),
49-
NewClientSDKDataBuilder().FlagWithValue("flag1", 1, ldvalue.String("yes"), 0).Build(),
50-
method,
51-
endpoint,
52-
)
53-
})
54-
}
55-
})
56-
}
57-
}
20+
// func TestPollingServiceServerSide(t *testing.T) {
21+
// doPollingServiceTests(
22+
// t,
23+
// ServerSideSDK,
24+
// EmptyServerSDKData(),
25+
// NewServerSDKDataBuilder().RawFlag("flag1", json.RawMessage(`{"key": "flag1"}`)).Build(),
26+
// "GET",
27+
// "/sdk/latest-all",
28+
// )
29+
// }
5830

59-
func TestPollingServiceJSClient(t *testing.T) {
60-
for _, oldUserPaths := range []bool{false, true} {
61-
userOrContext := h.IfElse(oldUserPaths, "user", "context")
62-
t.Run(userOrContext, func(t *testing.T) {
63-
for _, useReport := range []bool{true, false} {
64-
method := h.IfElse(useReport, "REPORT", "GET")
65-
endpoint := h.IfElse(
66-
useReport,
67-
fmt.Sprintf("/sdk/evalx/fakeid/%s", userOrContext),
68-
fmt.Sprintf("/sdk/evalx/fakeid/%ss/fakeuserdata", userOrContext),
69-
)
70-
t.Run(method, func(t *testing.T) {
71-
doPollingServiceTests(
72-
t,
73-
JSClientSDK,
74-
EmptyClientSDKData(),
75-
NewClientSDKDataBuilder().FlagWithValue("flag1", 1, ldvalue.String("yes"), 0).Build(),
76-
method,
77-
endpoint,
78-
)
79-
})
80-
}
81-
})
82-
}
83-
}
31+
// func TestPollingServiceMobile(t *testing.T) {
32+
// for _, oldUserPaths := range []bool{false, true} {
33+
// userOrContext := h.IfElse(oldUserPaths, "user", "context")
34+
// t.Run(userOrContext, func(t *testing.T) {
35+
// for _, useReport := range []bool{true, false} {
36+
// method := h.IfElse(useReport, "REPORT", "GET")
37+
// endpoint := h.IfElse(
38+
// useReport,
39+
// fmt.Sprintf("/msdk/evalx/%s", userOrContext),
40+
// fmt.Sprintf("/msdk/evalx/%ss/fakeuserdata", userOrContext),
41+
// )
42+
// t.Run(method, func(t *testing.T) {
43+
// doPollingServiceTests(
44+
// t,
45+
// MobileSDK,
46+
// EmptyClientSDKData(),
47+
// NewClientSDKDataBuilder().FlagWithValue("flag1", 1, ldvalue.String("yes"), 0).Build(),
48+
// method,
49+
// endpoint,
50+
// )
51+
// })
52+
// }
53+
// })
54+
// }
55+
// }
56+
57+
// func TestPollingServiceJSClient(t *testing.T) {
58+
// for _, oldUserPaths := range []bool{false, true} {
59+
// userOrContext := h.IfElse(oldUserPaths, "user", "context")
60+
// t.Run(userOrContext, func(t *testing.T) {
61+
// for _, useReport := range []bool{true, false} {
62+
// method := h.IfElse(useReport, "REPORT", "GET")
63+
// endpoint := h.IfElse(
64+
// useReport,
65+
// fmt.Sprintf("/sdk/evalx/fakeid/%s", userOrContext),
66+
// fmt.Sprintf("/sdk/evalx/fakeid/%ss/fakeuserdata", userOrContext),
67+
// )
68+
// t.Run(method, func(t *testing.T) {
69+
// doPollingServiceTests(
70+
// t,
71+
// JSClientSDK,
72+
// EmptyClientSDKData(),
73+
// NewClientSDKDataBuilder().FlagWithValue("flag1", 1, ldvalue.String("yes"), 0).Build(),
74+
// method,
75+
// endpoint,
76+
// )
77+
// })
78+
// }
79+
// })
80+
// }
81+
// }
8482

8583
func doPollingServiceTests(
8684
t *testing.T,

0 commit comments

Comments
 (0)