Skip to content

Conversation

@jeremylenz
Copy link
Collaborator

@jeremylenz jeremylenz commented Jan 8, 2026

What are the changes introduced in this pull request?

  1. Evaluate ForemanRhCloud.with_iop_smart_proxy? in a lambda for ForemanContext app metadata registration. This ensures the UI responds immediately when switching between IoP and non-IoP.
  2. Register the foreman_rh_cloud Ping extension regardless of IoP. This ensures that the advisor and vulnerability nodes will immediately appear when IoP is turned on, and disappear when turned off.

AI slop below, but I'm gonna hand-write the manual testing steps.

IoP Metadata Runtime Evaluation:

Simplified URL Caching:

  • Removed complex IoP state tracking from base_url, cert_base_url, and legacy_insights_url
  • These methods now rely on env_or_on_premise_url which is already IoP-aware
  • Cleaner, more maintainable code that's easier to understand

Ping Extension Improvements:

  • Removed conditional ping extension registration (always register, return {} when no IoP)
  • Added nil safety to iop_smart_proxy_url with safe navigation operator
  • Early return in ping method when no IoP proxy exists

Considerations taken when implementing this change?

  • Performance: The lambda evaluation relies on ActiveRecord query cache, so performance is unchanged despite dynamic evaluation
  • Backward compatibility: Always registering ping/status extensions ensures consistent behavior
  • Foreman core dependency: This PR depends on the runtime Proc evaluation feature in Foreman core (PR #10814)
  • User impact: When an IoP smart proxy is created or destroyed, the plugin metadata and URLs will now correctly reflect the current state without requiring a server restart

What are the testing steps for this pull request?

Unit Tests:

  • 4 tests for plugin metadata lambda evaluation
  • 10 tests for URL method dynamic behavior with IoP mode changes
  • All 336 foreman_rh_cloud tests passing

Manual Testing:
Check out this PR and the Foreman PR --> theforeman/foreman#10814

  1. Start Foreman with IoP smart proxy

  2. Infrastructure > About: Backend system status should show "advisor" and "vulnerability"

  3. Main nav: Insights should have 'Recommendations' and 'Vulnerability' sub-items.

  4. Both pages should load Scalprum components (you can tell because you see the "loading..." text)

  5. Host details page should have Recommendations and Vulnerability tabs

  6. Recommendations tab should load the Scalprum component

  7. Now, go to Infrastructure > Smart Proxies and DELETE the IoP smart proxy

  8. The following changes should be IMMEDIATE:

  9. Infrastructure > About: Backend system status will NOT have "advisor" and "vulnerability"

  10. Main nav: Insights should have 'Recommendations' and 'Inventory Upload'

  11. Host details page should have only 'Recommendations', NOT 'Vulnerabilities'

  12. Should NOT load Scalprum components anywhere

Now, Infrastructure > Smart Proxies > Create a new smart proxy with url https://localhost:24443
Everything should immediately go back to how it was in steps 1-6.

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 8, 2026

Reviewer's Guide

Implements runtime evaluation for IoP plugin metadata, simplifies URL resolution to always use the IoP‑aware helper instead of cached values, and makes the ping extension always registered but safely no‑ops when IoP is unavailable, with new tests covering IoP metadata and URL behavior across proxy lifecycle changes.

Sequence diagram for IoP ping invocation with runtime IoP detection

sequenceDiagram
  actor Admin
  participant UI
  participant ForemanServer
  participant ForemanRhCloud_Ping as ForemanRhCloud_Ping
  participant ForemanRhCloud as ForemanRhCloud

  Admin->>UI: Trigger IoP ping (e.g. status check)
  UI->>ForemanServer: HTTP request /rh_cloud/ping
  ForemanServer->>ForemanRhCloud_Ping: ping
  ForemanRhCloud_Ping->>ForemanRhCloud: with_iop_smart_proxy?
  alt IoP smart proxy available
    ForemanRhCloud-->>ForemanRhCloud_Ping: true
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud: iop_smart_proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: smart_proxy
    ForemanRhCloud_Ping->>ForemanRhCloud_Ping: iop_smart_proxy_url
    ForemanRhCloud_Ping-->>ForemanRhCloud_Ping: cache or ForemanRhCloud.iop_smart_proxy&.url
    ForemanRhCloud_Ping->>ForemanRhCloud_Ping: ping_services
    ForemanRhCloud_Ping-->>ForemanServer: IoP ping result
  else No IoP smart proxy
    ForemanRhCloud-->>ForemanRhCloud_Ping: false
    ForemanRhCloud_Ping-->>ForemanServer: {}
  end
  ForemanServer-->>UI: HTTP response
  UI-->>Admin: Show IoP status
Loading

Sequence diagram for runtime evaluation of IoP plugin metadata

sequenceDiagram
  participant ForemanCore as Foreman_Core
  participant MetadataRegistry as AppMetadataRegistry
  participant ForemanRhCloudPlugin as ForemanRhCloud_Plugin
  participant ForemanRhCloud as ForemanRhCloud

  ForemanRhCloudPlugin->>MetadataRegistry: register foreman_rh_cloud iop: lambda
  MetadataRegistry-->>ForemanRhCloudPlugin: registration complete

  loop On each metadata request
    ForemanCore->>MetadataRegistry: get foreman_rh_cloud metadata
    MetadataRegistry->>ForemanRhCloud: with_iop_smart_proxy? via lambda
    ForemanRhCloud-->>MetadataRegistry: current IoP availability
    MetadataRegistry-->>ForemanCore: metadata iop value reflects current IoP state
  end
Loading

Updated class diagram for ForemanRhCloud URL resolution and Ping behavior

classDiagram
  class ForemanRhCloud {
    +env_or_on_premise_url(env_var_name)
    +base_url()
    +cert_base_url()
    +legacy_insights_url()
    +verify_ssl_method()
    +with_iop_smart_proxy?()
    +iop_smart_proxy()
  }

  class ForemanRhCloud_Plugin {
    +register()
  }

  class AppMetadataRegistry {
    +register(key, metadata_hash)
  }

  class ForemanRhCloud_Ping {
    +iop_smart_proxy_url()
    +service_urls()
    +ping()
    +status()
    +ping_services()
    +exception_watch(result, blk)
  }

  ForemanRhCloud_Plugin --> AppMetadataRegistry : registers_metadata
  ForemanRhCloud_Plugin --> ForemanRhCloud_Ping : register_ping_extension
  ForemanRhCloud_Ping --> ForemanRhCloud : uses_with_iop_smart_proxy?
  ForemanRhCloud_Ping --> ForemanRhCloud : uses_iop_smart_proxy
  AppMetadataRegistry --> ForemanRhCloud : calls_with_iop_smart_proxy?_lambda
  ForemanRhCloud_Ping --> ForemanRhCloud : uses_urls
  ForemanRhCloud ..> ForemanRhCloud_Ping : shared_namespace
Loading

File-Level Changes

Change Details Files
Make IoP plugin metadata evaluated at runtime instead of at plugin registration time.
  • Change IoP metadata entry to store a lambda that calls the IoP smart proxy detection method when evaluated
  • Ensure metadata now reflects IoP smart proxy creation and destruction without requiring server restart
  • Add unit tests verifying metadata value changes as IoP proxies are created and destroyed
lib/foreman_rh_cloud/plugin.rb
test/unit/foreman_rh_cloud_iop_metadata_test.rb
Simplify and de-cache URL helpers to always use the IoP-aware URL resolution.
  • Remove memoization from base_url, cert_base_url, and legacy_insights_url so they call the IoP-aware helper on each invocation
  • Rely on env_or_on_premise_url to pick IoP, environment override, or default SaaS URL dynamically
  • Add tests for URL behavior across IoP presence, ENV overrides, and proxy lifecycle changes
lib/foreman_rh_cloud.rb
test/unit/foreman_rh_cloud_iop_metadata_test.rb
Always register ping/status extensions but make them safe when no IoP smart proxy exists.
  • Register ping and status extensions unconditionally during plugin registration
  • Guard ping execution with a runtime IoP smart proxy presence check and return an empty hash when IoP is absent
  • Make iop_smart_proxy_url nil-safe using safe navigation to avoid errors when no IoP proxy exists
lib/foreman_rh_cloud/plugin.rb
app/models/foreman_rh_cloud/ping.rb

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • Consider dropping the memoization on iop_smart_proxy_url (or adding a reset hook) so it stays consistent with the new runtime IoP detection—right now it can cache a nil or stale URL even though other IoP-related URLs are evaluated dynamically on each call.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider dropping the memoization on `iop_smart_proxy_url` (or adding a reset hook) so it stays consistent with the new runtime IoP detection—right now it can cache a `nil` or stale URL even though other IoP-related URLs are evaluated dynamically on each call.

## Individual Comments

### Comment 1
<location> `test/unit/foreman_rh_cloud_iop_metadata_test.rb:79-88` </location>
<code_context>
+  describe 'URL methods' do
</code_context>

<issue_to_address>
**suggestion (testing):** Add similar ENV and IoP precedence tests for cert_base_url and legacy_insights_url

Current precedence tests for `SATELLITE_RH_CLOUD_URL` / IoP and `base_url` look good, but there are no analogous tests for `cert_base_url` (`SATELLITE_CERT_RH_CLOUD_URL`) and `legacy_insights_url` (`SATELLITE_LEGACY_INSIGHTS_URL`). Please add tests that:

- Verify `cert_base_url` uses `SATELLITE_CERT_RH_CLOUD_URL` when set and no IoP proxy exists.
- Verify `legacy_insights_url` uses `SATELLITE_LEGACY_INSIGHTS_URL` when set and no IoP proxy exists.
- Verify that when both the ENV var and an IoP proxy are present, the IoP URL takes precedence for both helpers, matching the `base_url` behavior.

This keeps all three URL helpers consistent and better protected against regressions.

Suggested implementation:

```ruby
  describe 'URL methods' do
    test 'base_url returns cloud URL when no IoP smart proxy exists' do
      assert_equal 'https://cloud.redhat.com', ForemanRhCloud.base_url
    end

    test 'cert_base_url returns cloud URL when no IoP smart proxy exists' do
      assert_equal 'https://cert.cloud.redhat.com', ForemanRhCloud.cert_base_url
    end

    test 'legacy_insights_url returns cloud URL when no IoP smart proxy exists' do
      assert_equal 'https://cert-api.access.redhat.com', ForemanRhCloud.legacy_insights_url
    end

    test 'cert_base_url prefers SATELLITE_CERT_RH_CLOUD_URL when set and no IoP smart proxy exists' do
      old_env = ENV['SATELLITE_CERT_RH_CLOUD_URL']
      ENV['SATELLITE_CERT_RH_CLOUD_URL'] = 'https://env-cert.cloud.test'
      begin
        assert_equal 'https://env-cert.cloud.test', ForemanRhCloud.cert_base_url
      ensure
        ENV['SATELLITE_CERT_RH_CLOUD_URL'] = old_env
      end
    end

    test 'legacy_insights_url prefers SATELLITE_LEGACY_INSIGHTS_URL when set and no IoP smart proxy exists' do
      old_env = ENV['SATELLITE_LEGACY_INSIGHTS_URL']
      ENV['SATELLITE_LEGACY_INSIGHTS_URL'] = 'https://env-legacy.insights.test'
      begin
        assert_equal 'https://env-legacy.insights.test', ForemanRhCloud.legacy_insights_url
      ensure
        ENV['SATELLITE_LEGACY_INSIGHTS_URL'] = old_env
      end
    end

    test 'cert_base_url prefers IoP proxy URL over SATELLITE_CERT_RH_CLOUD_URL when both are present' do
      old_env = ENV['SATELLITE_CERT_RH_CLOUD_URL']
      ENV['SATELLITE_CERT_RH_CLOUD_URL'] = 'https://env-cert.cloud.test'

      ForemanRhCloud.stubs(:iop_proxy_url).returns('https://iop-cert.cloud.test')

      begin
        assert_equal 'https://iop-cert.cloud.test', ForemanRhCloud.cert_base_url
      ensure
        ENV['SATELLITE_CERT_RH_CLOUD_URL'] = old_env
        ForemanRhCloud.unstub(:iop_proxy_url)
      end
    end

    test 'legacy_insights_url prefers IoP proxy URL over SATELLITE_LEGACY_INSIGHTS_URL when both are present' do
      old_env = ENV['SATELLITE_LEGACY_INSIGHTS_URL']
      ENV['SATELLITE_LEGACY_INSIGHTS_URL'] = 'https://env-legacy.insights.test'

      ForemanRhCloud.stubs(:iop_proxy_url).returns('https://iop-legacy.insights.test')

      begin
        assert_equal 'https://iop-legacy.insights.test', ForemanRhCloud.legacy_insights_url
      ensure
        ENV['SATELLITE_LEGACY_INSIGHTS_URL'] = old_env
        ForemanRhCloud.unstub(:iop_proxy_url)
      end
    end

```

These tests assume:
1. The application reads the environment variables `SATELLITE_CERT_RH_CLOUD_URL` and `SATELLITE_LEGACY_INSIGHTS_URL` directly via `ENV[...]`.
2. `ForemanRhCloud.iop_proxy_url` is the method used by `cert_base_url` and `legacy_insights_url` to discover the IoP proxy URL, and Mocha is available for `stubs`/`unstub`.
If the existing `base_url` precedence tests use a different helper (e.g. a factory for an IoP proxy or a wrapper for environment handling), mirror that pattern instead of stubbing `iop_proxy_url`, to keep the style consistent across all three URL helpers.
</issue_to_address>

### Comment 2
<location> `test/unit/foreman_rh_cloud_iop_metadata_test.rb:129-142` </location>
<code_context>
+      ENV.delete('SATELLITE_RH_CLOUD_URL')
+    end
+
+    test 'URL methods update when IoP smart proxy is created' do
+      # Initially returns cloud URL
+      assert_equal 'https://cloud.redhat.com', ForemanRhCloud.base_url
+
+      # Create an IoP smart proxy
+      create_iop_proxy
+
+      # Should now return IoP URL
+      assert_equal 'https://iop.example.com', ForemanRhCloud.base_url
+    end
+
+    test 'URL methods update when IoP smart proxy is destroyed' do
+      # Create an IoP smart proxy
+      proxy = create_iop_proxy
</code_context>

<issue_to_address>
**suggestion (testing):** Extend dynamic IoP creation/destruction tests to cert_base_url and legacy_insights_url

Since `cert_base_url` and `legacy_insights_url` now also use `env_or_on_premise_url` and should reflect IoP availability, these tests should assert their behavior alongside `base_url`.

In the "created" test, also assert initial cloud values for all three helpers, then the IoP URL for all three after proxy creation. In the "destroyed" test, assert the IoP URL for all three before destruction, then the cloud URLs again after the proxy is destroyed. This keeps the tests consistent with the PR’s stated behavior for all URL helpers.

```suggestion
    test 'URL methods update when IoP smart proxy is created' do
      # Initially returns cloud URLs
      assert_equal 'https://cloud.redhat.com', ForemanRhCloud.base_url
      assert_equal 'https://cert.cloud.redhat.com', ForemanRhCloud.cert_base_url
      assert_equal 'https://cert-api.access.redhat.com', ForemanRhCloud.legacy_insights_url

      # Create an IoP smart proxy
      create_iop_proxy

      # Should now return IoP URL for all helpers
      assert_equal 'https://iop.example.com', ForemanRhCloud.base_url
      assert_equal 'https://iop.example.com', ForemanRhCloud.cert_base_url
      assert_equal 'https://iop.example.com', ForemanRhCloud.legacy_insights_url
    end

    test 'URL methods update when IoP smart proxy is destroyed' do
      # Create an IoP smart proxy
      proxy = create_iop_proxy

      # Initially returns IoP URL for all helpers
      assert_equal 'https://iop.example.com', ForemanRhCloud.base_url
      assert_equal 'https://iop.example.com', ForemanRhCloud.cert_base_url
      assert_equal 'https://iop.example.com', ForemanRhCloud.legacy_insights_url

      # Destroy the IoP smart proxy
      proxy.destroy

      # Should now return cloud URLs again
      assert_equal 'https://cloud.redhat.com', ForemanRhCloud.base_url
      assert_equal 'https://cert.cloud.redhat.com', ForemanRhCloud.cert_base_url
      assert_equal 'https://cert-api.access.redhat.com', ForemanRhCloud.legacy_insights_url
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

jeremylenz and others added 2 commits January 9, 2026 15:08
- Wrap IoP metadata in lambda for runtime evaluation to prevent stale
  state during app initialization
- Remove URL instance variable caching as env_or_on_premise_url is
  already IoP-aware
- Add nil safety to ping methods
- Add comprehensive unit tests for IoP metadata and URL methods

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Add ENV variable precedence tests for cert_base_url and legacy_insights_url
- Extend dynamic IoP creation/destruction tests to verify all URL helpers
- Fix memoization in Ping class for iop_smart_proxy_url to ensure runtime IoP detection

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@jeremylenz jeremylenz force-pushed the fix-iop-metadata-runtime-evaluation branch from 6b9f600 to 164a07b Compare January 9, 2026 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant