nonnative 1.106.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +16 -11
  3. data/.rubocop.yml +3 -0
  4. data/AGENTS.md +248 -0
  5. data/CHANGELOG.md +155 -0
  6. data/Gemfile.lock +65 -63
  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. data/nonnative.gemspec +1 -1
  44. metadata +7 -6
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
data/nonnative.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency 'cucumber', '>= 7', '< 11'
30
30
  spec.add_dependency 'get_process_mem', '>= 1', '< 2'
31
31
  spec.add_dependency 'grpc', '>= 1', '< 2'
32
- spec.add_dependency 'puma', '>= 6', '< 7'
32
+ spec.add_dependency 'puma', '>= 7', '< 8'
33
33
  spec.add_dependency 'rest-client', '>= 2', '< 3'
34
34
  spec.add_dependency 'retriable', '>= 3', '< 4'
35
35
  spec.add_dependency 'rspec-benchmark', '>= 0', '< 1'
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.106.0
4
+ version: 1.108.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -115,20 +115,20 @@ dependencies:
115
115
  requirements:
116
116
  - - ">="
117
117
  - !ruby/object:Gem::Version
118
- version: '6'
118
+ version: '7'
119
119
  - - "<"
120
120
  - !ruby/object:Gem::Version
121
- version: '7'
121
+ version: '8'
122
122
  type: :runtime
123
123
  prerelease: false
124
124
  version_requirements: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
- version: '6'
128
+ version: '7'
129
129
  - - "<"
130
130
  - !ruby/object:Gem::Version
131
- version: '7'
131
+ version: '8'
132
132
  - !ruby/object:Gem::Dependency
133
133
  name: rest-client
134
134
  requirement: !ruby/object:Gem::Requirement
@@ -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: []