|
370 | 370 |
|
371 | 371 | expect(OpenFeature::SDK.provider).to be_nil |
372 | 372 | end |
| 373 | + |
| 374 | + specify "After shutdown, hooks are cleared." do |
| 375 | + hook = Class.new { include OpenFeature::SDK::Hooks::Hook }.new |
| 376 | + OpenFeature::SDK.add_hooks(hook) |
| 377 | + expect(OpenFeature::SDK.hooks).not_to be_empty |
| 378 | + |
| 379 | + OpenFeature::SDK.shutdown |
| 380 | + |
| 381 | + expect(OpenFeature::SDK.hooks).to be_empty |
| 382 | + end |
| 383 | + |
| 384 | + specify "After shutdown, evaluation context is cleared." do |
| 385 | + OpenFeature::SDK.configure do |config| |
| 386 | + config.evaluation_context = OpenFeature::SDK::EvaluationContext.new(targeting_key: "user-1") |
| 387 | + end |
| 388 | + expect(OpenFeature::SDK.evaluation_context).not_to be_nil |
| 389 | + |
| 390 | + OpenFeature::SDK.shutdown |
| 391 | + |
| 392 | + expect(OpenFeature::SDK.evaluation_context).to be_nil |
| 393 | + end |
| 394 | + |
| 395 | + specify "After shutdown, transaction context propagator is cleared." do |
| 396 | + propagator = OpenFeature::SDK::ThreadLocalTransactionContextPropagator.new |
| 397 | + OpenFeature::SDK.set_transaction_context_propagator(propagator) |
| 398 | + expect(OpenFeature::SDK.configuration.transaction_context_propagator).not_to be_nil |
| 399 | + |
| 400 | + OpenFeature::SDK.shutdown |
| 401 | + |
| 402 | + expect(OpenFeature::SDK.configuration.transaction_context_propagator).to be_nil |
| 403 | + end |
| 404 | + end |
| 405 | + end |
| 406 | + |
| 407 | + context "1.4 - Flag Evaluation" do |
| 408 | + context "Requirement 1.4.10" do |
| 409 | + specify "client must not throw exceptions even when no hooks are registered and provider raises" do |
| 410 | + error_provider = Class.new do |
| 411 | + def metadata |
| 412 | + OpenFeature::SDK::Provider::ProviderMetadata.new(name: "Error Provider") |
| 413 | + end |
| 414 | + |
| 415 | + def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil) |
| 416 | + raise "provider internal error" |
| 417 | + end |
| 418 | + |
| 419 | + def fetch_string_value(flag_key:, default_value:, evaluation_context: nil) |
| 420 | + raise "provider internal error" |
| 421 | + end |
| 422 | + |
| 423 | + def fetch_number_value(flag_key:, default_value:, evaluation_context: nil) |
| 424 | + raise "provider internal error" |
| 425 | + end |
| 426 | + |
| 427 | + def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil) |
| 428 | + raise "provider internal error" |
| 429 | + end |
| 430 | + |
| 431 | + def fetch_float_value(flag_key:, default_value:, evaluation_context: nil) |
| 432 | + raise "provider internal error" |
| 433 | + end |
| 434 | + |
| 435 | + def fetch_object_value(flag_key:, default_value:, evaluation_context: nil) |
| 436 | + raise "provider internal error" |
| 437 | + end |
| 438 | + end.new |
| 439 | + |
| 440 | + OpenFeature::SDK.set_provider_and_wait(error_provider) |
| 441 | + client = OpenFeature::SDK.build_client |
| 442 | + |
| 443 | + # Ensure no hooks are registered (fast path) |
| 444 | + OpenFeature::SDK.hooks.clear |
| 445 | + |
| 446 | + result = nil |
| 447 | + expect { |
| 448 | + result = client.fetch_boolean_details(flag_key: "flag", default_value: false) |
| 449 | + }.not_to raise_error |
| 450 | + |
| 451 | + expect(result.value).to eq(false) |
| 452 | + expect(result.error_code).to eq(OpenFeature::SDK::Provider::ErrorCode::GENERAL) |
| 453 | + expect(result.reason).to eq(OpenFeature::SDK::Provider::Reason::ERROR) |
| 454 | + end |
373 | 455 | end |
374 | 456 | end |
375 | 457 |
|
|
470 | 552 | end |
471 | 553 | end |
472 | 554 |
|
| 555 | + context "1.7 - Provider Lifecycle (shutdown)" do |
| 556 | + context "Requirement 1.7.9" do |
| 557 | + specify "shutdown must not emit PROVIDER_READY before transitioning to NOT_READY" do |
| 558 | + provider = OpenFeature::SDK::Provider::InMemoryProvider.new |
| 559 | + OpenFeature::SDK.set_provider_and_wait(provider) |
| 560 | + |
| 561 | + states_observed = [] |
| 562 | + handler = ->(event_details) { states_observed << :ready } |
| 563 | + |
| 564 | + OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, handler) |
| 565 | + |
| 566 | + # Clear any immediate handler invocation from registration |
| 567 | + states_observed.clear |
| 568 | + |
| 569 | + OpenFeature::SDK.shutdown |
| 570 | + |
| 571 | + expect(states_observed).not_to include(:ready) |
| 572 | + end |
| 573 | + end |
| 574 | + end |
| 575 | + |
| 576 | + context "1.1 - API Metadata" do |
| 577 | + context "Requirement 1.1.5" do |
| 578 | + specify "The API must provide a function for retrieving the metadata of the configured provider" do |
| 579 | + provider = OpenFeature::SDK::Provider::InMemoryProvider.new |
| 580 | + OpenFeature::SDK.set_provider_and_wait(provider) |
| 581 | + |
| 582 | + expect(OpenFeature::SDK).to respond_to(:provider_metadata) |
| 583 | + expect(OpenFeature::SDK.provider_metadata.name).to eq("In-memory Provider") |
| 584 | + end |
| 585 | + |
| 586 | + specify "provider_metadata accepts a domain parameter" do |
| 587 | + default_provider = OpenFeature::SDK::Provider::InMemoryProvider.new |
| 588 | + domain_provider = OpenFeature::SDK::Provider::NoOpProvider.new |
| 589 | + |
| 590 | + OpenFeature::SDK.set_provider_and_wait(default_provider) |
| 591 | + OpenFeature::SDK.set_provider_and_wait(domain_provider, domain: "my-domain") |
| 592 | + |
| 593 | + expect(OpenFeature::SDK.provider_metadata(domain: "my-domain").name).to eq("No-op Provider") |
| 594 | + end |
| 595 | + |
| 596 | + specify "provider_metadata falls back to default provider when domain has no binding" do |
| 597 | + default_provider = OpenFeature::SDK::Provider::InMemoryProvider.new |
| 598 | + OpenFeature::SDK.set_provider_and_wait(default_provider) |
| 599 | + |
| 600 | + expect(OpenFeature::SDK.provider_metadata(domain: "unknown").name).to eq("In-memory Provider") |
| 601 | + end |
| 602 | + |
| 603 | + specify "provider_metadata returns nil when no provider is configured" do |
| 604 | + OpenFeature::SDK.shutdown |
| 605 | + |
| 606 | + expect(OpenFeature::SDK.provider_metadata).to be_nil |
| 607 | + end |
| 608 | + end |
| 609 | + end |
| 610 | + |
473 | 611 | context "Logger Methods" do |
474 | 612 | specify "delegates logger getter to configuration" do |
475 | 613 | logger = double("Logger") |
|
0 commit comments