@@ -764,6 +764,153 @@ func TestRequirement_1_4_13(t *testing.T) {
764
764
// The `client` SHOULD transform the `evaluation context` using the `provider's` `context transformer` function
765
765
// if one is defined, before passing the result of the transformation to the provider's flag resolution functions.
766
766
767
+ // TestRequirement_6_1 tests the 6.1.1 and 6.1.2 requirements by asserting that the returned client matches the interface
768
+ // defined by the 6.1.1 and 6.1.2 requirements
769
+
770
+ // Requirement_6_1_1
771
+ // The `client` MUST define a function for tracking the occurrence of a particular action or application state,
772
+ // with parameters `tracking event name` (string, required), `evaluation context` (optional) and `tracking event details` (optional),
773
+ // which returns nothing.
774
+
775
+ // Requirement_6_1_2
776
+ // The `client` MUST define a function for tracking the occurrence of a particular action or application state,
777
+ // with parameters `tracking event name` (string, required) and `tracking event details` (optional), which returns nothing.
778
+ func TestRequirement_6_1 (t * testing.T ) {
779
+ client := NewClient ("test-client" )
780
+
781
+ type requirements interface {
782
+ Track (ctx context.Context , trackingEventName string , evalCtx EvaluationContext , details TrackingEventDetails )
783
+ }
784
+
785
+ var clientI interface {} = client
786
+ if _ , ok := clientI .(requirements ); ! ok {
787
+ t .Error ("client returned by NewClient doesn't implement the 1.6.* requirements interface" )
788
+ }
789
+ }
790
+
791
+ // Requirement_6_1_3
792
+ // The evaluation context passed to the provider's track function MUST be merged in the order, with duplicate values being overwritten:
793
+ // - API (global; lowest precedence)
794
+ // - transaction
795
+ // - client
796
+ // - invocation (highest precedence)
797
+
798
+ // Requirement_6_1_4
799
+ // If the client's `track` function is called and the associated provider does not implement tracking, the client's `track` function MUST no-op.
800
+ // Allow backward compatible to non-Tracker Provider
801
+ func TestTrack (t * testing.T ) {
802
+ type inputCtx struct {
803
+ api EvaluationContext
804
+ txn EvaluationContext
805
+ client EvaluationContext
806
+ invocation EvaluationContext
807
+ }
808
+
809
+ type testcase struct {
810
+ inCtx inputCtx
811
+ eventName string
812
+ outCtx EvaluationContext
813
+ // allow asserting the input to provider
814
+ provider func (tc * testcase , ctrl * gomock.Controller ) FeatureProvider
815
+ }
816
+
817
+ // mockTrackingProvider is a feature provider that implements tracker contract.
818
+ type mockTrackingProvider struct {
819
+ * MockTracker
820
+ * MockFeatureProvider
821
+ }
822
+
823
+ tests := map [string ]* testcase {
824
+ "merging in correct order" : {
825
+ eventName : "example-event" ,
826
+ inCtx : inputCtx {
827
+ api : EvaluationContext {
828
+ attributes : map [string ]interface {}{
829
+ "1" : "api" ,
830
+ "2" : "api" ,
831
+ "3" : "api" ,
832
+ "4" : "api" ,
833
+ },
834
+ },
835
+ txn : EvaluationContext {
836
+ attributes : map [string ]interface {}{
837
+ "2" : "txn" ,
838
+ "3" : "txn" ,
839
+ "4" : "txn" ,
840
+ },
841
+ },
842
+ client : EvaluationContext {
843
+ attributes : map [string ]interface {}{
844
+ "3" : "client" ,
845
+ "4" : "client" ,
846
+ },
847
+ },
848
+ invocation : EvaluationContext {
849
+ attributes : map [string ]interface {}{
850
+ "4" : "invocation" ,
851
+ },
852
+ },
853
+ },
854
+ outCtx : EvaluationContext {
855
+ attributes : map [string ]interface {}{
856
+ "1" : "api" ,
857
+ "2" : "txn" ,
858
+ "3" : "client" ,
859
+ "4" : "invocation" ,
860
+ },
861
+ },
862
+ provider : func (tc * testcase , ctrl * gomock.Controller ) FeatureProvider {
863
+ provider := & mockTrackingProvider {
864
+ MockTracker : NewMockTracker (ctrl ),
865
+ MockFeatureProvider : NewMockFeatureProvider (ctrl ),
866
+ }
867
+
868
+ provider .MockFeatureProvider .EXPECT ().Metadata ().AnyTimes ()
869
+ // assert if Track is called once with evalCtx expected
870
+ provider .MockTracker .EXPECT ().Track (gomock .Any (), tc .eventName , tc .outCtx , TrackingEventDetails {}).Times (1 )
871
+
872
+ return provider
873
+ },
874
+ },
875
+ "do no-op if Provider do not implement Tracker" : {
876
+ inCtx : inputCtx {},
877
+ eventName : "example-event" ,
878
+ outCtx : EvaluationContext {},
879
+ provider : func (tc * testcase , ctrl * gomock.Controller ) FeatureProvider {
880
+ provider := NewMockFeatureProvider (ctrl )
881
+
882
+ provider .EXPECT ().Metadata ().AnyTimes ()
883
+
884
+ return provider
885
+ },
886
+ },
887
+ }
888
+
889
+ for name , test := range tests {
890
+ t .Run (name , func (t * testing.T ) {
891
+ // arrange
892
+ ctrl := gomock .NewController (t )
893
+ provider := test .provider (test , ctrl )
894
+ client := NewClient ("test-client" )
895
+
896
+ // use different api in this client to avoid racing when changing global context
897
+ client .api = newEvaluationAPI (newEventExecutor ())
898
+
899
+ client .api .SetEvaluationContext (test .inCtx .api )
900
+ _ = client .api .SetProviderAndWait (provider )
901
+
902
+ client .evaluationContext = test .inCtx .client
903
+ ctx := WithTransactionContext (context .Background (), test .inCtx .txn )
904
+
905
+ // action
906
+ client .Track (ctx , test .eventName , test .inCtx .invocation , TrackingEventDetails {})
907
+
908
+ // assert
909
+ ctrl .Finish ()
910
+ })
911
+ }
912
+ }
913
+
767
914
func TestFlattenContext (t * testing.T ) {
768
915
tests := map [string ]struct {
769
916
inCtx EvaluationContext
0 commit comments