nonnative 3.10.0 → 3.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a276b7ac5edae149fc6eb065909773b73c5c7e1684e4502244d88a0cdd6c521
4
- data.tar.gz: 82545f52a383ba1f67af5c1d01c72653adee7339a3e1968fb987eabc9236421e
3
+ metadata.gz: 6a7d0e5ea6dab1d00eedd363fc81d05d2dd3e4401a97729043bcd3132a071432
4
+ data.tar.gz: 53c589eda9e895f3c815446003c97e77859ccec3b0c021182dc1437d0632b267
5
5
  SHA512:
6
- metadata.gz: 0eea335efd1554822d7153077244d2f8ff5d19c46af6b19e153bd0298e99e4902f090b933899d4614e46a88252d9b95e9f7ec92b3d6e9e824c96f00694d579f9
7
- data.tar.gz: ce2d8e41f9313b289c3531fe8c605e869dd856a1eb8ae47bb1671ebc12aa1f97a3a8e795cad16681c11857b2a70ce4f2250b0310dd57286162cc9aed7efa764e
6
+ metadata.gz: be0b0b0d4cf1f606fe973f7bb6e38b3f2679b8445d6f24f7f05c4ca37eea62fc36f8910f6d0bcf44c1329b71d6638625d7d4a0d3226a14912662d91980523c1e
7
+ data.tar.gz: 15caeabfe50acdf2513ac2519bc953fb70effcc662c948c293be646d949bdb60c570359fb0817a6eb528207004f6a7cadfd440628026b58a0e9375003f650389
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (3.10.0)
4
+ nonnative (3.11.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
data/README.md CHANGED
@@ -38,6 +38,18 @@ Or install it yourself as:
38
38
  gem install nonnative
39
39
  ```
40
40
 
41
+ ## 🛠️ Contributor Bootstrap
42
+
43
+ Fresh clones need the shared `bin/` submodule before Make targets can load:
44
+
45
+ ```bash
46
+ git submodule sync && git submodule update --init
47
+ make help
48
+ ```
49
+
50
+ Use `make dep` before local validation when dependencies are missing. The CI-parity checks are
51
+ `make lint`, `make sec`, `make features`, and `make benchmarks`.
52
+
41
53
  ## 🚀 Usage
42
54
 
43
55
  Nonnative is configured via `Nonnative.configure` (programmatic) or `config.load_file(...)` (YAML).
@@ -67,12 +79,12 @@ Process/server fields:
67
79
  - `log`: per-runner log file used by process output redirection or server implementations.
68
80
 
69
81
  Process-only fields:
70
- - `readiness`: optional HTTP startup readiness check with explicit `port` and `path`.
82
+ - `readiness`: optional HTTP startup readiness check with explicit `port` and path-only `path`.
71
83
 
72
84
  Service fields:
73
85
  - `port`: client-facing service port. Services do not get TCP readiness/shutdown checks from Nonnative.
74
86
 
75
- Nonnative readiness and shutdown checks are TCP port checks by default. Configure process/server ports that are dedicated to the test run; if another process is already listening on the same endpoint, results are undefined. Processes can also opt into an HTTP readiness check that runs after TCP readiness succeeds.
87
+ Nonnative readiness and shutdown checks are TCP port checks by default. Configure process/server ports that are dedicated to the test run; if another process is already listening on the same endpoint, results are undefined. Processes can also opt into an HTTP readiness check that runs after TCP readiness succeeds. HTTP readiness paths must be path-only values, such as `/test/readyz`; absolute URLs and scheme-relative URLs are rejected.
76
88
 
77
89
  > [!WARNING]
78
90
  > TCP readiness and shutdown checks only prove that a TCP port opened or closed. HTTP readiness is process-only, checks for a 2xx response, and does not verify gRPC health, schema readiness, migrations, or other application-specific health.
@@ -98,6 +110,37 @@ Nonnative.stop
98
110
  > Call `Nonnative.clear` before reconfiguring Nonnative or starting a new lifecycle in the same Ruby process.
99
111
  > `Nonnative.clear` clears memoized configuration, logger, observability client, and pool.
100
112
 
113
+ ### 🧩 Test framework setup
114
+
115
+ For Cucumber, load and configure Nonnative in `features/support/env.rb`, then use lifecycle tags on scenarios:
116
+
117
+ ```ruby
118
+ require 'nonnative'
119
+
120
+ Nonnative.configure do |config|
121
+ config.load_file('configuration.yml')
122
+ end
123
+ ```
124
+
125
+ ```cucumber
126
+ @startup
127
+ Scenario: run with Nonnative around this scenario
128
+ ```
129
+
130
+ For RSpec or another suite that should start Nonnative once per test run, configure first and then require the startup integration:
131
+
132
+ ```ruby
133
+ require 'nonnative'
134
+
135
+ Nonnative.configure do |config|
136
+ config.load_file('configuration.yml')
137
+ end
138
+
139
+ require 'nonnative/startup'
140
+ ```
141
+
142
+ `nonnative/startup` calls `Nonnative.start` immediately and registers an `at_exit` stop, so load configuration before requiring it.
143
+
101
144
  ### 📈 Observability
102
145
 
103
146
  `Nonnative.observability` is an HTTP client for common service endpoints under the configured `name` and `url`:
@@ -123,7 +166,7 @@ expect(response.code).to eq(200)
123
166
 
124
167
  Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
125
168
  - `@startup`: start before scenario; stop after scenario
126
- - `@manual`: stop after scenario (start is expected to be triggered manually in steps)
169
+ - `@manual`: stop after scenario; use `When I start the system` to start manually
127
170
  - `@clear`: clears memoized configuration, logger, observability client, and pool before scenario
128
171
  - `@reset`: resets proxies after scenario
129
172
 
@@ -140,14 +183,6 @@ The repo’s own Cucumber suite also uses taxonomy tags to classify coverage:
140
183
 
141
184
  Requiring `nonnative` is enough; the Cucumber hooks and step definitions are installed lazily once Cucumber’s Ruby DSL is ready.
142
185
 
143
- If you want "start once per test run", require:
144
-
145
- ```ruby
146
- require 'nonnative/startup'
147
- ```
148
-
149
- This calls `Nonnative.start` immediately and registers an `at_exit` stop.
150
-
151
186
  ### ⚙️ Processes
152
187
 
153
188
  A process is some sort of command that you would run locally.
@@ -9,7 +9,7 @@ module Nonnative
9
9
  # @return [Integer] process HTTP readiness port
10
10
  attr_accessor :port
11
11
 
12
- # @return [String] HTTP readiness path
12
+ # @return [String] path-only HTTP readiness path
13
13
  attr_accessor :path
14
14
 
15
15
  # @param value [Hash, #to_h] readiness attributes
@@ -30,6 +30,15 @@ module Nonnative
30
30
  def validate!
31
31
  raise ArgumentError, "Process readiness requires 'port'" if port.nil?
32
32
  raise ArgumentError, "Process readiness requires 'path'" if path.nil?
33
+ raise ArgumentError, 'Process readiness path must be path-only' unless path_only?
34
+ end
35
+
36
+ def path_only?
37
+ uri = URI.parse(path.to_s)
38
+
39
+ uri.scheme.nil? && uri.host.nil?
40
+ rescue URI::InvalidURIError
41
+ false
33
42
  end
34
43
  end
35
44
  end
@@ -43,10 +43,11 @@ module Nonnative
43
43
  end
44
44
 
45
45
  def install_hooks
46
+ # Register @clear before startup hooks so combined tags reset stale state before creating a pool.
47
+ Before('@clear') { Nonnative.clear }
46
48
  Before('@startup') { Nonnative.start }
47
49
  After('@startup') { Nonnative.stop }
48
50
  After('@manual') { Nonnative.stop }
49
- Before('@clear') { Nonnative.clear }
50
51
  After('@reset') { Nonnative.reset }
51
52
  end
52
53
  end
@@ -28,7 +28,7 @@ module Nonnative
28
28
  Nonnative.logger.info "checking if readiness '#{endpoint}' is ready"
29
29
 
30
30
  timeout.perform do
31
- response = get(readiness.path)
31
+ response = get(path)
32
32
  raise Nonnative::Error unless ready_response?(response)
33
33
 
34
34
  true
@@ -4,8 +4,8 @@ module Nonnative
4
4
  # No-op proxy implementation.
5
5
  #
6
6
  # This is the default proxy when `service.proxy.kind` is `"none"`.
7
- # It does not bind/listen or alter traffic; it simply exposes the underlying runner's configured
8
- # `host` and primary `port`.
7
+ # It does not bind/listen or alter traffic; it simply exposes the service configuration's
8
+ # client-facing `host` and `port`.
9
9
  #
10
10
  # Services can always call `start`, `stop`, and `reset` safely on this proxy.
11
11
  #
@@ -41,7 +41,7 @@ module Nonnative
41
41
 
42
42
  # Returns the host clients should connect to.
43
43
  #
44
- # For {NoProxy}, this is the underlying runner configuration host.
44
+ # For {NoProxy}, this is the service configuration's client-facing `host`.
45
45
  #
46
46
  # @return [String]
47
47
  def host
@@ -50,7 +50,7 @@ module Nonnative
50
50
 
51
51
  # Returns the port clients should connect to.
52
52
  #
53
- # For {NoProxy}, this is the first underlying runner configuration port.
53
+ # For {NoProxy}, this is the service configuration's client-facing `port`.
54
54
  #
55
55
  # @return [Integer]
56
56
  def port
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Starts the configured Nonnative pool immediately and registers an `at_exit` stop hook.
4
+ #
5
+ # Configure Nonnative before requiring this file; it is intended for suites that keep one
6
+ # Nonnative lifecycle open for the whole Ruby process.
7
+
3
8
  at_exit do
4
9
  Nonnative.stop
5
10
  end
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '3.10.0'
7
+ VERSION = '3.11.0'
8
8
  end
data/lib/nonnative.rb CHANGED
@@ -48,6 +48,7 @@ require 'yaml'
48
48
  require 'open3'
49
49
  require 'securerandom'
50
50
  require 'shellwords'
51
+ require 'uri'
51
52
 
52
53
  require 'grpc'
53
54
  require 'sinatra'
@@ -204,8 +205,12 @@ module Nonnative
204
205
 
205
206
  # Resolves a proxy implementation for a configured kind.
206
207
  #
208
+ # `nil` and `"none"` resolve to {Nonnative::NoProxy}; any other kind must be registered in
209
+ # {Nonnative.proxies}.
210
+ #
207
211
  # @param kind [String] proxy kind name (for example `"fault_injection"`)
208
212
  # @return [Class] a subclass of {Nonnative::Proxy}
213
+ # @raise [ArgumentError] if the kind is not `"none"` and has not been registered
209
214
  def proxy(kind)
210
215
  kind.nil? || kind == 'none' ? NoProxy : proxies.fetch(kind) { raise ArgumentError, "Unsupported proxy kind '#{kind}'" }
211
216
  end
data/nonnative.gemspec CHANGED
@@ -11,8 +11,8 @@ Gem::Specification.new do |spec|
11
11
  spec.authors = ['Alejandro Falkowski']
12
12
  spec.email = ['alexrfalkowski@gmail.com']
13
13
 
14
- spec.summary = 'Allows you to keep using the power of Ruby to test other systems'
15
- spec.description = spec.summary
14
+ spec.summary = 'Ruby-first end-to-end harness for testing systems implemented in other languages'
15
+ spec.description = 'Starts OS processes, in-process Ruby servers, and proxy-only services with TCP readiness checks and fault-injection proxies.'
16
16
  spec.homepage = 'https://github.com/alexfalkowski/nonnative'
17
17
  spec.license = 'MIT'
18
18
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
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: 3.10.0
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -263,7 +263,8 @@ dependencies:
263
263
  - - "<"
264
264
  - !ruby/object:Gem::Version
265
265
  version: '5'
266
- description: Allows you to keep using the power of Ruby to test other systems
266
+ description: Starts OS processes, in-process Ruby servers, and proxy-only services
267
+ with TCP readiness checks and fault-injection proxies.
267
268
  email:
268
269
  - alexrfalkowski@gmail.com
269
270
  executables: []
@@ -351,5 +352,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
351
352
  requirements: []
352
353
  rubygems_version: 4.0.11
353
354
  specification_version: 4
354
- summary: Allows you to keep using the power of Ruby to test other systems
355
+ summary: Ruby-first end-to-end harness for testing systems implemented in other languages
355
356
  test_files: []