Module: DevDoc::Test::Lints::ExternalIoBoundary

Defined in:
lib/dev_doc/test/lints/external_io_boundary.rb

Overview

External-I/O boundary tripwire: HTTP-client code must live only in app/services/ adapters.

Rationale

External-I/O sequencing (requests, retries, provider fallback, pagination) is the one kind of orchestration whose home is app/services/ — see best_practices/backend/en/12_orchestration.md. DevDoc/Rails/NoPersistenceInService enforces the inward half of that boundary (adapters cannot persist or enqueue); this lint enforces the outward half: no HTTP client leaks into models, controllers, jobs, or views, where it would be unmockable, untracked, and invisible to the adapter conventions. A tripwire rather than a cop because it guards an invariant that currently holds — there is nothing for a cop's observed-violation justification gate to measure.

NOTE: Limitations:

  • Matching is textual (per line, comments stripped) over app/**/*.rb only — an HTTP call assembled via Object.const_get or made from lib/ is invisible.
  • Only the generic Ruby HTTP clients are in the default token list; add each provider SDK your project uses (e.g. Stripe) via the per-project override.

Usage

Include this module in a Minitest test class (Rails test env). To extend the token list or exempt a sanctioned path, redefine the constants on the test class:

class ExternalIoBoundaryTest < ActiveSupport::TestCase
include DevDoc::Test::Lints::ExternalIoBoundary
# HTTP_CLIENT_TOKENS =
#   (ExternalIoBoundaryChecker::DEFAULT_HTTP_CLIENT_TOKENS + %w[Stripe]).freeze
# ALLOWED_EXTERNAL_IO_PATHS = %w[app/middleware/proxy.rb].freeze
end

Constant Summary collapse

HTTP_CLIENT_TOKENS =

Defaults. Per-project override: redefine the constants on the test class that includes this module.

ExternalIoBoundaryChecker::DEFAULT_HTTP_CLIENT_TOKENS
ALLOWED_EXTERNAL_IO_PATHS =
[].freeze

Instance Method Summary collapse

Instance Method Details

#test_no_http_clients_outside_app_servicesObject



109
110
111
112
113
114
115
116
117
# File 'lib/dev_doc/test/lints/external_io_boundary.rb', line 109

def test_no_http_clients_outside_app_services
  offenders = ExternalIoBoundaryChecker.new(
    Rails.root,
    tokens: self.class::HTTP_CLIENT_TOKENS,
    allowed_paths: self.class::ALLOWED_EXTERNAL_IO_PATHS
  ).offenders

  assert offenders.empty?, external_io_boundary_message(offenders)
end