Skip to content

Commit 59ce65e

Browse files
Mat001claude
andauthored
[FSSDK-11558] Mpirnovar add holdouts support (#452)
* Add holdouts support to Agent This change adds holdout support to Agent by: 1. Updating to use local go-sdk with holdouts implementation 2. Adding GetHoldoutList and GetHoldoutsForFlag methods to TestProjectConfig mock 3. Migrating CMAB prediction endpoint config from global variable to CmabConfig struct Changes: - Added go.mod replace directive to use local go-sdk for development - Implemented GetHoldoutList() and GetHoldoutsForFlag() in TestProjectConfig - Updated CMAB endpoint configuration to use CmabConfig.PredictionEndpointTemplate - Removed unused cmab package import Once go-sdk is released with holdouts, the go.mod replace can be removed and the version bumped to the new release. * Fix CMAB endpoint tests after API migration Updated tests to reflect that CMAB prediction endpoint is now configured through CmabConfig.PredictionEndpointTemplate instead of a global variable. Removed assertions that checked cmab.CMABPredictionEndpoint since the endpoint is now encapsulated within the CMAB client configuration and cannot be easily verified from outside. The tests still verify that clients are created successfully with the configured endpoints. * add uit and acceptance tests * Remove Holdouts field from OptimizelyConfig API Updated Agent to use go-sdk branch that removed the Holdouts field from OptimizelyConfig. This aligns with JavaScript and C# SDKs and fixes FSC test failures. Agent doesn't need holdouts exposed in OptimizelyConfig because: - The /v1/decide endpoint uses go-sdk's decision service which handles holdouts internally through GetHoldoutsForFlag() and GetHoldoutList() methods - OptimizelyConfig is for metadata, not decision-making - Holdout decision logic is already present in go-sdk v2.3.0 Changes: - Updated go.mod to use go-sdk@mpirnovar-fix-activate-endpoint-holdouts - Removed TestConfigIncludesHoldouts test - Removed test_config_with_holdouts and validate_holdout_structure tests - Removed session_override_sdk_key_holdouts fixture - Removed sdk_key_holdouts constant 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Update Agent to use go-sdk v2.3.0 and add holdouts acceptance tests - Updated go.mod to use released go-sdk v2.3.0 - Removed local replace directive for go-sdk development - Added comprehensive acceptance tests for holdouts functionality - test_decide_holdouts_simple.py: 3 basic tests verifying holdouts work through Agent - test_decide_holdouts.py: 7 comprehensive tests covering various holdout scenarios - User bucketed into holdout - User not in holdout audience - Multiple flags with holdouts (DecideAll) - Forced decisions overriding holdouts - Decision reasons - Impression event tracking - Multiple flags with different holdout configurations All tests verify that Agent correctly handles holdout decisions from go-sdk v2.3.0 without requiring any Agent code changes. Holdouts are evaluated internally by go-sdk's decision service and reflected in the ruleKey field. Co-Authored-By: Claude <[email protected]> * Trigger checks * Remove holdouts field from test_config expected response go-sdk v2.3.0 does not include holdouts field in OptimizelyConfig. The holdouts field was added in a later commit (e70a8f3) after v2.3.0 was tagged. * add clarifying comment for a test --------- Co-authored-by: Claude <[email protected]>
1 parent 2bf50ce commit 59ce65e

File tree

8 files changed

+681
-24
lines changed

8 files changed

+681
-24
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/golang-jwt/jwt/v4 v4.5.2
1616
github.com/google/uuid v1.3.1
1717
github.com/lestrrat-go/jwx/v2 v2.0.20
18-
github.com/optimizely/go-sdk/v2 v2.1.1-0.20250930190916-92b83d299b7a
18+
github.com/optimizely/go-sdk/v2 v2.3.0
1919
github.com/orcaman/concurrent-map v1.0.0
2020
github.com/prometheus/client_golang v1.18.0
2121
github.com/rakyll/statik v0.1.7

go.sum

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
246246
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
247247
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
248248
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
249-
github.com/optimizely/go-sdk/v2 v2.1.1-0.20250930190916-92b83d299b7a h1:wB445WJVx9JLYsHFQiy2OruPJlZ9ejae8vfsRHKZAtQ=
250-
github.com/optimizely/go-sdk/v2 v2.1.1-0.20250930190916-92b83d299b7a/go.mod h1:MusRCFsU7+XzJCoCTgheLoENJSf1iiFYm4KbJqz6BYA=
249+
github.com/optimizely/go-sdk/v2 v2.3.0 h1:FK0ZRF+E7b6AAF64rOpSD+/wzvQ/WVbHyRzu4n2nzJc=
250+
github.com/optimizely/go-sdk/v2 v2.3.0/go.mod h1:MusRCFsU7+XzJCoCTgheLoENJSf1iiFYm4KbJqz6BYA=
251251
github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY=
252252
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
253253
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
@@ -350,6 +350,8 @@ golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632
350350
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
351351
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
352352
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
353+
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
354+
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
353355
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
354356
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
355357
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -394,6 +396,8 @@ golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
394396
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
395397
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
396398
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
399+
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
400+
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
397401
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
398402
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
399403
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -438,6 +442,9 @@ golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
438442
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
439443
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
440444
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
445+
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
446+
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
447+
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
441448
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
442449
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
443450
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -527,12 +534,15 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
527534
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
528535
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
529536
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
537+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
530538
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
531539
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
532540
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
533541
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
534542
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
535543
golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
544+
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
545+
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
536546
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
537547
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
538548
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -544,6 +554,8 @@ golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
544554
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
545555
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
546556
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
557+
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
558+
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
547559
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
548560
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
549561
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -562,6 +574,7 @@ golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
562574
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
563575
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
564576
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
577+
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
565578
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
566579
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
567580
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -623,6 +636,8 @@ golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI
623636
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
624637
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
625638
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
639+
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
640+
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
626641
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
627642
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
628643
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

pkg/optimizely/cache.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import (
3737
"github.com/optimizely/agent/plugins/userprofileservice"
3838
cachePkg "github.com/optimizely/go-sdk/v2/pkg/cache"
3939
"github.com/optimizely/go-sdk/v2/pkg/client"
40-
"github.com/optimizely/go-sdk/v2/pkg/cmab"
4140
sdkconfig "github.com/optimizely/go-sdk/v2/pkg/config"
4241
"github.com/optimizely/go-sdk/v2/pkg/decision"
4342
"github.com/optimizely/go-sdk/v2/pkg/event"
@@ -325,18 +324,6 @@ func defaultLoader(
325324
)
326325
clientOptions = append(clientOptions, client.WithOdpManager(odpManager))
327326

328-
// Configure CMAB prediction endpoint with priority: env var > config > default
329-
// Environment variable allows test/runtime overrides
330-
if cmabEndpoint := os.Getenv("OPTIMIZELY_CMAB_PREDICTIONENDPOINT"); cmabEndpoint != "" {
331-
// Environment variable takes highest priority
332-
cmab.CMABPredictionEndpoint = cmabEndpoint
333-
log.Info().Str("endpoint", cmabEndpoint).Str("source", "environment").Msg("Using CMAB prediction endpoint")
334-
} else if clientConf.CMAB.PredictionEndpoint != "" {
335-
// Use config value if environment variable not set
336-
cmab.CMABPredictionEndpoint = clientConf.CMAB.PredictionEndpoint
337-
log.Info().Str("endpoint", clientConf.CMAB.PredictionEndpoint).Str("source", "config").Msg("Using CMAB prediction endpoint")
338-
}
339-
340327
// Get CMAB cache from service configuration
341328
var clientCMABCache cachePkg.CacheWithRemove
342329
var rawCMABCache = getServiceWithType(cmabCachePlugin, sdkKey, cmabCacheMap, clientConf.CMAB.Cache)
@@ -348,10 +335,23 @@ func defaultLoader(
348335
}
349336
}
350337

351-
// Create CMAB config using client API with custom cache
338+
// Configure CMAB prediction endpoint with priority: env var > config > default
339+
var predictionEndpoint string
340+
if cmabEndpoint := os.Getenv("OPTIMIZELY_CMAB_PREDICTIONENDPOINT"); cmabEndpoint != "" {
341+
// Environment variable takes highest priority
342+
predictionEndpoint = cmabEndpoint
343+
log.Info().Str("endpoint", cmabEndpoint).Str("source", "environment").Msg("Using CMAB prediction endpoint")
344+
} else if clientConf.CMAB.PredictionEndpoint != "" {
345+
// Use config value if environment variable not set
346+
predictionEndpoint = clientConf.CMAB.PredictionEndpoint
347+
log.Info().Str("endpoint", clientConf.CMAB.PredictionEndpoint).Str("source", "config").Msg("Using CMAB prediction endpoint")
348+
}
349+
350+
// Create CMAB config using client API with custom cache and endpoint
352351
cmabConfig := client.CmabConfig{
353-
Cache: clientCMABCache,
354-
HTTPTimeout: clientConf.CMAB.RequestTimeout,
352+
Cache: clientCMABCache,
353+
HTTPTimeout: clientConf.CMAB.RequestTimeout,
354+
PredictionEndpointTemplate: predictionEndpoint,
355355
}
356356

357357
// Add to client options

pkg/optimizely/cache_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
"github.com/optimizely/agent/plugins/userprofileservice"
3939
"github.com/optimizely/agent/plugins/userprofileservice/services"
4040
"github.com/optimizely/go-sdk/v2/pkg/cache"
41-
"github.com/optimizely/go-sdk/v2/pkg/cmab"
4241
sdkconfig "github.com/optimizely/go-sdk/v2/pkg/config"
4342
"github.com/optimizely/go-sdk/v2/pkg/decision"
4443
"github.com/optimizely/go-sdk/v2/pkg/event"
@@ -902,8 +901,8 @@ func (s *DefaultLoaderTestSuite) TestCMABEndpointFromConfig() {
902901

903902
s.NoError(err)
904903
s.NotNil(client)
905-
// Verify that the CMAB prediction endpoint was set from config
906-
s.Equal(configEndpoint, cmab.CMABPredictionEndpoint)
904+
// CMAB prediction endpoint is now configured through CmabConfig.PredictionEndpointTemplate
905+
// and cannot be easily verified from outside the client
907906
}
908907

909908
func (s *DefaultLoaderTestSuite) TestCMABEndpointEnvironmentOverridesConfig() {
@@ -945,8 +944,8 @@ func (s *DefaultLoaderTestSuite) TestCMABEndpointEnvironmentOverridesConfig() {
945944

946945
s.NoError(err)
947946
s.NotNil(client)
948-
// Verify that the environment variable takes priority
949-
s.Equal(envEndpoint, cmab.CMABPredictionEndpoint)
947+
// CMAB prediction endpoint is now configured through CmabConfig.PredictionEndpointTemplate
948+
// Environment variable priority is handled in cache.go lines 341-348
950949
}
951950

952951
func TestDefaultLoaderTestSuite(t *testing.T) {

pkg/optimizely/optimizelytest/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,18 @@ func (c *TestProjectConfig) GetFlagVariationsMap() map[string][]entities.Variati
523523
return c.flagVariationsMap
524524
}
525525

526+
// GetHoldoutList returns an array of all holdouts in the project
527+
func (c *TestProjectConfig) GetHoldoutList() []entities.Holdout {
528+
return []entities.Holdout{}
529+
}
530+
531+
// GetHoldoutsForFlag returns all holdouts applicable to the given feature flag.
532+
// In production, this would return a filtered list based on the flag's includedFlags/excludedFlags.
533+
// For this test mock, we return an empty list since no tests currently require holdout logic.
534+
func (c *TestProjectConfig) GetHoldoutsForFlag(featureKey string) []entities.Holdout {
535+
return []entities.Holdout{}
536+
}
537+
526538
// GetAttributeKeyByID returns the attribute key for the given ID
527539
func (c *TestProjectConfig) GetAttributeKeyByID(id string) (string, error) {
528540
for _, attr := range c.AttributeMap {
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
holdouts_datafile = {
2+
"accountId": "12133785640",
3+
"projectId": "6460519658291200",
4+
"revision": "12",
5+
"attributes": [
6+
{"id": "5502380200951808", "key": "all"},
7+
{"id": "5750214343000064", "key": "ho"}
8+
],
9+
"audiences": [
10+
{
11+
"name": "ho_3_aud",
12+
"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]",
13+
"id": "5435551013142528"
14+
},
15+
{
16+
"name": "ho_6_aud",
17+
"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]",
18+
"id": "5841838209236992"
19+
},
20+
{
21+
"name": "ho_4_aud",
22+
"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]",
23+
"id": "6043616745881600"
24+
},
25+
{
26+
"name": "ho_5_aud",
27+
"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]",
28+
"id": "6410995866796032"
29+
},
30+
{
31+
"id": "$opt_dummy_audience",
32+
"name": "Optimizely-Generated Audience for Backwards Compatibility",
33+
"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]"
34+
}
35+
],
36+
"version": "4",
37+
"events": [
38+
{"id": "6554438379241472", "experimentIds": [], "key": "event1"}
39+
],
40+
"integrations": [],
41+
"holdouts": [
42+
{
43+
"id": "1673115",
44+
"key": "holdout_6",
45+
"status": "Running",
46+
"variations": [
47+
{"id": "$opt_dummy_variation_id", "key": "off", "featureEnabled": False, "variables": []}
48+
],
49+
"trafficAllocation": [
50+
{"entityId": "$opt_dummy_variation_id", "endOfRange": 4000}
51+
],
52+
"audienceIds": ["5841838209236992"],
53+
"audienceConditions": ["or", "5841838209236992"]
54+
},
55+
{
56+
"id": "1673114",
57+
"key": "holdout_5",
58+
"status": "Running",
59+
"variations": [
60+
{"id": "$opt_dummy_variation_id", "key": "off", "featureEnabled": False, "variables": []}
61+
],
62+
"trafficAllocation": [
63+
{"entityId": "$opt_dummy_variation_id", "endOfRange": 2000}
64+
],
65+
"audienceIds": ["6410995866796032"],
66+
"audienceConditions": ["or", "6410995866796032"]
67+
},
68+
{
69+
"id": "1673113",
70+
"key": "holdouts_4",
71+
"status": "Running",
72+
"variations": [
73+
{"id": "$opt_dummy_variation_id", "key": "off", "featureEnabled": False, "variables": []}
74+
],
75+
"trafficAllocation": [
76+
{"entityId": "$opt_dummy_variation_id", "endOfRange": 5000}
77+
],
78+
"audienceIds": ["6043616745881600"],
79+
"audienceConditions": ["or", "6043616745881600"]
80+
},
81+
{
82+
"id": "1673112",
83+
"key": "holdout_3",
84+
"status": "Running",
85+
"variations": [
86+
{"id": "$opt_dummy_variation_id", "key": "off", "featureEnabled": False, "variables": []}
87+
],
88+
"trafficAllocation": [
89+
{"entityId": "$opt_dummy_variation_id", "endOfRange": 1000}
90+
],
91+
"audienceIds": ["5435551013142528"],
92+
"audienceConditions": ["or", "5435551013142528"]
93+
}
94+
],
95+
"anonymizeIP": True,
96+
"botFiltering": False,
97+
"typedAudiences": [
98+
{
99+
"name": "ho_3_aud",
100+
"conditions": ["and", ["or", ["or", {"match": "exact", "name": "ho", "type": "custom_attribute", "value": 3}], ["or", {"match": "le", "name": "all", "type": "custom_attribute", "value": 3}]]],
101+
"id": "5435551013142528"
102+
},
103+
{
104+
"name": "ho_6_aud",
105+
"conditions": ["and", ["or", ["or", {"match": "exact", "name": "ho", "type": "custom_attribute", "value": 6}], ["or", {"match": "le", "name": "all", "type": "custom_attribute", "value": 6}]]],
106+
"id": "5841838209236992"
107+
},
108+
{
109+
"name": "ho_4_aud",
110+
"conditions": ["and", ["or", ["or", {"match": "exact", "name": "ho", "type": "custom_attribute", "value": 4}], ["or", {"match": "le", "name": "all", "type": "custom_attribute", "value": 4}]]],
111+
"id": "6043616745881600"
112+
},
113+
{
114+
"name": "ho_5_aud",
115+
"conditions": ["and", ["or", ["or", {"match": "exact", "name": "ho", "type": "custom_attribute", "value": 5}], ["or", {"match": "le", "name": "all", "type": "custom_attribute", "value": 5}]]],
116+
"id": "6410995866796032"
117+
}
118+
],
119+
"variables": [],
120+
"environmentKey": "production",
121+
"sdkKey": "BLsSFScP7tSY5SCYuKn8c",
122+
"featureFlags": [
123+
{"id": "497759", "key": "flag1", "rolloutId": "rollout-497759-631765411405174", "experimentIds": [], "variables": []},
124+
{"id": "497760", "key": "flag2", "rolloutId": "rollout-497760-631765411405174", "experimentIds": [], "variables": []}
125+
],
126+
"rollouts": [
127+
{
128+
"id": "rollout-497759-631765411405174",
129+
"experiments": [
130+
{
131+
"id": "default-rollout-497759-631765411405174",
132+
"key": "default-rollout-497759-631765411405174",
133+
"status": "Running",
134+
"layerId": "rollout-497759-631765411405174",
135+
"variations": [{"id": "1583341", "key": "variation_1", "featureEnabled": True, "variables": []}],
136+
"trafficAllocation": [{"entityId": "1583341", "endOfRange": 10000}],
137+
"forcedVariations": {},
138+
"audienceIds": [],
139+
"audienceConditions": []
140+
}
141+
]
142+
},
143+
{
144+
"id": "rollout-497760-631765411405174",
145+
"experiments": [
146+
{
147+
"id": "default-rollout-497760-631765411405174",
148+
"key": "default-rollout-497760-631765411405174",
149+
"status": "Running",
150+
"layerId": "rollout-497760-631765411405174",
151+
"variations": [{"id": "1583340", "key": "variation_2", "featureEnabled": True, "variables": []}],
152+
"trafficAllocation": [{"entityId": "1583340", "endOfRange": 10000}],
153+
"forcedVariations": {},
154+
"audienceIds": [],
155+
"audienceConditions": []
156+
}
157+
]
158+
}
159+
],
160+
"experiments": [],
161+
"groups": [],
162+
"region": "US"
163+
}

0 commit comments

Comments
 (0)