|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require "spec_helper" |
| 4 | + |
| 5 | +RSpec.describe "Tracking Specification" do |
| 6 | + before(:each) do |
| 7 | + OpenFeature::SDK::API.instance.send(:configuration).send(:reset) |
| 8 | + end |
| 9 | + |
| 10 | + context "6.1 - Tracking API" do |
| 11 | + context "Condition 6.1.1.1" do |
| 12 | + specify "The client MUST define a function for tracking with parameters: tracking event name (required), evaluation context (optional), and tracking event details (optional)" do |
| 13 | + provider = OpenFeature::SDK::Provider::NoOpProvider.new |
| 14 | + OpenFeature::SDK.set_provider(provider) |
| 15 | + client = OpenFeature::SDK.build_client |
| 16 | + |
| 17 | + expect(client).to respond_to(:track) |
| 18 | + |
| 19 | + # Verify the method accepts the required and optional parameters |
| 20 | + method = client.method(:track) |
| 21 | + params = method.parameters |
| 22 | + |
| 23 | + # First param is the required tracking event name |
| 24 | + expect(params).to include([:req, :tracking_event_name]) |
| 25 | + end |
| 26 | + end |
| 27 | + |
| 28 | + context "Requirement 6.1.3" do |
| 29 | + specify "The evaluation context passed to the provider's track function MUST be merged in the order: API → client → invocation" do |
| 30 | + captured_context = nil |
| 31 | + |
| 32 | + tracking_provider = Class.new do |
| 33 | + def track(event_name, evaluation_context:, tracking_event_details:) |
| 34 | + # Capture for assertion |
| 35 | + end |
| 36 | + |
| 37 | + def metadata |
| 38 | + OpenFeature::SDK::Provider::ProviderMetadata.new(name: "tracking-provider") |
| 39 | + end |
| 40 | + end.new |
| 41 | + |
| 42 | + allow(tracking_provider).to receive(:track) do |event_name, evaluation_context:, tracking_event_details:| |
| 43 | + captured_context = evaluation_context |
| 44 | + end |
| 45 | + |
| 46 | + OpenFeature::SDK.configure do |config| |
| 47 | + config.evaluation_context = OpenFeature::SDK::EvaluationContext.new(api_key: "api_value", shared: "api") |
| 48 | + end |
| 49 | + OpenFeature::SDK.set_provider(tracking_provider) |
| 50 | + |
| 51 | + client = OpenFeature::SDK.build_client( |
| 52 | + evaluation_context: OpenFeature::SDK::EvaluationContext.new(client_key: "client_value", shared: "client") |
| 53 | + ) |
| 54 | + |
| 55 | + invocation_context = OpenFeature::SDK::EvaluationContext.new( |
| 56 | + invocation_key: "invocation_value", |
| 57 | + shared: "invocation" |
| 58 | + ) |
| 59 | + |
| 60 | + client.track("checkout", evaluation_context: invocation_context) |
| 61 | + |
| 62 | + expect(captured_context.field("api_key")).to eq("api_value") |
| 63 | + expect(captured_context.field("client_key")).to eq("client_value") |
| 64 | + expect(captured_context.field("invocation_key")).to eq("invocation_value") |
| 65 | + # Invocation has highest precedence |
| 66 | + expect(captured_context.field("shared")).to eq("invocation") |
| 67 | + end |
| 68 | + end |
| 69 | + |
| 70 | + context "Requirement 6.1.4" do |
| 71 | + specify "If the provider does not implement tracking, the client's track function MUST perform no operation" do |
| 72 | + # NoOpProvider does not implement track |
| 73 | + provider = OpenFeature::SDK::Provider::NoOpProvider.new |
| 74 | + OpenFeature::SDK.set_provider(provider) |
| 75 | + client = OpenFeature::SDK.build_client |
| 76 | + |
| 77 | + expect { client.track("event-name") }.not_to raise_error |
| 78 | + end |
| 79 | + |
| 80 | + specify "If the provider implements tracking, the track function is called" do |
| 81 | + track_called = false |
| 82 | + |
| 83 | + tracking_provider = Class.new do |
| 84 | + define_method(:track) do |event_name, evaluation_context:, tracking_event_details:| |
| 85 | + track_called = true |
| 86 | + end |
| 87 | + end.new |
| 88 | + |
| 89 | + OpenFeature::SDK.set_provider(tracking_provider) |
| 90 | + client = OpenFeature::SDK.build_client |
| 91 | + |
| 92 | + client.track("purchase") |
| 93 | + |
| 94 | + expect(track_called).to be true |
| 95 | + end |
| 96 | + end |
| 97 | + end |
| 98 | + |
| 99 | + context "6.2 - Tracking Event Details" do |
| 100 | + context "Requirement 6.2.1" do |
| 101 | + specify "The tracking event details MUST define an optional numeric value" do |
| 102 | + details = OpenFeature::SDK::TrackingEventDetails.new(value: 99.99) |
| 103 | + expect(details.value).to eq(99.99) |
| 104 | + end |
| 105 | + |
| 106 | + specify "The value defaults to nil when not provided" do |
| 107 | + details = OpenFeature::SDK::TrackingEventDetails.new |
| 108 | + expect(details.value).to be_nil |
| 109 | + end |
| 110 | + |
| 111 | + specify "The value must be numeric if provided" do |
| 112 | + expect { OpenFeature::SDK::TrackingEventDetails.new(value: "not_a_number") }.to raise_error(ArgumentError) |
| 113 | + end |
| 114 | + end |
| 115 | + |
| 116 | + context "Requirement 6.2.2" do |
| 117 | + specify "Tracking event details MUST support custom fields with string keys" do |
| 118 | + details = OpenFeature::SDK::TrackingEventDetails.new( |
| 119 | + value: 42, |
| 120 | + item: "premium-plan", |
| 121 | + quantity: 1, |
| 122 | + enabled: true |
| 123 | + ) |
| 124 | + |
| 125 | + expect(details.fields["item"]).to eq("premium-plan") |
| 126 | + expect(details.fields["quantity"]).to eq(1) |
| 127 | + expect(details.fields["enabled"]).to be true |
| 128 | + end |
| 129 | + end |
| 130 | + |
| 131 | + specify "tracking event details are passed through to the provider" do |
| 132 | + captured_details = nil |
| 133 | + |
| 134 | + tracking_provider = Class.new do |
| 135 | + define_method(:track) do |event_name, evaluation_context:, tracking_event_details:| |
| 136 | + captured_details = tracking_event_details |
| 137 | + end |
| 138 | + end.new |
| 139 | + |
| 140 | + OpenFeature::SDK.set_provider(tracking_provider) |
| 141 | + client = OpenFeature::SDK.build_client |
| 142 | + |
| 143 | + details = OpenFeature::SDK::TrackingEventDetails.new(value: 19.99, plan: "enterprise") |
| 144 | + client.track("subscription", tracking_event_details: details) |
| 145 | + |
| 146 | + expect(captured_details).to be_a(OpenFeature::SDK::TrackingEventDetails) |
| 147 | + expect(captured_details.value).to eq(19.99) |
| 148 | + expect(captured_details.fields["plan"]).to eq("enterprise") |
| 149 | + end |
| 150 | + end |
| 151 | +end |
0 commit comments