nonnative 2.23.0 → 3.1.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: 53ded0979e2825948ce16066839a784ef26ee94385d78ba2f557da5ce9e4424a
4
- data.tar.gz: 5dbceda50fe7dc82c109a3fec1c4af041a27026a438bb7c51eee52344be13ab1
3
+ metadata.gz: 3e20a9fa436249c182c4fc90bfaaeeaed91035899d31340eba35d1f4208a31ab
4
+ data.tar.gz: c68e618e0a2a48cf9958210a8200a96c2aff7c0406eeec332bea01cf9290e860
5
5
  SHA512:
6
- metadata.gz: d2952d70183482b382509e0088fc5e2c8e4b53a320ddb00d57e9237961355f39bc48828c26779568318bad54552ad373bd341349d2cbb77cedd5dbc0fa84f6c0
7
- data.tar.gz: fb7c6404f30ec98145e168c919ad4de609eb4a11436bf0c5f8d8a7832e93bc9fad55c8cd4b820242bdfc9f928640dcd3577b83cf54dd98520c08046d12bd71f2
6
+ metadata.gz: c228d28fa41f6e9d526c107feedb4aebfed673aca7b47d7262913db7ceb04468772a1ca068fd4cf696a46a50fc52f01f3dfb9934547945c6ef78b03298cd6cc4
7
+ data.tar.gz: 60ebed64503ac1505ccd4398330290627600f14519d40389ba96a5e02189762e7d54b314ee98fdd31220f9aba560fc6e09ad7340ffc3925b3f3359747bcf0c0f
data/.circleci/config.yml CHANGED
@@ -3,7 +3,7 @@ version: 2.1
3
3
  jobs:
4
4
  build:
5
5
  docker:
6
- - image: alexfalkowski/ruby:3.12
6
+ - image: alexfalkowski/ruby:3.14
7
7
  working_directory: ~/nonnative
8
8
  steps:
9
9
  - checkout:
@@ -35,7 +35,7 @@ jobs:
35
35
  resource_class: arm.large
36
36
  sync:
37
37
  docker:
38
- - image: alexfalkowski/release:8.11
38
+ - image: alexfalkowski/release:8.14
39
39
  working_directory: ~/nonnative
40
40
  steps:
41
41
  - checkout:
@@ -46,7 +46,7 @@ jobs:
46
46
  resource_class: arm.large
47
47
  version:
48
48
  docker:
49
- - image: alexfalkowski/release:8.11
49
+ - image: alexfalkowski/release:8.14
50
50
  working_directory: ~/nonnative
51
51
  steps:
52
52
  - checkout:
@@ -58,7 +58,7 @@ jobs:
58
58
  resource_class: arm.large
59
59
  wait-all:
60
60
  docker:
61
- - image: alexfalkowski/ruby:3.12
61
+ - image: alexfalkowski/ruby:3.14
62
62
  steps:
63
63
  - run: echo "all applicable jobs finished"
64
64
  resource_class: arm.large
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (2.23.0)
4
+ nonnative (3.1.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
@@ -30,7 +30,7 @@ GEM
30
30
  config (5.6.1)
31
31
  deep_merge (~> 1.2, >= 1.2.1)
32
32
  ostruct
33
- cucumber (11.0.0)
33
+ cucumber (11.1.0)
34
34
  base64 (~> 0.2)
35
35
  builder (~> 3.2)
36
36
  cucumber-ci-environment (> 9, < 12)
@@ -64,24 +64,24 @@ GEM
64
64
  get_process_mem (1.0.0)
65
65
  bigdecimal (>= 2.0)
66
66
  ffi (~> 1.0)
67
- google-protobuf (4.34.1-x86_64-darwin)
67
+ google-protobuf (4.35.0-x86_64-darwin)
68
68
  bigdecimal
69
69
  rake (~> 13.3)
70
- google-protobuf (4.34.1-x86_64-linux-gnu)
70
+ google-protobuf (4.35.0-x86_64-linux-gnu)
71
71
  bigdecimal
72
72
  rake (~> 13.3)
73
- googleapis-common-protos-types (1.22.0)
73
+ googleapis-common-protos-types (1.23.0)
74
74
  google-protobuf (~> 4.26)
75
- grpc (1.80.0-x86_64-darwin)
75
+ grpc (1.81.0-x86_64-darwin)
76
76
  google-protobuf (>= 3.25, < 5.0)
77
77
  googleapis-common-protos-types (~> 1.0)
78
- grpc (1.80.0-x86_64-linux-gnu)
78
+ grpc (1.81.0-x86_64-linux-gnu)
79
79
  google-protobuf (>= 3.25, < 5.0)
80
80
  googleapis-common-protos-types (~> 1.0)
81
81
  http-accept (1.7.0)
82
82
  http-cookie (1.1.6)
83
83
  domain_name (~> 0.5)
84
- json (2.19.5)
84
+ json (2.19.8)
85
85
  language_server-protocol (3.17.0.5)
86
86
  lint_roller (1.1.0)
87
87
  logger (1.7.0)
@@ -101,7 +101,7 @@ GEM
101
101
  ast (~> 2.4.1)
102
102
  racc
103
103
  prism (1.9.0)
104
- puma (7.2.0)
104
+ puma (7.2.1)
105
105
  nio4r (~> 2.0)
106
106
  racc (1.8.1)
107
107
  rack (3.2.6)
@@ -124,7 +124,7 @@ GEM
124
124
  http-cookie (>= 1.0.2, < 2.0)
125
125
  mime-types (>= 1.16, < 4.0)
126
126
  netrc (~> 0.8)
127
- retriable (3.4.1)
127
+ retriable (3.8.0)
128
128
  rexml (3.4.4)
129
129
  rspec (3.13.2)
130
130
  rspec-core (~> 3.13.0)
@@ -146,7 +146,7 @@ GEM
146
146
  rspec-support (3.13.7)
147
147
  rspec-wait (1.0.2)
148
148
  rspec (>= 3.4)
149
- rubocop (1.86.2)
149
+ rubocop (1.87.0)
150
150
  json (~> 2.3)
151
151
  language_server-protocol (~> 3.17.0.2)
152
152
  lint_roller (~> 1.1.0)
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/nonnative.svg)](https://badge.fury.io/rb/nonnative)
4
4
  [![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)
5
5
 
6
- # Nonnative
6
+ # 🧪 Nonnative
7
7
 
8
8
  Nonnative is a Ruby-first harness for end-to-end testing of systems implemented in other languages.
9
9
 
@@ -15,7 +15,10 @@ It helps you:
15
15
 
16
16
  Once started, you can test however you like (TCP, HTTP, gRPC, etc).
17
17
 
18
- ## Installation
18
+ ## 📦 Installation
19
+
20
+ > [!IMPORTANT]
21
+ > Nonnative currently supports Ruby `>= 4.0.0` and `< 5.0.0`.
19
22
 
20
23
  Add this line to your application's Gemfile:
21
24
 
@@ -35,12 +38,15 @@ Or install it yourself as:
35
38
  gem install nonnative
36
39
  ```
37
40
 
38
- ## Usage
41
+ ## 🚀 Usage
39
42
 
40
43
  Nonnative is configured via `Nonnative.configure` (programmatic) or `config.load_file(...)` (YAML).
41
44
  YAML configuration is loaded as data only: ERB is not evaluated and arbitrary Ruby objects are not
42
45
  deserialized.
43
46
 
47
+ > [!CAUTION]
48
+ > Treat YAML configuration as plain data. ERB is not evaluated and arbitrary Ruby object tags are rejected.
49
+
44
50
  High-level configuration fields:
45
51
  - `version`: configuration version (example: `"1.0"`).
46
52
  - `name`: logical system name (used by `Nonnative.observability` for `/<name>/healthz`, etc).
@@ -52,18 +58,68 @@ High-level configuration fields:
52
58
 
53
59
  Common runner fields:
54
60
  - `name`: runner name used for lookup.
55
- - `host`/`port`: client-facing address. `host` defaults to `127.0.0.1`. For processes and servers, this address is also used for readiness/shutdown port checks. When a `fault_injection` proxy is enabled, this is the endpoint your tests/clients should hit.
61
+ - `host`: client-facing host. Defaults to `127.0.0.1`.
56
62
 
57
63
  Process/server fields:
64
+ - `ports`: client-facing ports. These are also used for readiness/shutdown port checks. When a `fault_injection` proxy is enabled, clients should hit the first configured port.
58
65
  - `timeout`: max time (seconds) for readiness/shutdown port checks.
59
66
  - `wait`: small sleep (seconds) between lifecycle steps.
60
67
  - `log`: per-runner log file used by process output redirection or server implementations.
61
68
 
69
+ Service fields:
70
+ - `port`: client-facing proxy port. Services do not get TCP readiness/shutdown checks from Nonnative.
71
+
62
72
  For `fault_injection`, the nested `proxy.host`/`proxy.port` describe the upstream target behind the proxy. Nested `proxy.host` also defaults to `127.0.0.1`. In-process server implementations typically bind there via `proxy.host` / `proxy.port`.
63
73
 
64
- Nonnative readiness and shutdown checks are TCP-only. Configure ports that are dedicated to the test run; if another process is already listening on the same `host`/`port`, results are undefined.
74
+ > [!IMPORTANT]
75
+ > When a proxy is enabled, tests and clients connect to the runner `host` and client-facing endpoint (`ports` first entry for processes/servers, `port` for services); the nested `proxy.host`/`proxy.port` is the upstream target behind the proxy.
76
+
77
+ Nonnative readiness and shutdown checks are TCP-only. Configure process/server ports that are dedicated to the test run; if another process is already listening on the same endpoint, results are undefined.
78
+
79
+ > [!WARNING]
80
+ > Readiness and shutdown checks only prove that a TCP port opened or closed. They do not verify HTTP status, gRPC health, schema readiness, migrations, or application-specific health.
81
+
82
+ Start and stop Nonnative around the test scope that should own the configured runners:
83
+
84
+ ```ruby
85
+ require 'nonnative'
86
+
87
+ Nonnative.configure do |config|
88
+ config.load_file('configuration.yml')
89
+ end
90
+
91
+ Nonnative.start
92
+ # run tests...
93
+ Nonnative.stop
94
+ ```
95
+
96
+ `Nonnative.start` starts services first, then servers and processes. `Nonnative.stop` stops processes and servers first, then services. If startup fails, Nonnative rolls back runners that already started and raises `Nonnative::StartError`; shutdown failures raise `Nonnative::StopError`.
97
+
98
+ > [!NOTE]
99
+ > `Nonnative.clear` clears memoized configuration, logger, observability client, and pool. Use it before reconfiguring Nonnative in the same Ruby process.
65
100
 
66
- ### Lifecycle strategies (Cucumber integration)
101
+ ### 📈 Observability
102
+
103
+ `Nonnative.observability` is an HTTP client for common service endpoints under the configured `name` and `url`:
104
+
105
+ - `health(...)`: calls `/<name>/healthz`.
106
+ - `liveness(...)`: calls `/<name>/livez`.
107
+ - `readiness(...)`: calls `/<name>/readyz`.
108
+ - `metrics(...)`: calls `/<name>/metrics`.
109
+
110
+ Each method accepts RestClient options such as `headers`, `open_timeout`, and `read_timeout`.
111
+
112
+ ```ruby
113
+ response = Nonnative.observability.health(
114
+ headers: { content_type: :json, accept: :json },
115
+ open_timeout: 2,
116
+ read_timeout: 2
117
+ )
118
+
119
+ expect(response.code).to eq(200)
120
+ ```
121
+
122
+ ### 🔁 Lifecycle strategies (Cucumber integration)
67
123
 
68
124
  Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
69
125
  - `@startup`: start before scenario; stop after scenario
@@ -84,7 +140,7 @@ The repo’s own Cucumber suite also uses taxonomy tags to classify coverage:
84
140
 
85
141
  Requiring `nonnative` is enough; the Cucumber hooks and step definitions are installed lazily once Cucumber’s Ruby DSL is ready.
86
142
 
87
- If you want start once per test run”, require:
143
+ If you want "start once per test run", require:
88
144
 
89
145
  ```ruby
90
146
  require 'nonnative/startup'
@@ -92,11 +148,13 @@ require 'nonnative/startup'
92
148
 
93
149
  This calls `Nonnative.start` immediately and registers an `at_exit` stop.
94
150
 
95
- ### Processes
151
+ ### ⚙️ Processes
96
152
 
97
153
  A process is some sort of command that you would run locally.
98
- Commands can be strings or argv arrays. String commands preserve legacy shell semantics, while argv arrays
99
- avoid shell interpretation and are preferred for new configuration.
154
+ Programmatic `p.command` values must be callables that return a shell string or an argv array. YAML `command` values can be scalars or lists and are wrapped internally. String commands preserve legacy shell semantics, while argv arrays avoid shell interpretation and are preferred for new configuration.
155
+
156
+ > [!TIP]
157
+ > Prefer argv arrays for new process commands. Use shell strings only when you intentionally need shell parsing, expansion, or redirection.
100
158
 
101
159
  Set it up programmatically:
102
160
 
@@ -114,7 +172,7 @@ Nonnative.configure do |config|
114
172
  p.command = -> { ['features/support/bin/start', '12_321'] }
115
173
  p.timeout = 5
116
174
  p.wait = 0.1
117
- p.port = 12_321
175
+ p.ports = [12_321]
118
176
  p.log = '12_321.log'
119
177
  p.signal = 'INT' # Possible values are described in Signal.list.keys.
120
178
  p.environment = { # Pass environment variables to process.
@@ -127,7 +185,7 @@ Nonnative.configure do |config|
127
185
  p.command = -> { ['features/support/bin/start', '12_322'] }
128
186
  p.timeout = 0.5
129
187
  p.wait = 0.1
130
- p.port = 12_322
188
+ p.ports = [12_322]
131
189
  p.log = '12_322.log'
132
190
  end
133
191
  end
@@ -148,7 +206,8 @@ processes:
148
206
  - "12_321"
149
207
  timeout: 5
150
208
  wait: 1
151
- port: 12321
209
+ ports:
210
+ - 12321
152
211
  log: 12_321.log
153
212
  signal: INT # Possible values are described in Signal.list.keys.
154
213
  environment: # Pass environment variables to process.
@@ -160,7 +219,8 @@ processes:
160
219
  - "12_322"
161
220
  timeout: 5
162
221
  wait: 1
163
- port: 12322
222
+ ports:
223
+ - 12322
164
224
  log: 12_322.log
165
225
  ```
166
226
 
@@ -180,9 +240,9 @@ With cucumber you can also verify how much memory is used by the process:
180
240
  Then the process 'start_1' should consume less than '25mb' of memory
181
241
  ```
182
242
 
183
- ### Servers
243
+ ### 🖥️ Servers
184
244
 
185
- A server is a dependency to some external API.
245
+ A server is an in-process Ruby fake or helper server that Nonnative starts in a thread. Use servers for dependencies that are easiest to model inside the test process, such as small TCP, HTTP, or gRPC fakes.
186
246
 
187
247
  Define your server:
188
248
 
@@ -231,7 +291,7 @@ Nonnative.configure do |config|
231
291
  s.name = 'server_1'
232
292
  s.klass = Nonnative::TCPServer
233
293
  s.timeout = 1
234
- s.port = 12_323
294
+ s.ports = [12_323]
235
295
  s.log = 'server_1.log'
236
296
  end
237
297
 
@@ -239,7 +299,7 @@ Nonnative.configure do |config|
239
299
  s.name = 'server_2'
240
300
  s.klass = Nonnative::TCPServer
241
301
  s.timeout = 1
242
- s.port = 12_324
302
+ s.ports = [12_324]
243
303
  s.log = 'server_2.log'
244
304
  end
245
305
  end
@@ -257,13 +317,15 @@ servers:
257
317
  name: server_1
258
318
  class: Nonnative::TCPServer
259
319
  timeout: 1
260
- port: 12323
320
+ ports:
321
+ - 12323
261
322
  log: server_1.log
262
323
  -
263
324
  name: server_2
264
325
  class: Nonnative::TCPServer
265
326
  timeout: 1
266
- port: 12324
327
+ ports:
328
+ - 12324
267
329
  log: server_2.log
268
330
  ```
269
331
 
@@ -277,7 +339,7 @@ Nonnative.configure do |config|
277
339
  end
278
340
  ```
279
341
 
280
- #### HTTP
342
+ #### 🌐 HTTP
281
343
 
282
344
  Define your server:
283
345
 
@@ -314,7 +376,7 @@ Nonnative.configure do |config|
314
376
  s.name = 'http_server_1'
315
377
  s.klass = Nonnative::Features::HTTPServer
316
378
  s.timeout = 1
317
- s.port = 4567
379
+ s.ports = [4567]
318
380
  s.log = 'http_server_1.log'
319
381
  end
320
382
  end
@@ -332,7 +394,8 @@ servers:
332
394
  name: http_server_1
333
395
  class: Nonnative::Features::HTTPServer
334
396
  timeout: 1
335
- port: 4567
397
+ ports:
398
+ - 4567
336
399
  log: http_server_1.log
337
400
  ```
338
401
 
@@ -346,9 +409,9 @@ Nonnative.configure do |config|
346
409
  end
347
410
  ```
348
411
 
349
- ##### Proxy
412
+ ##### 🔀 Proxy
350
413
 
351
- The system allows you to define a http proxy for external systems, e.g api.github.com
414
+ The system allows you to define an HTTP proxy for external systems, e.g. `api.github.com`.
352
415
 
353
416
  Define your server:
354
417
 
@@ -379,7 +442,7 @@ Nonnative.configure do |config|
379
442
  s.name = 'http_server_proxy'
380
443
  s.klass = Nonnative::Features::HTTPProxyServer
381
444
  s.timeout = 1
382
- s.port = 4567
445
+ s.ports = [4567]
383
446
  s.log = 'http_server_proxy.log'
384
447
  end
385
448
  end
@@ -397,7 +460,8 @@ servers:
397
460
  name: http_server_proxy
398
461
  class: Nonnative::Features::HTTPProxyServer
399
462
  timeout: 1
400
- port: 4567
463
+ ports:
464
+ - 4567
401
465
  log: http_server_proxy.log
402
466
  ```
403
467
 
@@ -411,7 +475,7 @@ Nonnative.configure do |config|
411
475
  end
412
476
  ```
413
477
 
414
- #### gRPC
478
+ #### 📡 gRPC
415
479
 
416
480
  Define your server:
417
481
 
@@ -448,7 +512,7 @@ Nonnative.configure do |config|
448
512
  s.name = 'grpc_server_1'
449
513
  s.klass = Nonnative::Features::GRPCServer
450
514
  s.timeout = 1
451
- s.port = 9002
515
+ s.ports = [9002]
452
516
  s.log = 'grpc_server_1.log'
453
517
  end
454
518
  end
@@ -466,7 +530,8 @@ servers:
466
530
  name: grpc_server_1
467
531
  class: Nonnative::Features::GRPCServer
468
532
  timeout: 1
469
- port: 9002
533
+ ports:
534
+ - 9002
470
535
  log: grpc_server_1.log
471
536
  ```
472
537
 
@@ -480,10 +545,12 @@ Nonnative.configure do |config|
480
545
  end
481
546
  ```
482
547
 
483
- ### Services
548
+ ### 🧩 Services
484
549
 
485
550
  A service is an external dependency to your system that you **do not** want Nonnative to start (no OS process, no Ruby thread). Services are primarily useful when paired with proxies, because they let you inject failures into dependencies that are managed elsewhere (e.g. a DB running in Docker).
486
551
 
552
+ Services do not get process lifecycle management or TCP readiness/shutdown checks from Nonnative. They only provide a named runner and optional proxy lifecycle for a dependency that another tool already manages.
553
+
487
554
  Set it up programmatically:
488
555
 
489
556
  ```ruby
@@ -537,16 +604,25 @@ Nonnative.configure do |config|
537
604
  end
538
605
  ```
539
606
 
540
- #### Proxies
607
+ #### 🕸️ Proxies
541
608
 
542
- We allow different proxies to be configured. These proxies can be used to simulate all kind of situations. The proxies that can be configured are:
609
+ These proxies can simulate different situations. Available proxy kinds are:
543
610
 
544
611
  - `none` (this is the default)
545
612
  - `fault_injection`
546
613
 
547
- For `fault_injection`, keep the runner `host`/`port` as the client-facing endpoint and use nested `proxy.host`/`proxy.port` for the upstream target behind the proxy.
614
+ > [!WARNING]
615
+ > Unknown proxy kinds fall back to `none`. If fault injection is not taking effect, check the `kind` spelling or register the custom kind before loading the configuration.
616
+
617
+ Custom proxy kinds can be registered through `Nonnative.proxies`:
618
+
619
+ ```ruby
620
+ Nonnative.proxies['custom'] = CustomProxy
621
+ ```
548
622
 
549
- ##### Proxies Processes
623
+ For `fault_injection`, keep the runner `host` and first `ports` entry as the client-facing endpoint and use nested `proxy.host`/`proxy.port` for the upstream target behind the proxy.
624
+
625
+ ##### ⚙️ Process Proxies
550
626
 
551
627
  Add this to an existing process configuration:
552
628
 
@@ -561,7 +637,7 @@ Nonnative.configure do |config|
561
637
 
562
638
  config.process do |p|
563
639
  p.host = '127.0.0.1'
564
- p.port = 20_000
640
+ p.ports = [20_000]
565
641
 
566
642
  p.proxy = {
567
643
  kind: 'fault_injection',
@@ -587,7 +663,8 @@ log: nonnative.log
587
663
  processes:
588
664
  -
589
665
  host: 127.0.0.1
590
- port: 20000
666
+ ports:
667
+ - 20000
591
668
  proxy:
592
669
  kind: fault_injection
593
670
  host: 127.0.0.1
@@ -598,7 +675,7 @@ processes:
598
675
  delay: 5
599
676
  ```
600
677
 
601
- ##### Proxies Servers
678
+ ##### 🖥️ Server Proxies
602
679
 
603
680
  Add this to an existing server configuration:
604
681
 
@@ -613,7 +690,7 @@ Nonnative.configure do |config|
613
690
 
614
691
  config.server do |s|
615
692
  s.host = '127.0.0.1'
616
- s.port = 20_000
693
+ s.ports = [20_000]
617
694
 
618
695
  s.proxy = {
619
696
  kind: 'fault_injection',
@@ -639,7 +716,8 @@ log: nonnative.log
639
716
  servers:
640
717
  -
641
718
  host: 127.0.0.1
642
- port: 20000
719
+ ports:
720
+ - 20000
643
721
  proxy:
644
722
  kind: fault_injection
645
723
  host: 127.0.0.1
@@ -650,7 +728,7 @@ servers:
650
728
  delay: 5
651
729
  ```
652
730
 
653
- ##### Proxies Services
731
+ ##### 🧩 Service Proxies
654
732
 
655
733
  Add this to an existing service configuration:
656
734
 
@@ -704,17 +782,17 @@ services:
704
782
  delay: 5
705
783
  ```
706
784
 
707
- ##### Fault Injection
785
+ ##### 🧪 Fault Injection
708
786
 
709
787
  The `fault_injection` proxy allows you to simulate failures by injecting them. We currently support the following:
710
788
 
711
- Clients connect to the runner `host`/`port`, while the proxy forwards traffic to nested `proxy.host`/`proxy.port`.
789
+ Clients connect to the runner `host` and client-facing endpoint, while the proxy forwards traffic to nested `proxy.host`/`proxy.port`.
712
790
 
713
791
  - `close_all` - Closes the socket as soon as it connects.
714
- - `delay` - This delays the communication between the connection. Default is 2 secs can be configured through options.
715
- - `invalid_data` - This takes the input and rearranges it to produce invalid data.
792
+ - `delay` - Delays traffic on the connection. Defaults to 2 seconds and can be configured through options.
793
+ - `invalid_data` - Forwards client requests unchanged, then corrupts upstream responses before they reach the client.
716
794
 
717
- ###### Fault Injection Processes
795
+ ###### ⚙️ Fault Injection Processes
718
796
 
719
797
  Set it up programmatically:
720
798
 
@@ -733,7 +811,7 @@ Given I set the proxy for process 'process_1' to 'close_all'
733
811
  Then I should reset the proxy for process 'process_1'
734
812
  ```
735
813
 
736
- ###### Fault Injection Servers
814
+ ###### 🖥️ Fault Injection Servers
737
815
 
738
816
  Set it up programmatically:
739
817
 
@@ -752,7 +830,7 @@ Given I set the proxy for server 'server_1' to 'close_all'
752
830
  Then I should reset the proxy for server 'server_1'
753
831
  ```
754
832
 
755
- ###### Fault Injection Services
833
+ ###### 🧩 Fault Injection Services
756
834
 
757
835
  Set it up programmatically:
758
836
 
@@ -771,7 +849,7 @@ Given I set the proxy for service 'service_1' to 'close_all'
771
849
  Then I should reset the proxy for service 'service_1'
772
850
  ```
773
851
 
774
- ### Go
852
+ ### 🐹 Go
775
853
 
776
854
  As we love using Go as a language for services we have added support to start binaries with defined parameters.
777
855
 
@@ -782,7 +860,7 @@ Nonnative.configure do |config|
782
860
  config.process do |p|
783
861
  p.name = 'go'
784
862
  p.command = -> { Nonnative.go_argv(%w[cover], 'reports', 'your_binary', 'sub_command', '-i file:.config/server.yml') }
785
- p.port = 12_345
863
+ p.ports = [12_345]
786
864
  end
787
865
  end
788
866
  ```
@@ -791,6 +869,9 @@ Use `Nonnative.go_argv(...)` when a process should execute without shell interpr
791
869
 
792
870
  YAML `go:` configuration is for Go test binaries compiled with `go test -c`. It builds argv entries in this order: executable, optional `-test.*` profiling/trace/coverage flags, command, then parameters. Parameter strings are parsed into argv words with shell-style quoting, but the argv entries are executed without shell interpretation.
793
871
 
872
+ > [!IMPORTANT]
873
+ > If `tools` is omitted or empty, Nonnative enables all Go tools: `prof`, `trace`, and `cover`. Provide a subset, such as `tools: [cover]`, to limit the generated `-test.*` flags.
874
+
794
875
  To get this to work you will need to create a `main_test.go` file with these contents:
795
876
 
796
877
  ```go
@@ -830,6 +911,7 @@ processes:
830
911
  parameters:
831
912
  - "-i file:.config/server.yml"
832
913
  timeout: 5
833
- port: 8000
914
+ ports:
915
+ - 8000
834
916
  log: go.log
835
917
  ```
@@ -19,7 +19,7 @@ module Nonnative
19
19
  # p.name = 'api'
20
20
  # p.command = -> { './bin/api' }
21
21
  # p.host = '127.0.0.1'
22
- # p.port = 8080
22
+ # p.ports = [8080, 9090]
23
23
  # p.timeout = 10
24
24
  # p.log = 'api.log'
25
25
  # end
@@ -123,14 +123,14 @@ module Nonnative
123
123
 
124
124
  def add_processes(cfg)
125
125
  processes = cfg.processes || []
126
- processes.each do |fd|
127
- process do |d|
128
- d.command = command(fd)
129
- d.signal = fd.signal
130
- d.environment = fd.environment
131
- runner_attributes(d, fd)
132
-
133
- proxy d, fd.proxy
126
+ processes.each do |loaded_process|
127
+ process do |process_config|
128
+ process_config.command = command(loaded_process)
129
+ process_config.signal = loaded_process.signal
130
+ process_config.environment = loaded_process.environment
131
+ runner_attributes(process_config, loaded_process)
132
+
133
+ assign_proxy(process_config, loaded_process.proxy)
134
134
  end
135
135
  end
136
136
  end
@@ -149,25 +149,25 @@ module Nonnative
149
149
 
150
150
  def add_servers(cfg)
151
151
  servers = cfg.servers || []
152
- servers.each do |fd|
153
- server do |s|
154
- s.klass = Object.const_get(server_class_name(fd))
155
- runner_attributes(s, fd)
152
+ servers.each do |loaded_server|
153
+ server do |server_config|
154
+ server_config.klass = Object.const_get(server_class_name(loaded_server))
155
+ runner_attributes(server_config, loaded_server)
156
156
 
157
- proxy s, fd.proxy
157
+ assign_proxy(server_config, loaded_server.proxy)
158
158
  end
159
159
  end
160
160
  end
161
161
 
162
162
  def add_services(cfg)
163
163
  services = cfg.services || []
164
- services.each do |fd|
165
- service do |s|
166
- s.name = fd.name
167
- s.host = fd.host if fd.host
168
- s.port = fd.port
164
+ services.each do |loaded_service|
165
+ service do |service_config|
166
+ service_config.name = loaded_service.name
167
+ service_config.host = loaded_service.host if loaded_service.host
168
+ assign_service_port(service_config, loaded_service)
169
169
 
170
- proxy s, fd.proxy
170
+ assign_proxy(service_config, loaded_service.proxy)
171
171
  end
172
172
  end
173
173
  end
@@ -182,24 +182,38 @@ module Nonnative
182
182
  runner.timeout = loaded.timeout
183
183
  runner.wait = loaded.wait if loaded.wait
184
184
  runner.host = loaded.host if loaded.host
185
- runner.port = loaded.port
185
+ assign_ports(runner, loaded)
186
186
  runner.log = loaded.log if loaded.respond_to?(:log)
187
187
  end
188
188
 
189
- def proxy(runner, proxy)
190
- return unless proxy
189
+ def assign_ports(runner, loaded)
190
+ values = loaded.to_h
191
+ raise ArgumentError, "Use 'ports' instead of 'port' for runner '#{loaded.name}'" if values.key?(:port) || values.key?('port')
191
192
 
192
- p = {
193
- kind: proxy.kind,
194
- port: proxy.port,
195
- log: proxy.log,
196
- options: proxy.options
193
+ runner.ports = loaded.ports if loaded.ports
194
+ end
195
+
196
+ def assign_service_port(service, loaded)
197
+ values = loaded.to_h
198
+ raise ArgumentError, "Use 'port' instead of 'ports' for service '#{loaded.name}'" if values.key?(:ports) || values.key?('ports')
199
+
200
+ service.port = loaded.port if loaded.port
201
+ end
202
+
203
+ def assign_proxy(runner, loaded_proxy)
204
+ return unless loaded_proxy
205
+
206
+ proxy_attributes = {
207
+ kind: loaded_proxy.kind,
208
+ port: loaded_proxy.port,
209
+ log: loaded_proxy.log,
210
+ options: loaded_proxy.options
197
211
  }
198
212
 
199
- p[:host] = proxy.host if proxy.host
200
- p[:wait] = proxy.wait if proxy.wait
213
+ proxy_attributes[:host] = loaded_proxy.host if loaded_proxy.host
214
+ proxy_attributes[:wait] = loaded_proxy.wait if loaded_proxy.wait
201
215
 
202
- runner.proxy = p
216
+ runner.proxy = proxy_attributes
203
217
  end
204
218
  end
205
219
  end
@@ -19,7 +19,7 @@ module Nonnative
19
19
  # @return [String, nil] signal name to use for stopping (defaults to `"INT"` when not set)
20
20
  attr_accessor :signal
21
21
 
22
- # @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
22
+ # @return [Numeric] readiness timeout (seconds) used when waiting for ports to open/close
23
23
  attr_accessor :timeout
24
24
 
25
25
  # @return [String] log file path to append process stdout/stderr to
@@ -15,9 +15,12 @@ module Nonnative
15
15
  class ConfigurationRunner
16
16
  # @return [String, nil] runner name used for lookup (for example via `pool.process_by_name`)
17
17
  # @return [String] host to bind/connect to (defaults to `"127.0.0.1"`)
18
- # @return [Integer] port to bind/connect to
18
+ # @return [Array<Integer>] ports to bind/connect to
19
19
  # @return [Numeric] wait interval (seconds) used by runners between lifecycle steps
20
- attr_accessor :name, :host, :port, :wait
20
+ attr_accessor :name, :host, :wait
21
+
22
+ # @return [Array<Integer>] client-facing ports used for readiness/shutdown checks
23
+ attr_reader :ports
21
24
 
22
25
  # Proxy configuration for this runner.
23
26
  #
@@ -31,19 +34,37 @@ module Nonnative
31
34
  #
32
35
  # Defaults:
33
36
  # - `host`: `"127.0.0.1"`
34
- # - `port`: `0`
37
+ # - `ports`: `[0]`
35
38
  # - `wait`: `0.1`
36
39
  # - `proxy`: a new {Nonnative::ConfigurationProxy} with its own defaults
37
40
  #
38
41
  # @return [void]
39
42
  def initialize
40
43
  self.host = '127.0.0.1'
41
- self.port = 0
44
+ @ports = [0]
42
45
  self.wait = 0.1
43
46
 
44
47
  @proxy = Nonnative::ConfigurationProxy.new
45
48
  end
46
49
 
50
+ # Sets the client-facing ports for this runner.
51
+ #
52
+ # @param value [Array<Integer>] ports to check for readiness/shutdown
53
+ # @return [void]
54
+ def ports=(value)
55
+ @ports = Array(value)
56
+ end
57
+
58
+ # Returns the primary client-facing port.
59
+ #
60
+ # This preserves a single endpoint for proxy binding and client helpers while the public
61
+ # configuration contract uses {#ports}.
62
+ #
63
+ # @return [Integer]
64
+ def port
65
+ ports.first
66
+ end
67
+
47
68
  # Sets proxy configuration using a hash-like value.
48
69
  #
49
70
  # This is primarily used when loading YAML configuration files, where proxy attributes are
@@ -12,7 +12,7 @@ module Nonnative
12
12
  # @see Nonnative::Server
13
13
  class ConfigurationServer < ConfigurationRunner
14
14
  # @return [Class] a class that implements `#initialize(service)`, and lifecycle hooks expected by {Nonnative::Server}
15
- # @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
15
+ # @return [Numeric] readiness timeout (seconds) used when waiting for ports to open/close
16
16
  # @return [String] log file path used by server implementations (for example Puma/gRPC log files)
17
17
  attr_accessor :klass, :timeout, :log
18
18
  end
@@ -11,5 +11,33 @@ module Nonnative
11
11
  # @see Nonnative::Configuration
12
12
  # @see Nonnative::Service
13
13
  class ConfigurationService < ConfigurationRunner
14
+ # @return [Integer] client-facing port used by the service proxy
15
+ attr_accessor :port
16
+
17
+ # Creates a service configuration with defaults.
18
+ #
19
+ # @return [void]
20
+ def initialize
21
+ super
22
+
23
+ self.port = 0
24
+ end
25
+
26
+ # Services expose a single proxy listener, so plural runner ports are not supported.
27
+ #
28
+ # @return [void]
29
+ # @raise [ArgumentError] when plural service ports are read
30
+ def ports
31
+ raise ArgumentError, "Use 'port' instead of 'ports' for service '#{name}'"
32
+ end
33
+
34
+ # Services expose a single proxy listener, so plural runner ports are not supported.
35
+ #
36
+ # @param _value [Array<Integer>] ignored plural ports
37
+ # @return [void]
38
+ # @raise [ArgumentError] when plural service ports are assigned
39
+ def ports=(_value)
40
+ raise ArgumentError, "Use 'port' instead of 'ports' for service '#{name}'"
41
+ end
14
42
  end
15
43
  end
@@ -18,8 +18,8 @@ module Nonnative
18
18
  #
19
19
  # ## Wiring
20
20
  #
21
- # When enabled, your test/client should connect to the runner `host` / `port` (the proxy endpoint),
22
- # and the proxy will forward traffic to the upstream target exposed by {#host}:{#port}.
21
+ # When enabled, your test/client should connect to the runner `host` and primary port (the proxy
22
+ # endpoint), and the proxy will forward traffic to the upstream target exposed by {#host}:{#port}.
23
23
  #
24
24
  # ## Configuration
25
25
  #
@@ -62,7 +62,7 @@ module Nonnative
62
62
 
63
63
  # Starts the proxy accept loop in a background thread.
64
64
  #
65
- # This binds a TCP server on the underlying runner’s `service.host` / `service.port`.
65
+ # This binds a TCP server on the underlying runner’s `service.host` and primary port.
66
66
  # Clients connect to that runner endpoint, while upstream traffic is forwarded to {#host}:{#port}.
67
67
  #
68
68
  # @return [void]
@@ -34,7 +34,7 @@ module Nonnative
34
34
  # Binds the gRPC server and begins serving requests.
35
35
  #
36
36
  # The server binds to the upstream proxy host/port so the fault-injection proxy can expose the
37
- # runner host/port as the client-facing endpoint used by readiness checks.
37
+ # runner host and first configured port as the client-facing endpoint used by readiness checks.
38
38
  #
39
39
  # @return [void]
40
40
  def perform_start
@@ -50,9 +50,10 @@ module Nonnative
50
50
 
51
51
  # Executes the upstream request and returns the response.
52
52
  #
53
- # @param verb [String] HTTP verb name (e.g. `"get"`)
54
- # @param uri [String] upstream URI
55
- # @param opts [Hash] RestClient options (e.g. headers)
53
+ # @param method [Symbol] HTTP verb name (e.g. `:get`)
54
+ # @param url [String] upstream URL
55
+ # @param headers [Hash] request headers
56
+ # @param payload [String, nil] request payload
56
57
  # @return [RestClient::Response] response for error statuses, otherwise RestClient return value
57
58
  def api_response(method:, url:, headers:, payload: nil)
58
59
  options = { method:, url:, headers: }
@@ -109,7 +110,7 @@ module Nonnative
109
110
  # s.klass = Nonnative::Features::HTTPProxyServer
110
111
  # s.timeout = 2
111
112
  # s.host = '127.0.0.1'
112
- # s.port = 4567
113
+ # s.ports = [4567]
113
114
  # s.log = 'proxy.log'
114
115
  # end
115
116
  # end
@@ -20,7 +20,7 @@ module Nonnative
20
20
  # s.klass = ->(service) { Nonnative::HTTPServer.new(app, service) }
21
21
  # s.timeout = 2
22
22
  # s.host = '127.0.0.1'
23
- # s.port = 4567
23
+ # s.ports = [4567]
24
24
  # s.log = 'http.log'
25
25
  # end
26
26
  # end
@@ -49,7 +49,7 @@ module Nonnative
49
49
  # Binds the Puma server and begins serving.
50
50
  #
51
51
  # The listener binds to the upstream proxy host/port so the fault-injection proxy can expose the
52
- # runner host/port as the client-facing endpoint used by readiness checks.
52
+ # runner host and first configured port as the client-facing endpoint used by readiness checks.
53
53
  #
54
54
  # @return [void]
55
55
  def perform_start
@@ -66,6 +66,6 @@ module Nonnative
66
66
 
67
67
  private
68
68
 
69
- attr_reader :queue, :server
69
+ attr_reader :server
70
70
  end
71
71
  end
@@ -5,7 +5,7 @@ module Nonnative
5
5
  #
6
6
  # This is the default proxy when `service.proxy.kind` is `"none"` (or an unknown kind is provided).
7
7
  # It does not bind/listen or alter traffic; it simply exposes the underlying runner's configured
8
- # `host` and `port`.
8
+ # `host` and primary `port`.
9
9
  #
10
10
  # Runners can always call `start`, `stop`, and `reset` safely on this proxy.
11
11
  #
@@ -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 underlying runner configuration port.
53
+ # For {NoProxy}, this is the first underlying runner configuration port.
54
54
  #
55
55
  # @return [Integer]
56
56
  def port
@@ -9,7 +9,7 @@ module Nonnative
9
9
  # - On start: services first, then servers/processes (in parallel port-check threads)
10
10
  # - On stop: processes/servers first, then services
11
11
  #
12
- # Readiness and shutdown are determined via TCP port checks ({Nonnative::Port#open?} / {Nonnative::Port#closed?}).
12
+ # Readiness and shutdown are determined via TCP port checks ({Nonnative::Ports#open?} / {Nonnative::Ports#closed?}).
13
13
  #
14
14
  # @see Nonnative.start
15
15
  # @see Nonnative.stop
@@ -35,7 +35,7 @@ module Nonnative
35
35
  errors = []
36
36
 
37
37
  errors.concat(service_lifecycle(services, :start, :start))
38
- [servers, processes].each { |t| errors.concat(process(t, :start, :open?, :start, &)) }
38
+ [servers, processes].each { |runners| errors.concat(run_lifecycle_checks(runners, :start, :open?, :start, &)) }
39
39
 
40
40
  errors
41
41
  end
@@ -51,7 +51,7 @@ module Nonnative
51
51
  def stop(&)
52
52
  errors = []
53
53
 
54
- [processes, servers].each { |t| errors.concat(process(t, :stop, :closed?, :stop, &)) }
54
+ [processes, servers].each { |runners| errors.concat(run_lifecycle_checks(runners, :stop, :closed?, :stop, &)) }
55
55
  errors.concat(service_lifecycle(services, :stop, :stop))
56
56
 
57
57
  errors
@@ -69,7 +69,9 @@ module Nonnative
69
69
  def rollback(&)
70
70
  errors = []
71
71
 
72
- [existing_processes, existing_servers].each { |t| errors.concat(process(t, :stop, :closed?, :stop, &)) }
72
+ [existing_processes, existing_servers].each do |runners|
73
+ errors.concat(run_lifecycle_checks(runners, :stop, :closed?, :stop, &))
74
+ end
73
75
  errors.concat(service_lifecycle(existing_services, :stop, :stop))
74
76
 
75
77
  errors
@@ -129,7 +131,7 @@ module Nonnative
129
131
 
130
132
  @processes = []
131
133
  configuration.processes.each do |p|
132
- @processes << [Nonnative::Process.new(p), Nonnative::Port.new(p)]
134
+ @processes << [Nonnative::Process.new(p), Nonnative::Ports.new(p)]
133
135
  end
134
136
 
135
137
  @processes
@@ -140,7 +142,7 @@ module Nonnative
140
142
 
141
143
  @servers = []
142
144
  configuration.servers.each do |s|
143
- @servers << [s.klass.new(s), Nonnative::Port.new(s)]
145
+ @servers << [s.klass.new(s), Nonnative::Ports.new(s)]
144
146
  end
145
147
 
146
148
  @servers
@@ -177,15 +179,15 @@ module Nonnative
177
179
  end
178
180
  end
179
181
 
180
- def process(all, type_method, port_method, action, &)
182
+ def run_lifecycle_checks(runners, lifecycle_method, port_method, action, &)
181
183
  checks = []
182
184
  errors = []
183
185
 
184
- all.each do |type, port|
185
- values = type.send(type_method)
186
- checks << [type, values, Thread.new { check_port(port, port_method) }]
186
+ runners.each do |runner, port|
187
+ values = runner.send(lifecycle_method)
188
+ checks << [runner, values, Thread.new { check_port(port, port_method) }]
187
189
  rescue StandardError => e
188
- errors << lifecycle_error(action, type, e)
190
+ errors << lifecycle_error(action, runner, e)
189
191
  end
190
192
 
191
193
  errors.concat(yield_results(checks, action, &))
@@ -3,21 +3,23 @@
3
3
  module Nonnative
4
4
  # Performs TCP port readiness/shutdown checks for a configured runner.
5
5
  #
6
- # Nonnative uses this to decide whether a process/server is ready after start, and whether it has
7
- # shut down after stop. The checks repeatedly attempt to open a TCP connection to `process.host`
8
- # and `process.port` until either:
6
+ # Nonnative uses this to decide whether a process/server port is ready after start, and whether it
7
+ # has shut down after stop. The checks repeatedly attempt to open a TCP connection to `process.host`
8
+ # and the configured `port` until either:
9
9
  #
10
10
  # - the expected condition is met, or
11
11
  # - the configured timeout elapses (in which case the method returns `false`)
12
12
  #
13
13
  # The `process` argument is a runner configuration object (e.g. {Nonnative::ConfigurationProcess}
14
- # or {Nonnative::ConfigurationServer}) that responds to `host`, `port`, and `timeout`.
14
+ # or {Nonnative::ConfigurationServer}) that responds to `host` and `timeout`.
15
15
  #
16
16
  # @see Nonnative::Pool for how these checks are orchestrated during start/stop
17
17
  class Port
18
- # @param process [#host, #port, #timeout] runner configuration providing connection details
19
- def initialize(process)
18
+ # @param process [#host, #timeout] runner configuration providing connection details
19
+ # @param port [Integer] port to check
20
+ def initialize(process, port = process.port)
20
21
  @process = process
22
+ @port = port
21
23
  @timeout = Nonnative::Timeout.new(process.timeout)
22
24
  end
23
25
 
@@ -28,7 +30,7 @@ module Nonnative
28
30
  #
29
31
  # @return [Boolean] `true` if the port opened in time; otherwise `false`
30
32
  def open?
31
- Nonnative.logger.info "checking if port '#{process.port}' is open on host '#{process.host}'"
33
+ Nonnative.logger.info "checking if port '#{port}' is open on host '#{process.host}'"
32
34
 
33
35
  timeout.perform do
34
36
  open_socket
@@ -46,7 +48,7 @@ module Nonnative
46
48
  #
47
49
  # @return [Boolean] `true` if the port closed in time; otherwise `false`
48
50
  def closed?
49
- Nonnative.logger.info "checking if port '#{process.port}' is closed on host '#{process.host}'"
51
+ Nonnative.logger.info "checking if port '#{port}' is closed on host '#{process.host}'"
50
52
 
51
53
  timeout.perform do
52
54
  open_socket
@@ -61,10 +63,10 @@ module Nonnative
61
63
 
62
64
  private
63
65
 
64
- attr_reader :process, :timeout
66
+ attr_reader :process, :port, :timeout
65
67
 
66
68
  def open_socket
67
- TCPSocket.new(process.host, process.port).close
69
+ TCPSocket.new(process.host, port).close
68
70
  end
69
71
 
70
72
  def sleep_interval
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ # Performs aggregate TCP readiness/shutdown checks for all configured runner ports.
5
+ #
6
+ # A runner is considered ready only when every configured port is open, and stopped only when every
7
+ # configured port is closed.
8
+ #
9
+ # @see Nonnative::Port
10
+ class Ports
11
+ # @param runner [#host, #ports, #timeout] runner configuration providing connection details
12
+ def initialize(runner)
13
+ @ports = runner.ports.map { |port| Nonnative::Port.new(runner, port) }
14
+ end
15
+
16
+ # Returns whether all configured ports become connectable before their timeouts elapse.
17
+ #
18
+ # @return [Boolean]
19
+ def open?
20
+ ports.all?(&:open?)
21
+ end
22
+
23
+ # Returns whether all configured ports become non-connectable before their timeouts elapse.
24
+ #
25
+ # @return [Boolean]
26
+ def closed?
27
+ ports.all?(&:closed?)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :ports
33
+ end
34
+ end
@@ -65,18 +65,18 @@ module Nonnative
65
65
  ::TCPSocket.new(proxy.host, proxy.port)
66
66
  end
67
67
 
68
- # Pipes data from `socket1` to `socket2` if `socket1` is readable.
68
+ # Pipes data from `source_socket` to `destination_socket` when the source is readable.
69
69
  #
70
70
  # @param ready [Array<Array<IO>>] the result from `select`
71
- # @param socket1 [IO] readable side
72
- # @param socket2 [IO] writable side
71
+ # @param source_socket [IO] readable side
72
+ # @param destination_socket [IO] writable side
73
73
  # @return [Boolean] whether the piping loop should terminate
74
- def pipe?(ready, socket1, socket2)
75
- if ready[0].include?(socket1)
76
- data = read(socket1)
74
+ def pipe?(ready, source_socket, destination_socket)
75
+ if ready[0].include?(source_socket)
76
+ data = read(source_socket)
77
77
  return true if data.empty?
78
78
 
79
- write socket2, data
79
+ write destination_socket, data
80
80
  end
81
81
 
82
82
  false
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '2.23.0'
7
+ VERSION = '3.1.0'
8
8
  end
data/lib/nonnative.rb CHANGED
@@ -24,7 +24,7 @@
24
24
  # p.name = 'api'
25
25
  # p.command = -> { './bin/api' }
26
26
  # p.host = '127.0.0.1'
27
- # p.port = 8080
27
+ # p.ports = [8080, 9090]
28
28
  # p.timeout = 10
29
29
  # p.log = 'api.log'
30
30
  # end
@@ -68,6 +68,7 @@ require 'nonnative/stop_error'
68
68
  require 'nonnative/not_found_error'
69
69
  require 'nonnative/timeout'
70
70
  require 'nonnative/port'
71
+ require 'nonnative/ports'
71
72
  require 'nonnative/configuration_file'
72
73
  require 'nonnative/configuration'
73
74
  require 'nonnative/configuration_runner'
@@ -208,7 +209,7 @@ module Nonnative
208
209
 
209
210
  # Starts all configured services, servers, and processes, and waits for readiness.
210
211
  #
211
- # Readiness is determined by attempting to connect to each runner's configured host/port.
212
+ # Readiness is determined by attempting to connect to each runner's configured host/ports.
212
213
  #
213
214
  # @return [void]
214
215
  # @raise [Nonnative::StartError] if one or more runners fail to start or become ready in time
data/nonnative.gemspec CHANGED
@@ -11,7 +11,7 @@ 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'
14
+ spec.summary = 'Allows you to keep using the power of Ruby to test other systems'
15
15
  spec.description = spec.summary
16
16
  spec.homepage = 'https://github.com/alexfalkowski/nonnative'
17
17
  spec.license = 'MIT'
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: 2.23.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -263,7 +263,7 @@ 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: Allows you to keep using the power of Ruby to test other systems
267
267
  email:
268
268
  - alexrfalkowski@gmail.com
269
269
  executables: []
@@ -310,6 +310,7 @@ files:
310
310
  - lib/nonnative/observability.rb
311
311
  - lib/nonnative/pool.rb
312
312
  - lib/nonnative/port.rb
313
+ - lib/nonnative/ports.rb
313
314
  - lib/nonnative/process.rb
314
315
  - lib/nonnative/proxy.rb
315
316
  - lib/nonnative/proxy_factory.rb
@@ -348,5 +349,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
348
349
  requirements: []
349
350
  rubygems_version: 4.0.11
350
351
  specification_version: 4
351
- summary: Allows you to keep using the power of ruby to test other systems
352
+ summary: Allows you to keep using the power of Ruby to test other systems
352
353
  test_files: []