nonnative 1.107.0 → 1.109.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 +36 -6
  3. data/.rubocop.yml +8 -2
  4. data/AGENTS.md +248 -0
  5. data/CHANGELOG.md +141 -0
  6. data/Gemfile.lock +69 -58
  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/cucumber.rb +4 -12
  16. data/lib/nonnative/delay_socket_pair.rb +15 -0
  17. data/lib/nonnative/error.rb +7 -0
  18. data/lib/nonnative/fault_injection_proxy.rb +63 -0
  19. data/lib/nonnative/go_command.rb +34 -0
  20. data/lib/nonnative/grpc_server.rb +30 -0
  21. data/lib/nonnative/header.rb +44 -0
  22. data/lib/nonnative/http_client.rb +45 -0
  23. data/lib/nonnative/http_proxy_server.rb +62 -1
  24. data/lib/nonnative/http_server.rb +40 -0
  25. data/lib/nonnative/invalid_data_socket_pair.rb +15 -0
  26. data/lib/nonnative/no_proxy.rb +35 -0
  27. data/lib/nonnative/not_found_error.rb +7 -0
  28. data/lib/nonnative/observability.rb +44 -0
  29. data/lib/nonnative/pool.rb +50 -0
  30. data/lib/nonnative/port.rb +26 -0
  31. data/lib/nonnative/process.rb +29 -0
  32. data/lib/nonnative/proxy.rb +24 -0
  33. data/lib/nonnative/proxy_factory.rb +16 -0
  34. data/lib/nonnative/runner.rb +30 -0
  35. data/lib/nonnative/server.rb +28 -0
  36. data/lib/nonnative/service.rb +16 -0
  37. data/lib/nonnative/socket_pair.rb +46 -0
  38. data/lib/nonnative/socket_pair_factory.rb +21 -0
  39. data/lib/nonnative/start_error.rb +5 -0
  40. data/lib/nonnative/stop_error.rb +5 -0
  41. data/lib/nonnative/timeout.rb +20 -0
  42. data/lib/nonnative/version.rb +4 -1
  43. data/lib/nonnative.rb +128 -0
  44. metadata +3 -2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (1.107.0)
4
+ nonnative (1.109.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 11)
@@ -18,94 +18,102 @@ PATH
18
18
  GEM
19
19
  remote: https://rubygems.org/
20
20
  specs:
21
+ addressable (2.8.9)
22
+ public_suffix (>= 2.0.2, < 8.0)
21
23
  ast (2.4.3)
22
24
  base64 (0.3.0)
23
25
  benchmark-malloc (0.2.0)
24
26
  benchmark-perf (0.6.0)
25
27
  benchmark-trend (0.4.0)
26
- bigdecimal (3.2.3)
28
+ bigdecimal (4.0.1)
27
29
  builder (3.3.0)
28
- concurrent-ruby (1.3.5)
30
+ concurrent-ruby (1.3.6)
29
31
  config (5.6.1)
30
32
  deep_merge (~> 1.2, >= 1.2.1)
31
33
  ostruct
32
- cucumber (10.1.0)
34
+ cucumber (10.2.0)
33
35
  base64 (~> 0.2)
34
36
  builder (~> 3.2)
35
- cucumber-ci-environment (> 9, < 11)
37
+ cucumber-ci-environment (> 9, < 12)
36
38
  cucumber-core (> 15, < 17)
37
- cucumber-cucumber-expressions (> 17, < 19)
38
- cucumber-html-formatter (> 20.3, < 22)
39
+ cucumber-cucumber-expressions (> 17, < 20)
40
+ cucumber-html-formatter (> 21, < 23)
39
41
  diff-lcs (~> 1.5)
40
42
  logger (~> 1.6)
41
43
  mini_mime (~> 1.1)
42
44
  multi_test (~> 1.1)
43
45
  sys-uname (~> 1.3)
44
- cucumber-ci-environment (10.0.1)
45
- cucumber-core (15.2.1)
46
- cucumber-gherkin (> 27, < 33)
47
- cucumber-messages (> 26, < 30)
48
- cucumber-tag-expressions (> 5, < 7)
46
+ cucumber-ci-environment (11.0.0)
47
+ cucumber-core (16.2.0)
48
+ cucumber-gherkin (> 36, < 40)
49
+ cucumber-messages (> 31, < 33)
50
+ cucumber-tag-expressions (> 6, < 9)
49
51
  cucumber-cucumber-expressions (18.0.1)
50
52
  bigdecimal
51
- cucumber-gherkin (32.2.0)
52
- cucumber-messages (> 25, < 28)
53
- cucumber-html-formatter (21.14.0)
54
- cucumber-messages (> 19, < 28)
55
- cucumber-messages (27.2.0)
56
- cucumber-tag-expressions (6.1.2)
53
+ cucumber-gherkin (39.0.0)
54
+ cucumber-messages (>= 31, < 33)
55
+ cucumber-html-formatter (22.3.0)
56
+ cucumber-messages (> 23, < 33)
57
+ cucumber-messages (32.2.0)
58
+ cucumber-tag-expressions (8.1.0)
57
59
  deep_merge (1.2.2)
58
60
  diff-lcs (1.6.2)
59
61
  docile (1.4.1)
60
62
  domain_name (0.6.20240107)
61
- ffi (1.17.2-x86_64-darwin)
62
- ffi (1.17.2-x86_64-linux-gnu)
63
+ ffi (1.17.3-x86_64-darwin)
64
+ ffi (1.17.3-x86_64-linux-gnu)
63
65
  get_process_mem (1.0.0)
64
66
  bigdecimal (>= 2.0)
65
67
  ffi (~> 1.0)
66
- google-protobuf (4.32.0-x86_64-darwin)
68
+ google-protobuf (4.34.0-x86_64-darwin)
67
69
  bigdecimal
68
- rake (>= 13)
69
- google-protobuf (4.32.0-x86_64-linux-gnu)
70
+ rake (~> 13.3)
71
+ google-protobuf (4.34.0-x86_64-linux-gnu)
70
72
  bigdecimal
71
- rake (>= 13)
72
- googleapis-common-protos-types (1.21.0)
73
+ rake (~> 13.3)
74
+ googleapis-common-protos-types (1.22.0)
73
75
  google-protobuf (~> 4.26)
74
- grpc (1.74.1-x86_64-darwin)
76
+ grpc (1.78.1-x86_64-darwin)
75
77
  google-protobuf (>= 3.25, < 5.0)
76
78
  googleapis-common-protos-types (~> 1.0)
77
- grpc (1.74.1-x86_64-linux-gnu)
79
+ grpc (1.78.1-x86_64-linux-gnu)
78
80
  google-protobuf (>= 3.25, < 5.0)
79
81
  googleapis-common-protos-types (~> 1.0)
80
82
  http-accept (1.7.0)
81
- http-cookie (1.0.8)
83
+ http-cookie (1.1.0)
82
84
  domain_name (~> 0.5)
83
- json (2.13.2)
85
+ json (2.18.1)
86
+ json-schema (6.1.0)
87
+ addressable (~> 2.8)
88
+ bigdecimal (>= 3.1, < 5)
84
89
  language_server-protocol (3.17.0.5)
85
90
  lint_roller (1.1.0)
86
91
  logger (1.7.0)
92
+ mcp (0.8.0)
93
+ json-schema (>= 4.1)
87
94
  memoist3 (1.0.0)
88
95
  mime-types (3.7.0)
89
96
  logger
90
97
  mime-types-data (~> 3.2025, >= 3.2025.0507)
91
- mime-types-data (3.2025.0902)
98
+ mime-types-data (3.2026.0303)
92
99
  mini_mime (1.1.5)
93
100
  multi_test (1.1.0)
94
101
  mustermann (3.0.4)
95
102
  ruby2_keywords (~> 0.0.1)
96
103
  netrc (0.11.0)
97
- nio4r (2.7.4)
104
+ nio4r (2.7.5)
98
105
  ostruct (0.6.3)
99
106
  parallel (1.27.0)
100
- parser (3.3.9.0)
107
+ parser (3.3.10.2)
101
108
  ast (~> 2.4.1)
102
109
  racc
103
- prism (1.4.0)
104
- puma (7.0.0)
110
+ prism (1.9.0)
111
+ public_suffix (7.0.5)
112
+ puma (7.2.0)
105
113
  nio4r (~> 2.0)
106
114
  racc (1.8.1)
107
- rack (3.2.1)
108
- rack-protection (4.1.1)
115
+ rack (3.2.5)
116
+ rack-protection (4.2.1)
109
117
  base64 (>= 0.1.0)
110
118
  logger (>= 1.6.0)
111
119
  rack (>= 3.0.0, < 4)
@@ -113,18 +121,19 @@ GEM
113
121
  base64 (>= 0.1.0)
114
122
  rack (>= 3.0.0)
115
123
  rainbow (3.1.1)
116
- rake (13.3.0)
117
- rbs (3.9.4)
124
+ rake (13.3.1)
125
+ rbs (3.10.3)
118
126
  logger
119
- regexp_parser (2.11.2)
127
+ tsort
128
+ regexp_parser (2.11.3)
120
129
  rest-client (2.1.0)
121
130
  http-accept (>= 1.7.0, < 2.0)
122
131
  http-cookie (>= 1.0.2, < 2.0)
123
132
  mime-types (>= 1.16, < 4.0)
124
133
  netrc (~> 0.8)
125
- retriable (3.1.2)
126
- rexml (3.4.2)
127
- rspec (3.13.1)
134
+ retriable (3.4.1)
135
+ rexml (3.4.4)
136
+ rspec (3.13.2)
128
137
  rspec-core (~> 3.13.0)
129
138
  rspec-expectations (~> 3.13.0)
130
139
  rspec-mocks (~> 3.13.0)
@@ -133,32 +142,33 @@ GEM
133
142
  benchmark-perf (~> 0.6)
134
143
  benchmark-trend (~> 0.4)
135
144
  rspec (>= 3.0)
136
- rspec-core (3.13.5)
145
+ rspec-core (3.13.6)
137
146
  rspec-support (~> 3.13.0)
138
147
  rspec-expectations (3.13.5)
139
148
  diff-lcs (>= 1.2.0, < 2.0)
140
149
  rspec-support (~> 3.13.0)
141
- rspec-mocks (3.13.5)
150
+ rspec-mocks (3.13.8)
142
151
  diff-lcs (>= 1.2.0, < 2.0)
143
152
  rspec-support (~> 3.13.0)
144
- rspec-support (3.13.5)
153
+ rspec-support (3.13.7)
145
154
  rspec-wait (1.0.2)
146
155
  rspec (>= 3.4)
147
- rubocop (1.80.2)
156
+ rubocop (1.85.1)
148
157
  json (~> 2.3)
149
158
  language_server-protocol (~> 3.17.0.2)
150
159
  lint_roller (~> 1.1.0)
160
+ mcp (~> 0.6)
151
161
  parallel (~> 1.10)
152
162
  parser (>= 3.3.0.2)
153
163
  rainbow (>= 2.2.2, < 4.0)
154
164
  regexp_parser (>= 2.9.3, < 3.0)
155
- rubocop-ast (>= 1.46.0, < 2.0)
165
+ rubocop-ast (>= 1.49.0, < 2.0)
156
166
  ruby-progressbar (~> 1.7)
157
167
  unicode-display_width (>= 2.4.0, < 4.0)
158
- rubocop-ast (1.46.0)
168
+ rubocop-ast (1.49.0)
159
169
  parser (>= 3.3.7.2)
160
- prism (~> 1.4)
161
- ruby-lsp (0.26.1)
170
+ prism (~> 1.7)
171
+ ruby-lsp (0.26.8)
162
172
  language_server-protocol (~> 3.17.0)
163
173
  prism (>= 1.2, < 2.0)
164
174
  rbs (>= 3, < 5)
@@ -173,20 +183,21 @@ GEM
173
183
  simplecov (~> 0.19)
174
184
  simplecov-html (0.13.2)
175
185
  simplecov_json_formatter (0.1.4)
176
- sinatra (4.1.1)
186
+ sinatra (4.2.1)
177
187
  logger (>= 1.6.0)
178
188
  mustermann (~> 3.0)
179
189
  rack (>= 3.0.0, < 4)
180
- rack-protection (= 4.1.1)
190
+ rack-protection (= 4.2.1)
181
191
  rack-session (>= 2.0.0, < 3)
182
192
  tilt (~> 2.0)
183
- sys-uname (1.4.1)
193
+ sys-uname (1.5.0)
184
194
  ffi (~> 1.1)
185
195
  memoist3 (~> 1.0.0)
186
- tilt (2.6.1)
187
- unicode-display_width (3.1.5)
188
- unicode-emoji (~> 4.0, >= 4.0.4)
189
- unicode-emoji (4.0.4)
196
+ tilt (2.7.0)
197
+ tsort (0.2.0)
198
+ unicode-display_width (3.2.0)
199
+ unicode-emoji (~> 4.1)
200
+ unicode-emoji (4.2.0)
190
201
 
191
202
  PLATFORMS
192
203
  x86_64-darwin-21
@@ -202,4 +213,4 @@ DEPENDENCIES
202
213
  simplecov-cobertura
203
214
 
204
215
  BUNDLED WITH
205
- 2.6.9
216
+ 4.0.8
data/README.md CHANGED
@@ -5,15 +5,15 @@
5
5
 
6
6
  # Nonnative
7
7
 
8
- Do you love building microservices using different languages?
8
+ Nonnative is a Ruby-first harness for end-to-end testing of systems implemented in other languages.
9
9
 
10
- Do you love testing applications using [cucumber](https://cucumber.io/) with [ruby](https://www.ruby-lang.org/en/)?
10
+ It helps you:
11
+ - start **OS processes** (e.g. your Go/Java/Rust service binary),
12
+ - start **in-process Ruby servers** (e.g. small HTTP/TCP/gRPC fakes for dependencies),
13
+ - optionally start **proxies** in front of processes/servers/services for fault-injection,
14
+ - wait for readiness/shutdown using **TCP port checks**.
11
15
 
12
- Well so do I. The issue is that most languages the cucumber implementation is not always complete or you have to write a lot of code to get it working.
13
-
14
- So why not test the way you want and build the microservice how you want. These kind of tests will make sure your application is tested properly by going end-to-end.
15
-
16
- The way it works is it spawns processes or servers you configure and waits for it to start. Then you communicate with your microservice however you like (TCP, HTTP, gRPC, etc)
16
+ Once started, you can test however you like (TCP, HTTP, gRPC, etc).
17
17
 
18
18
  ## Installation
19
19
 
@@ -37,27 +37,38 @@ gem install nonnative
37
37
 
38
38
  ## Usage
39
39
 
40
- Configure nonnative with the following:
40
+ Nonnative is configured via {#Nonnative.configure} (programmatic) or `config.load_file(...)` (YAML).
41
+
42
+ High-level configuration fields:
43
+ - `version`: configuration version (example: `"1.0"`).
44
+ - `name`: logical system name (used by `Nonnative.observability` for `/<name>/healthz`, etc).
45
+ - `url`: base URL for observability queries (example: `http://localhost:4567`).
46
+ - `log`: path for the Nonnative logger output.
47
+ - `processes`: child processes to `spawn`.
48
+ - `servers`: in-process Ruby servers started in threads.
49
+ - `services`: external dependencies (proxy-only; no process/thread started by Nonnative).
41
50
 
42
- - The version of the configuration (1.0).
43
- - The name of the service.
44
- - The URL of the service.
45
- - A log file.
46
- - Process, Server or Service that you want to start.
47
- - A timeout value.
48
- - A time to wait.
49
- - Port to verify.
50
- - The class for servers.
51
- - The log for servers/processes
52
- - The strategy for processes, servers and services.
51
+ Runner fields (process/server/service):
52
+ - `timeout`: max time (seconds) for readiness/shutdown port checks.
53
+ - `wait`: small sleep (seconds) between lifecycle steps.
54
+ - `host`/`port`: address used for port checks; when a proxy is enabled, reads happen via the proxy.
55
+ - `log`: per-runner log file (used by process output redirection or server implementations).
53
56
 
54
- ### Strategy
57
+ ### Lifecycle strategies (Cucumber integration)
55
58
 
56
- The strategy can be one of the following values:
59
+ Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
60
+ - `@startup`: start before scenario; stop after scenario
61
+ - `@manual`: stop after scenario (start is expected to be triggered manually in steps)
62
+ - `@clear`: clears memoized configuration and pool before scenario
63
+ - `@reset`: resets proxies after scenario
57
64
 
58
- - startup - When we include `nonnative/startup`, it will start it once.
59
- - before - When we tag our features with `@startup` it will start and stop after the scenario.
60
- - manual - When we tag our features with `@manual` it will stop after the scenario.
65
+ If you want “start once per test run”, require:
66
+
67
+ ```ruby
68
+ require 'nonnative/startup'
69
+ ```
70
+
71
+ This calls `Nonnative.start` immediately and registers an `at_exit` stop.
61
72
 
62
73
  ### Processes
63
74
 
@@ -443,9 +454,9 @@ end
443
454
 
444
455
  ### Services
445
456
 
446
- A service is an external dependency to your system. This is usually an expensive process to start like a DB and you would prefer to be managed externally. Services are not really exciting by themselves, it's when we add proxies that they allow us to do some extra work.
457
+ 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).
447
458
 
448
- Setup it up programmatically:
459
+ Set it up programmatically:
449
460
 
450
461
  ```ruby
451
462
  require 'nonnative'
@@ -458,33 +469,37 @@ Nonnative.configure do |config|
458
469
 
459
470
  config.service do |s|
460
471
  s.name = 'postgres'
461
- p.port = 5432
472
+ s.host = '127.0.0.1'
473
+ s.port = 5432
462
474
  end
463
475
 
464
476
  config.service do |s|
465
477
  s.name = 'redis'
478
+ s.host = '127.0.0.1'
466
479
  s.port = 6379
467
480
  end
468
481
  end
469
482
  ```
470
483
 
471
- Setup it up through configuration:
484
+ Set it up through configuration (YAML):
472
485
 
473
486
  ```yaml
474
487
  version: "1.0"
475
488
  name: test
476
489
  url: http://localhost:4567
477
490
  log: nonnative.log
478
- processes:
491
+ services:
479
492
  -
480
493
  name: postgres
494
+ host: 127.0.0.1
481
495
  port: 5432
482
496
  -
483
497
  name: redis
498
+ host: 127.0.0.1
484
499
  port: 6379
485
500
  ```
486
501
 
487
- Then load the file with
502
+ Then load the file with:
488
503
 
489
504
  ```ruby
490
505
  require 'nonnative'
@@ -593,7 +608,7 @@ servers:
593
608
 
594
609
  ##### Proxies Services
595
610
 
596
- Setup it up programmatically:
611
+ Set it up programmatically:
597
612
 
598
613
  ```ruby
599
614
  require 'nonnative'
@@ -603,11 +618,15 @@ Nonnative.configure do |config|
603
618
  config.name = 'test'
604
619
  config.url = 'http://localhost:4567'
605
620
  config.log = 'nonnative.log'
606
- config.wait = 1
607
621
 
608
622
  config.service do |s|
623
+ s.name = 'redis'
624
+ s.host = '127.0.0.1'
625
+ s.port = 6379
626
+
609
627
  s.proxy = {
610
628
  kind: 'fault_injection',
629
+ host: '127.0.0.1',
611
630
  port: 20_000,
612
631
  log: 'proxy_server.log',
613
632
  wait: 1,
@@ -1,7 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Socket-pair variant used by the fault-injection proxy to simulate immediate connection failure.
5
+ #
6
+ # When active, the proxy accepts a TCP connection and closes it immediately without forwarding any
7
+ # bytes to the upstream service.
8
+ #
9
+ # This behavior is enabled by calling {Nonnative::FaultInjectionProxy#close_all}.
10
+ #
11
+ # @see Nonnative::FaultInjectionProxy
12
+ # @see Nonnative::SocketPairFactory
13
+ # @see Nonnative::SocketPair
4
14
  class CloseAllSocketPair < SocketPair
15
+ # Closes the accepted socket immediately.
16
+ #
17
+ # @param local_socket [TCPSocket] the accepted client socket
18
+ # @return [void]
5
19
  def connect(local_socket)
6
20
  Nonnative.logger.info "closing socket '#{local_socket.inspect}' for 'close_all' pair"
7
21
 
@@ -1,15 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # The gem configuration object.
5
+ #
6
+ # You can populate configuration either programmatically via the DSL ({#process}, {#server}, {#service}),
7
+ # or by loading a YAML file via {#load_file}.
8
+ #
9
+ # The configuration is consumed when {Nonnative.start} is called.
10
+ #
11
+ # == Programmatic configuration
12
+ #
13
+ # Nonnative.configure do |config|
14
+ # config.name = 'example'
15
+ # config.url = 'http://127.0.0.1:8080'
16
+ # config.log = 'test.log'
17
+ #
18
+ # config.process do |p|
19
+ # p.name = 'api'
20
+ # p.command = -> { './bin/api' }
21
+ # p.host = '127.0.0.1'
22
+ # p.port = 8080
23
+ # p.timeout = 10
24
+ # p.log = 'api.log'
25
+ # end
26
+ # end
27
+ #
28
+ # == File-based configuration
29
+ #
30
+ # Nonnative.configure do |config|
31
+ # config.load_file('features/configs/processes.yml')
32
+ # end
33
+ #
4
34
  class Configuration
35
+ # Creates an empty configuration.
36
+ #
37
+ # @return [void]
5
38
  def initialize
6
39
  @processes = []
7
40
  @servers = []
8
41
  @services = []
9
42
  end
10
43
 
44
+ # @return [String, nil] logical system name (used for observability endpoints)
45
+ # @return [String, nil] configuration version
46
+ # @return [String, nil] base URL for observability queries (for example `"http://127.0.0.1:8080"`)
47
+ # @return [String, nil] path to the Nonnative log file
48
+ # @return [Array<Nonnative::ConfigurationProcess>] configured processes
49
+ # @return [Array<Nonnative::ConfigurationServer>] configured in-process servers
50
+ # @return [Array<Nonnative::ConfigurationService>] configured services (proxy-only)
11
51
  attr_accessor :name, :version, :url, :log, :processes, :servers, :services
12
52
 
53
+ # Loads a configuration file and appends its runners to this instance.
54
+ #
55
+ # The file is loaded using the `config` gem via {Nonnative.configurations}. Top-level attributes are
56
+ # copied onto this object, and runner sections are transformed into configuration runner objects.
57
+ #
58
+ # @param path [String] path to a configuration file (typically YAML)
59
+ # @return [void]
13
60
  def load_file(path)
14
61
  cfg = Nonnative.configurations(path)
15
62
 
@@ -23,6 +70,10 @@ module Nonnative
23
70
  add_services(cfg)
24
71
  end
25
72
 
73
+ # Adds a process configuration entry.
74
+ #
75
+ # @yieldparam process [Nonnative::ConfigurationProcess]
76
+ # @return [void]
26
77
  def process
27
78
  process = Nonnative::ConfigurationProcess.new
28
79
  yield process
@@ -30,6 +81,10 @@ module Nonnative
30
81
  processes << process
31
82
  end
32
83
 
84
+ # Adds a server configuration entry.
85
+ #
86
+ # @yieldparam server [Nonnative::ConfigurationServer]
87
+ # @return [void]
33
88
  def server
34
89
  server = Nonnative::ConfigurationServer.new
35
90
  yield server
@@ -37,6 +92,13 @@ module Nonnative
37
92
  servers << server
38
93
  end
39
94
 
95
+ # Adds a service configuration entry.
96
+ #
97
+ # A "service" does not manage a Ruby thread or OS process; it exists so that a proxy can be started
98
+ # and controlled for an external dependency.
99
+ #
100
+ # @yieldparam service [Nonnative::ConfigurationService]
101
+ # @return [void]
40
102
  def service
41
103
  service = Nonnative::ConfigurationService.new
42
104
  yield service
@@ -44,6 +106,11 @@ module Nonnative
44
106
  services << service
45
107
  end
46
108
 
109
+ # Finds a configured process by name.
110
+ #
111
+ # @param name [String]
112
+ # @return [Nonnative::ConfigurationProcess]
113
+ # @raise [Nonnative::NotFoundError] if no matching process exists
47
114
  def process_by_name(name)
48
115
  process = processes.find { |s| s.name == name }
49
116
  raise NotFoundError, "Could not find process with name '#{name}'" if process.nil?
@@ -1,7 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Process-specific configuration.
5
+ #
6
+ # A "process" is an OS-level child process started via `spawn` and stopped via signals.
7
+ # It is managed by {Nonnative::Process} at runtime.
8
+ #
9
+ # Instances are usually created through {Nonnative::Configuration#process}.
10
+ #
11
+ # @see Nonnative::Configuration
12
+ # @see Nonnative::Process
4
13
  class ConfigurationProcess < ConfigurationRunner
14
+ # @return [Proc] a callable that returns the command string to execute (e.g. `-> { "./bin/api" }`)
15
+ # @return [String, nil] signal name to use for stopping (defaults to `"INT"` when not set)
16
+ # @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
17
+ # @return [String] log file path to append process stdout/stderr to
18
+ # @return [Hash, nil] environment variables to pass to the spawned process
5
19
  attr_accessor :command, :signal, :timeout, :log, :environment
6
20
  end
7
21
  end
@@ -1,9 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Proxy configuration attached to a runner configuration.
5
+ #
6
+ # A proxy allows you to interpose behavior between a client and a real service. For example,
7
+ # the built-in `"fault_injection"` proxy can close connections, introduce delays, or corrupt data
8
+ # for resilience testing.
9
+ #
10
+ # This object is created automatically for each runner via {Nonnative::ConfigurationRunner}.
11
+ # When `kind` is set to `"none"`, no proxy is started and the runner will use its configured
12
+ # `host`/`port` directly.
13
+ #
14
+ # @see Nonnative::ConfigurationRunner#proxy
15
+ # @see Nonnative.proxies
4
16
  class ConfigurationProxy
17
+ # @return [String] proxy kind name (for example `"none"` or `"fault_injection"`)
18
+ # @return [String] proxy bind host (defaults to `"0.0.0.0"`)
19
+ # @return [Integer] proxy bind port (defaults to `0`)
20
+ # @return [String, nil] path to proxy log file (implementation-dependent)
21
+ # @return [Numeric] wait interval (seconds) after proxy state changes (defaults to `0.1`)
22
+ # @return [Hash] proxy implementation options (implementation-dependent)
5
23
  attr_accessor :kind, :host, :port, :log, :wait, :options
6
24
 
25
+ # Creates a proxy configuration with defaults.
26
+ #
27
+ # Defaults:
28
+ # - `kind`: `"none"`
29
+ # - `host`: `"0.0.0.0"`
30
+ # - `port`: `0`
31
+ # - `wait`: `0.1`
32
+ # - `options`: `{}`
33
+ #
34
+ # @return [void]
7
35
  def initialize
8
36
  self.kind = 'none'
9
37
  self.host = '0.0.0.0'
@@ -1,10 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Base configuration for a runnable unit managed by Nonnative.
5
+ #
6
+ # This class holds connection and timing attributes common to processes, servers and services,
7
+ # as well as a nested {Nonnative::ConfigurationProxy} describing how/if a proxy should be started.
8
+ #
9
+ # Instances of this type are typically created via {Nonnative::Configuration#process},
10
+ # {Nonnative::Configuration#server}, or {Nonnative::Configuration#service}.
11
+ #
12
+ # @see Nonnative::ConfigurationProcess
13
+ # @see Nonnative::ConfigurationServer
14
+ # @see Nonnative::ConfigurationService
4
15
  class ConfigurationRunner
16
+ # @return [String, nil] runner name used for lookup (for example via `pool.process_by_name`)
17
+ # @return [String] host to bind/connect to (defaults to `"0.0.0.0"`)
18
+ # @return [Integer] port to bind/connect to
19
+ # @return [Numeric] wait interval (seconds) used by runners between lifecycle steps
5
20
  attr_accessor :name, :host, :port, :wait
21
+
22
+ # Proxy configuration for this runner.
23
+ #
24
+ # Note that this returns a configuration object even if no proxy is enabled; by default
25
+ # the proxy kind is `"none"`.
26
+ #
27
+ # @return [Nonnative::ConfigurationProxy]
6
28
  attr_reader :proxy
7
29
 
30
+ # Creates a runner configuration with defaults.
31
+ #
32
+ # Defaults:
33
+ # - `host`: `"0.0.0.0"`
34
+ # - `port`: `0`
35
+ # - `wait`: `0.1`
36
+ # - `proxy`: a new {Nonnative::ConfigurationProxy} with its own defaults
37
+ #
38
+ # @return [void]
8
39
  def initialize
9
40
  self.host = '0.0.0.0'
10
41
  self.port = 0
@@ -13,6 +44,19 @@ module Nonnative
13
44
  @proxy = Nonnative::ConfigurationProxy.new
14
45
  end
15
46
 
47
+ # Sets proxy configuration using a hash-like value.
48
+ #
49
+ # This is primarily used when loading YAML configuration files, where proxy attributes are
50
+ # represented as scalar values.
51
+ #
52
+ # @param value [Hash] proxy attributes
53
+ # @option value [String] :kind proxy kind name (for example `"fault_injection"`)
54
+ # @option value [String] :host proxy bind host (optional)
55
+ # @option value [Integer] :port proxy bind port
56
+ # @option value [String] :log proxy log file path
57
+ # @option value [Numeric] :wait wait interval (seconds) after state changes (optional)
58
+ # @option value [Hash] :options proxy implementation specific options
59
+ # @return [void]
16
60
  def proxy=(value)
17
61
  proxy.kind = value[:kind]
18
62
  proxy.host = value[:host] if value[:host]
@@ -1,7 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
+ # Server-specific configuration.
5
+ #
6
+ # A "server" is an in-process Ruby component started in a background thread and stopped via a
7
+ # server-specific shutdown routine. It is managed by {Nonnative::Server} at runtime.
8
+ #
9
+ # Instances are usually created through {Nonnative::Configuration#server}.
10
+ #
11
+ # @see Nonnative::Configuration
12
+ # @see Nonnative::Server
4
13
  class ConfigurationServer < ConfigurationRunner
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
16
+ # @return [String] log file path used by server implementations (for example Puma/gRPC log files)
5
17
  attr_accessor :klass, :timeout, :log
6
18
  end
7
19
  end