nonnative 1.107.0 → 1.108.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +8 -6
  3. data/.rubocop.yml +3 -0
  4. data/AGENTS.md +248 -0
  5. data/CHANGELOG.md +104 -0
  6. data/Gemfile.lock +53 -51
  7. data/README.md +51 -32
  8. data/lib/nonnative/close_all_socket_pair.rb +14 -0
  9. data/lib/nonnative/configuration.rb +67 -0
  10. data/lib/nonnative/configuration_process.rb +14 -0
  11. data/lib/nonnative/configuration_proxy.rb +28 -0
  12. data/lib/nonnative/configuration_runner.rb +44 -0
  13. data/lib/nonnative/configuration_server.rb +12 -0
  14. data/lib/nonnative/configuration_service.rb +9 -0
  15. data/lib/nonnative/delay_socket_pair.rb +15 -0
  16. data/lib/nonnative/error.rb +7 -0
  17. data/lib/nonnative/fault_injection_proxy.rb +63 -0
  18. data/lib/nonnative/go_command.rb +34 -0
  19. data/lib/nonnative/grpc_server.rb +30 -0
  20. data/lib/nonnative/header.rb +44 -0
  21. data/lib/nonnative/http_client.rb +45 -0
  22. data/lib/nonnative/http_proxy_server.rb +62 -1
  23. data/lib/nonnative/http_server.rb +40 -0
  24. data/lib/nonnative/invalid_data_socket_pair.rb +15 -0
  25. data/lib/nonnative/no_proxy.rb +35 -0
  26. data/lib/nonnative/not_found_error.rb +7 -0
  27. data/lib/nonnative/observability.rb +44 -0
  28. data/lib/nonnative/pool.rb +50 -0
  29. data/lib/nonnative/port.rb +26 -0
  30. data/lib/nonnative/process.rb +29 -0
  31. data/lib/nonnative/proxy.rb +24 -0
  32. data/lib/nonnative/proxy_factory.rb +16 -0
  33. data/lib/nonnative/runner.rb +30 -0
  34. data/lib/nonnative/server.rb +28 -0
  35. data/lib/nonnative/service.rb +16 -0
  36. data/lib/nonnative/socket_pair.rb +46 -0
  37. data/lib/nonnative/socket_pair_factory.rb +21 -0
  38. data/lib/nonnative/start_error.rb +5 -0
  39. data/lib/nonnative/stop_error.rb +5 -0
  40. data/lib/nonnative/timeout.rb +20 -0
  41. data/lib/nonnative/version.rb +4 -1
  42. data/lib/nonnative.rb +128 -0
  43. metadata +3 -2
@@ -1,13 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Runtime runner that manages an OS-level child process.
5
+ #
6
+ # A process runner:
7
+ # - starts the configured proxy (if any),
8
+ # - spawns a child process using the configured command and environment,
9
+ # - waits briefly (via the runner `wait`), and
10
+ # - participates in readiness/shutdown via TCP port checks orchestrated by {Nonnative::Pool}.
11
+ #
12
+ # The underlying configuration is a {Nonnative::ConfigurationProcess}.
13
+ #
14
+ # @see Nonnative::ConfigurationProcess
15
+ # @see Nonnative::Pool
4
16
  class Process < Runner
17
+ # @param service [Nonnative::ConfigurationProcess] process configuration
5
18
  def initialize(service)
6
19
  super
7
20
 
8
21
  @timeout = Nonnative::Timeout.new(service.timeout)
9
22
  end
10
23
 
24
+ # Starts the proxy (if any) and spawns the configured process if it is not already running.
25
+ #
26
+ # @return [Array<(Integer, Boolean)>]
27
+ # a tuple of:
28
+ # - the spawned process id (pid)
29
+ # - whether the process appears to still be running (non-blocking wait result)
11
30
  def start
12
31
  unless process_exists?
13
32
  proxy.start
@@ -18,6 +37,11 @@ module Nonnative
18
37
  [pid, ::Process.waitpid2(pid, ::Process::WNOHANG).nil?]
19
38
  end
20
39
 
40
+ # Stops the process (if running) and stops the proxy (if any).
41
+ #
42
+ # The process is signalled using the configured signal (defaults to `INT` when not set).
43
+ #
44
+ # @return [Integer, nil] the pid that was stopped (or `nil` if the process was never started)
21
45
  def stop
22
46
  if process_exists?
23
47
  process_kill
@@ -28,6 +52,11 @@ module Nonnative
28
52
  pid
29
53
  end
30
54
 
55
+ # Returns a memoized memory reader for the spawned process.
56
+ #
57
+ # This is primarily used by acceptance tests to assert memory usage.
58
+ #
59
+ # @return [GetProcessMem, nil] a memory reader for the pid, or `nil` if not started
31
60
  def memory
32
61
  return if pid.nil?
33
62
 
@@ -1,15 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Base class for proxy implementations.
5
+ #
6
+ # A proxy is responsible for interposing behavior between a client and a target service.
7
+ # Runners ({Nonnative::Process}, {Nonnative::Server}, and {Nonnative::Service}) create a proxy
8
+ # instance via {Nonnative::ProxyFactory} based on `service.proxy.kind`.
9
+ #
10
+ # Concrete proxies typically implement these public methods:
11
+ # - `start`: begin proxying (bind/listen, start threads, etc)
12
+ # - `stop`: stop proxying and release resources
13
+ # - `reset`: return proxy behavior to a healthy/default state
14
+ # - `host` / `port`: endpoint clients should connect to when the proxy is enabled
15
+ #
16
+ # @see Nonnative::ProxyFactory
17
+ # @see Nonnative::NoProxy
18
+ # @see Nonnative::FaultInjectionProxy
4
19
  class Proxy
20
+ # @param service [Nonnative::ConfigurationRunner] runner configuration with an attached proxy configuration
5
21
  def initialize(service)
6
22
  @service = service
7
23
  end
8
24
 
9
25
  protected
10
26
 
27
+ # Returns the underlying runner configuration.
28
+ #
29
+ # @return [Nonnative::ConfigurationRunner]
11
30
  attr_reader :service
12
31
 
32
+ # Sleeps for the proxy wait interval configured on `service.proxy.wait`.
33
+ #
34
+ # Proxies can use this to allow state transitions to take effect.
35
+ #
36
+ # @return [void]
13
37
  def wait
14
38
  sleep service.proxy.wait
15
39
  end
@@ -1,8 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Factory for creating proxy instances for runners.
5
+ #
6
+ # Each runtime runner ({Nonnative::Process}, {Nonnative::Server}, {Nonnative::Service}) constructs
7
+ # a proxy via this factory. The proxy implementation is selected by `service.proxy.kind` and resolved
8
+ # using {Nonnative.proxy}.
9
+ #
10
+ # If the kind is unknown (or `"none"`), {Nonnative.proxy} returns {Nonnative::NoProxy}.
11
+ #
12
+ # @see Nonnative.proxy
13
+ # @see Nonnative.proxies
14
+ # @see Nonnative::Proxy
15
+ # @see Nonnative::NoProxy
4
16
  class ProxyFactory
5
17
  class << self
18
+ # Creates a proxy instance for the given runner configuration.
19
+ #
20
+ # @param service [Nonnative::ConfigurationRunner] runner configuration with an attached proxy configuration
21
+ # @return [Nonnative::Proxy] proxy instance (may be a {Nonnative::NoProxy})
6
22
  def create(service)
7
23
  proxy = Nonnative.proxy(service.proxy.kind)
8
24
 
@@ -1,26 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Base runtime wrapper for a configured runnable unit.
5
+ #
6
+ # A runner wraps a configuration object (a subclass of {Nonnative::ConfigurationRunner}) and
7
+ # exposes lifecycle behavior via specialized subclasses:
8
+ #
9
+ # - {Nonnative::Process} for OS-level child processes
10
+ # - {Nonnative::Server} for in-process Ruby servers (threads)
11
+ # - {Nonnative::Service} for proxy-only external dependencies
12
+ #
13
+ # Each runner has an associated proxy instance created via {Nonnative::ProxyFactory}.
14
+ #
15
+ # @see Nonnative::Process
16
+ # @see Nonnative::Server
17
+ # @see Nonnative::Service
4
18
  class Runner
19
+ # Returns the proxy instance for this runner.
20
+ #
21
+ # @return [Nonnative::Proxy]
5
22
  attr_reader :proxy
6
23
 
24
+ # @param service [Nonnative::ConfigurationRunner] runner configuration
7
25
  def initialize(service)
8
26
  @service = service
9
27
  @proxy = Nonnative::ProxyFactory.create(service)
10
28
  end
11
29
 
30
+ # Returns the configured runner name.
31
+ #
32
+ # @return [String, nil]
12
33
  def name
13
34
  service.name
14
35
  end
15
36
 
16
37
  protected
17
38
 
39
+ # Returns the underlying configuration object.
40
+ #
41
+ # @return [Nonnative::ConfigurationRunner]
18
42
  attr_reader :service
19
43
 
44
+ # Sleeps for the configured `wait` interval after start-related work.
45
+ #
46
+ # @return [void]
20
47
  def wait_start
21
48
  sleep service.wait
22
49
  end
23
50
 
51
+ # Sleeps for the configured `wait` interval after stop-related work.
52
+ #
53
+ # @return [void]
24
54
  def wait_stop
25
55
  sleep service.wait
26
56
  end
@@ -1,13 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Runtime runner that manages an in-process Ruby server.
5
+ #
6
+ # A server runner:
7
+ # - starts the configured proxy (if any),
8
+ # - starts a Ruby thread that runs {#perform_start},
9
+ # - waits briefly (via the runner `wait`), and
10
+ # - participates in readiness/shutdown via TCP port checks orchestrated by {Nonnative::Pool}.
11
+ #
12
+ # Concrete server implementations are expected to subclass {Nonnative::Server} and implement:
13
+ # - {#perform_start} (to bind/listen and begin serving), and
14
+ # - {#perform_stop} (to gracefully shut down).
15
+ #
16
+ # The underlying configuration is a {Nonnative::ConfigurationServer}.
17
+ #
18
+ # @see Nonnative::ConfigurationServer
19
+ # @see Nonnative::Pool
4
20
  class Server < Runner
21
+ # @param service [Nonnative::ConfigurationServer] server configuration
5
22
  def initialize(service)
6
23
  super
7
24
 
8
25
  @timeout = Nonnative::Timeout.new(service.timeout)
9
26
  end
10
27
 
28
+ # Starts the proxy (if any) and starts the server thread if not already started.
29
+ #
30
+ # @return [Array<(Integer, TrueClass)>]
31
+ # a tuple of:
32
+ # - a stable identifier for this server instance (`object_id`)
33
+ # - `true` (thread creation itself is considered started; readiness is checked separately)
11
34
  def start
12
35
  unless thread
13
36
  proxy.start
@@ -21,6 +44,11 @@ module Nonnative
21
44
  [object_id, true]
22
45
  end
23
46
 
47
+ # Stops the server if it is running.
48
+ #
49
+ # Calls {#perform_stop}, terminates the server thread, stops the proxy (if any), and waits briefly.
50
+ #
51
+ # @return [Integer] the server identifier (`object_id`)
24
52
  def stop
25
53
  if thread
26
54
  perform_stop
@@ -1,13 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Runtime runner for an external dependency.
5
+ #
6
+ # A service runner does not manage an OS process or Ruby thread. It exists so Nonnative can manage
7
+ # a proxy lifecycle (start/stop/reset) for an external service that is managed elsewhere (for example
8
+ # a database running in Docker).
9
+ #
10
+ # The underlying configuration is a {Nonnative::ConfigurationService}.
11
+ #
12
+ # @see Nonnative::ConfigurationService
13
+ # @see Nonnative::Proxy
4
14
  class Service < Runner
15
+ # Starts the configured proxy (if any).
16
+ #
17
+ # @return [void]
5
18
  def start
6
19
  proxy.start
7
20
 
8
21
  Nonnative.logger.info "started service '#{service.name}'"
9
22
  end
10
23
 
24
+ # Stops the configured proxy (if any).
25
+ #
26
+ # @return [void]
11
27
  def stop
12
28
  proxy.stop
13
29
 
@@ -1,11 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Base socket-pair implementation used by TCP proxies.
5
+ #
6
+ # A socket-pair connects an accepted local socket to a remote upstream socket and forwards bytes
7
+ # in both directions until one side closes.
8
+ #
9
+ # This is used by {Nonnative::FaultInjectionProxy} to implement pass-through forwarding, and is
10
+ # subclassed to inject failures (close immediately, delay reads, corrupt writes, etc).
11
+ #
12
+ # The `proxy` argument is expected to provide `host` and `port` for the upstream connection
13
+ # (typically a {Nonnative::ConfigurationProxy}).
14
+ #
15
+ # @see Nonnative::FaultInjectionProxy
16
+ # @see Nonnative::SocketPairFactory
17
+ # @see Nonnative::CloseAllSocketPair
18
+ # @see Nonnative::DelaySocketPair
19
+ # @see Nonnative::InvalidDataSocketPair
4
20
  class SocketPair
21
+ # @param proxy [#host, #port, #options] proxy configuration used to connect upstream
5
22
  def initialize(proxy)
6
23
  @proxy = proxy
7
24
  end
8
25
 
26
+ # Connects the given local socket to an upstream socket and pipes data until the connection ends.
27
+ #
28
+ # @param local_socket [TCPSocket] the accepted client socket
29
+ # @return [void]
9
30
  def connect(local_socket)
10
31
  remote_socket = create_remote_socket
11
32
 
@@ -24,12 +45,24 @@ module Nonnative
24
45
 
25
46
  protected
26
47
 
48
+ # Returns the proxy configuration.
49
+ #
50
+ # @return [Object]
27
51
  attr_reader :proxy
28
52
 
53
+ # Creates the upstream socket connection.
54
+ #
55
+ # @return [TCPSocket]
29
56
  def create_remote_socket
30
57
  ::TCPSocket.new(proxy.host, proxy.port)
31
58
  end
32
59
 
60
+ # Pipes data from `socket1` to `socket2` if `socket1` is readable.
61
+ #
62
+ # @param ready [Array<Array<IO>>] the result from `select`
63
+ # @param socket1 [IO] readable side
64
+ # @param socket2 [IO] writable side
65
+ # @return [Boolean] whether the piping loop should terminate
33
66
  def pipe?(ready, socket1, socket2)
34
67
  if ready[0].include?(socket1)
35
68
  data = read(socket1)
@@ -41,10 +74,23 @@ module Nonnative
41
74
  false
42
75
  end
43
76
 
77
+ # Reads bytes from the given socket.
78
+ #
79
+ # Subclasses can override this to inject behavior (e.g. delay).
80
+ #
81
+ # @param socket [IO]
82
+ # @return [String]
44
83
  def read(socket)
45
84
  socket.recv(1024) || ''
46
85
  end
47
86
 
87
+ # Writes bytes to the given socket.
88
+ #
89
+ # Subclasses can override this to inject behavior (e.g. corrupt data).
90
+ #
91
+ # @param socket [IO]
92
+ # @param data [String]
93
+ # @return [Integer] number of bytes written
48
94
  def write(socket, data)
49
95
  socket.write(data)
50
96
  end
@@ -1,8 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Factory for creating socket-pair implementations used by {Nonnative::FaultInjectionProxy}.
5
+ #
6
+ # A socket-pair is responsible for wiring a local accepted socket to a remote upstream socket,
7
+ # optionally injecting failures (close connections, add delays, corrupt data, etc).
8
+ #
9
+ # Proxy states are mapped as follows:
10
+ # - `:none` (or any unknown value) -> {Nonnative::SocketPair} (pass-through)
11
+ # - `:close_all` -> {Nonnative::CloseAllSocketPair}
12
+ # - `:delay` -> {Nonnative::DelaySocketPair}
13
+ # - `:invalid_data` -> {Nonnative::InvalidDataSocketPair}
14
+ #
15
+ # @see Nonnative::FaultInjectionProxy
16
+ # @see Nonnative::SocketPair
17
+ # @see Nonnative::CloseAllSocketPair
18
+ # @see Nonnative::DelaySocketPair
19
+ # @see Nonnative::InvalidDataSocketPair
4
20
  class SocketPairFactory
5
21
  class << self
22
+ # Creates a socket-pair instance for the given proxy state.
23
+ #
24
+ # @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:delay`, `:invalid_data`)
25
+ # @param proxy [Nonnative::ConfigurationProxy] proxy configuration (host/port/options)
26
+ # @return [Nonnative::SocketPair] a socket-pair implementation instance
6
27
  def create(kind, proxy)
7
28
  pair = case kind
8
29
  when :close_all
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Raised when {Nonnative.start} fails to start one or more configured runners.
5
+ #
6
+ # The error message typically contains one line per failing runner.
7
+ #
8
+ # @see Nonnative.start
4
9
  class StartError < Nonnative::Error
5
10
  end
6
11
  end
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Raised when {Nonnative.stop} fails to stop one or more configured runners within the configured timeouts.
5
+ #
6
+ # The error message typically contains one line per runner that did not stop cleanly in time.
7
+ #
8
+ # @see Nonnative.stop
4
9
  class StopError < Nonnative::Error
5
10
  end
6
11
  end
@@ -1,11 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Small helper to run a block with a timeout and convert timeout errors into `false`.
5
+ #
6
+ # This is used internally for readiness/shutdown loops (for example port checks) where the common
7
+ # control-flow is “keep retrying until the timeout elapses”.
8
+ #
9
+ # @example
10
+ # timeout = Nonnative::Timeout.new(1) # seconds
11
+ # ok = timeout.perform do
12
+ # # do work that may take time
13
+ # true
14
+ # end
15
+ # # ok is either the block result or false if the timeout elapsed
16
+ #
4
17
  class Timeout
18
+ # @param time [Numeric] timeout duration in seconds
5
19
  def initialize(time)
6
20
  @time = time
7
21
  end
8
22
 
23
+ # Executes the given block with the configured timeout.
24
+ #
25
+ # If the timeout elapses, returns `false` instead of raising `Timeout::Error`.
26
+ #
27
+ # @yield the work to execute under a timeout
28
+ # @return [Object, false] the block's return value, or `false` if the timeout elapsed
9
29
  def perform(&)
10
30
  ::Timeout.timeout(time, &)
11
31
  rescue ::Timeout::Error
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- VERSION = '1.107.0'
4
+ # The current gem version.
5
+ #
6
+ # @return [String]
7
+ VERSION = '1.108.0'
5
8
  end
data/lib/nonnative.rb CHANGED
@@ -1,5 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # = Nonnative
4
+ #
5
+ # Nonnative is a Ruby-first harness for end-to-end testing of services implemented in other languages.
6
+ # It can:
7
+ #
8
+ # - start external processes and in-process servers
9
+ # - wait for readiness via port checks
10
+ # - optionally run fault-injection proxies in front of services
11
+ #
12
+ # The public entry points are exposed as module-level methods on {Nonnative}.
13
+ #
14
+ # == Basic usage
15
+ #
16
+ # Configure the system under test:
17
+ #
18
+ # Nonnative.configure do |config|
19
+ # config.name = 'example'
20
+ # config.url = 'http://127.0.0.1:8080'
21
+ # config.log = 'test.log'
22
+ #
23
+ # config.process do |p|
24
+ # p.name = 'api'
25
+ # p.command = -> { './bin/api' }
26
+ # p.host = '127.0.0.1'
27
+ # p.port = 8080
28
+ # p.timeout = 10
29
+ # p.log = 'api.log'
30
+ # end
31
+ # end
32
+ #
33
+ # Start and stop around your test suite:
34
+ #
35
+ # Nonnative.start
36
+ # # run tests...
37
+ # Nonnative.stop
38
+ #
39
+ # == Notes
40
+ #
41
+ # This file also requires integration helpers used by acceptance tests. If you require `nonnative` outside a
42
+ # Cucumber runtime, loading `nonnative/cucumber` may not be desirable for your environment.
43
+ #
3
44
  require 'socket'
4
45
  require 'timeout'
5
46
  require 'yaml'
@@ -56,46 +97,116 @@ require 'nonnative/go_command'
56
97
  require 'nonnative/cucumber'
57
98
  require 'nonnative/header'
58
99
 
100
+ # The main namespace for the gem.
101
+ #
102
+ # Most consumers will interact with module-level methods:
103
+ #
104
+ # - {Nonnative.configure} / {Nonnative.configuration}
105
+ # - {Nonnative.start} / {Nonnative.stop}
106
+ # - {Nonnative.clear} / {Nonnative.reset}
107
+ #
108
+ # @see Nonnative::Configuration for the configuration DSL
109
+ # @see Nonnative::Pool for lifecycle orchestration once started
59
110
  module Nonnative
60
111
  class << self
112
+ # Returns the current runner pool (created on {Nonnative.start}).
113
+ #
114
+ # @return [Nonnative::Pool, nil] the pool instance, or `nil` if not started yet
61
115
  attr_reader :pool
62
116
 
117
+ # Loads one or more configuration files using the `config` gem.
118
+ #
119
+ # This is primarily used by {Nonnative::Configuration#load_file}, but is public for advanced cases.
120
+ #
121
+ # @param files [Array<String>] paths to configuration files
122
+ # @return [Config::Options] the loaded configuration object
63
123
  def configurations(*files)
64
124
  Config.load_files(files)
65
125
  end
66
126
 
127
+ # Returns the current configuration (memoized).
128
+ #
129
+ # @return [Nonnative::Configuration]
67
130
  def configuration
68
131
  @configuration ||= Nonnative::Configuration.new
69
132
  end
70
133
 
134
+ # Yields the configuration to a block for programmatic setup.
135
+ #
136
+ # @yieldparam config [Nonnative::Configuration]
137
+ # @return [void]
138
+ #
139
+ # @example
140
+ # Nonnative.configure do |config|
141
+ # config.name = 'my-service'
142
+ # # ...
143
+ # end
71
144
  def configure
72
145
  yield configuration
73
146
  end
74
147
 
148
+ # Returns the gem logger (memoized).
149
+ #
150
+ # The logger writes to the path configured at {Nonnative::Configuration#log}.
151
+ #
152
+ # @return [Logger]
75
153
  def logger
76
154
  @logger ||= Logger.new(configuration.log)
77
155
  end
78
156
 
157
+ # Reads a file and returns only lines matching the given predicate.
158
+ #
159
+ # @param path [String] file path to read
160
+ # @param predicate [#call] callable that receives a line and returns truthy/falsey
161
+ # @return [Array<String>] matching lines
79
162
  def log_lines(path, predicate)
80
163
  File.readlines(path).select { |l| predicate.call(l) }
81
164
  end
82
165
 
166
+ # Builds a Go test executable command line with optional profiling/trace/coverage flags.
167
+ #
168
+ # This is used when process configuration specifies a `go` section.
169
+ #
170
+ # @param tools [Array<String>] enabled tool names (e.g. `["prof", "trace", "cover"]`)
171
+ # @param output [String] directory where outputs should be written
172
+ # @param exec [String] the test binary (or wrapper) to execute
173
+ # @param cmd [String] the command argument passed to the test binary
174
+ # @param params [Array<String>] extra parameters for the command
175
+ # @return [String] executable command string
83
176
  def go_executable(tools, output, exec, cmd, *params)
84
177
  Nonnative::GoCommand.new(tools, exec, output).executable(cmd, params)
85
178
  end
86
179
 
180
+ # Returns an HTTP client for common health/readiness endpoints.
181
+ #
182
+ # @return [Nonnative::Observability]
87
183
  def observability
88
184
  @observability ||= Nonnative::Observability.new(configuration.url)
89
185
  end
90
186
 
187
+ # Returns the configured proxy kinds mapped to proxy classes.
188
+ #
189
+ # Consumers can extend this map to add custom proxy implementations.
190
+ #
191
+ # @return [Hash{String=>Class}]
91
192
  def proxies
92
193
  @proxies ||= { 'fault_injection' => Nonnative::FaultInjectionProxy }.freeze
93
194
  end
94
195
 
196
+ # Resolves a proxy implementation for a configured kind.
197
+ #
198
+ # @param kind [String] proxy kind name (for example `"fault_injection"`)
199
+ # @return [Class] a subclass of {Nonnative::Proxy}
95
200
  def proxy(kind)
96
201
  Nonnative.proxies[kind] || Nonnative::NoProxy
97
202
  end
98
203
 
204
+ # Starts all configured services, servers, and processes, and waits for readiness.
205
+ #
206
+ # Readiness is determined by attempting to connect to each runner's configured host/port.
207
+ #
208
+ # @return [void]
209
+ # @raise [Nonnative::StartError] if one or more runners fail to start or become ready in time
99
210
  def start
100
211
  @pool ||= Nonnative::Pool.new(configuration)
101
212
  errors = []
@@ -108,6 +219,10 @@ module Nonnative
108
219
  raise Nonnative::StartError, errors.join("\n") unless errors.empty?
109
220
  end
110
221
 
222
+ # Stops all configured processes and servers, then services, and waits for shutdown.
223
+ #
224
+ # @return [void]
225
+ # @raise [Nonnative::StopError] if one or more runners fail to stop in time
111
226
  def stop
112
227
  return if @pool.nil?
113
228
 
@@ -120,19 +235,32 @@ module Nonnative
120
235
  raise Nonnative::StopError, errors.join("\n") unless errors.empty?
121
236
  end
122
237
 
238
+ # Clears the memoized configuration instance.
239
+ #
240
+ # @return [void]
123
241
  def clear_configuration
124
242
  @configuration = nil
125
243
  end
126
244
 
245
+ # Clears the memoized pool instance.
246
+ #
247
+ # @return [void]
127
248
  def clear_pool
128
249
  @pool = nil
129
250
  end
130
251
 
252
+ # Clears memoized configuration and pool.
253
+ #
254
+ # @return [void]
131
255
  def clear
132
256
  clear_configuration
133
257
  clear_pool
134
258
  end
135
259
 
260
+ # Resets proxies for all currently started runners.
261
+ #
262
+ # @return [void]
263
+ # @raise [NoMethodError] if called before {Nonnative.start} (because {Nonnative.pool} is nil)
136
264
  def reset
137
265
  Nonnative.pool.reset
138
266
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nonnative
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.107.0
4
+ version: 1.108.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -264,6 +264,7 @@ files:
264
264
  - ".gitignore"
265
265
  - ".gitmodules"
266
266
  - ".rubocop.yml"
267
+ - AGENTS.md
267
268
  - CHANGELOG.md
268
269
  - Gemfile
269
270
  - Gemfile.lock
@@ -330,7 +331,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
330
331
  - !ruby/object:Gem::Version
331
332
  version: '0'
332
333
  requirements: []
333
- rubygems_version: 3.6.9
334
+ rubygems_version: 4.0.3
334
335
  specification_version: 4
335
336
  summary: Allows you to keep using the power of ruby to test other systems
336
337
  test_files: []