nonnative 3.0.0 → 3.2.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: 786062cd10bbaba4298dd41abc683bc7a5fd769ea7c41a05e39a9f41bc2c5d96
4
- data.tar.gz: 1d0e8303858861f849aacbcb54ba2bac1f5a8336fdb74c44d09cb48881658c9f
3
+ metadata.gz: 3015cc7b1f784a45dbb77184fd73e67e6cf33ff9d7bb97d09ebd01f1359d85b9
4
+ data.tar.gz: 9bf8f2fc9bb72f8de89e76f44912d62e8a7cd19ccb533c7930600fbc70411b28
5
5
  SHA512:
6
- metadata.gz: 729e1141493b2b21c5c9014216a73aade7f7ec1ab03686ab581cbcb1b1c28d2d469a4fd5e416bcd391c2f8056725433a0befe40c283357166b0743f3f4f2d834
7
- data.tar.gz: e17dfe6bd69bbf4d0faf76d0b37502a32abf1ca0943d24f261c4c32301be86fff18e302f65dc9af32ac499ca73d5fe4a08ede17422d2de715a15b4c986ea64c7
6
+ metadata.gz: 73147dafad8a271673d070f033a1a3f92270994b0593bb7f391d0ae126ed0e34005cfd22af9e01052846bd757b866fba9db4d36a3468219be54208e8121a5f80
7
+ data.tar.gz: ad58bc0015ccdf3f91f9ffbb5ca12de3bd9650d540ef33ec982df353abd6892c496328815f60991e340721971e1bfbf86a663292337a755280d1157c2ff9d5be
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (3.0.0)
4
+ nonnative (3.2.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
data/README.md CHANGED
@@ -10,7 +10,7 @@ Nonnative is a Ruby-first harness for end-to-end testing of systems implemented
10
10
  It helps you:
11
11
  - start **OS processes** (e.g. your Go/Java/Rust service binary),
12
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,
13
+ - optionally start **service proxies** for fault-injection in front of externally managed dependencies,
14
14
  - wait for readiness/shutdown using **TCP port checks**.
15
15
 
16
16
  Once started, you can test however you like (TCP, HTTP, gRPC, etc).
@@ -54,23 +54,22 @@ High-level configuration fields:
54
54
  - `log`: path for the Nonnative logger output.
55
55
  - `processes`: child processes to `spawn`.
56
56
  - `servers`: in-process Ruby servers started in threads.
57
- - `services`: external dependencies (proxy-only; no process/thread started by Nonnative).
57
+ - `services`: external dependencies (no process/thread started by Nonnative).
58
58
 
59
59
  Common runner fields:
60
60
  - `name`: runner name used for lookup.
61
- - `host`/`ports`: 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, clients should hit the first configured port.
61
+ - `host`: client-facing host. Defaults to `127.0.0.1`.
62
62
 
63
63
  Process/server fields:
64
+ - `ports`: client-facing ports. These are also used for readiness/shutdown port checks.
64
65
  - `timeout`: max time (seconds) for readiness/shutdown port checks.
65
66
  - `wait`: small sleep (seconds) between lifecycle steps.
66
67
  - `log`: per-runner log file used by process output redirection or server implementations.
67
68
 
68
- 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`.
69
+ Service fields:
70
+ - `port`: client-facing service port. Services do not get TCP readiness/shutdown checks from Nonnative.
69
71
 
70
- > [!IMPORTANT]
71
- > When a proxy is enabled, tests and clients connect to the runner `host` and first configured `ports` entry; the nested `proxy.host`/`proxy.port` is the upstream target behind the proxy.
72
-
73
- 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`/`ports` endpoint, results are undefined.
72
+ 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.
74
73
 
75
74
  > [!WARNING]
76
75
  > 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.
@@ -248,7 +247,7 @@ module Nonnative
248
247
  def initialize(service)
249
248
  super
250
249
 
251
- @socket_server = ::TCPServer.new(proxy.host, proxy.port)
250
+ @socket_server = ::TCPServer.new(service.host, service.port)
252
251
  end
253
252
 
254
253
  def perform_start
@@ -405,9 +404,9 @@ Nonnative.configure do |config|
405
404
  end
406
405
  ```
407
406
 
408
- ##### 🔀 Proxy
407
+ ##### 🔀 HTTP Forward Proxy
409
408
 
410
- The system allows you to define an HTTP proxy for external systems, e.g. `api.github.com`.
409
+ The system allows you to define an in-process HTTP forward proxy server for external systems, e.g. `api.github.com`. This is a server implementation, not a fault-injection service proxy.
411
410
 
412
411
  Define your server:
413
412
 
@@ -543,9 +542,9 @@ end
543
542
 
544
543
  ### 🧩 Services
545
544
 
546
- 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).
545
+ A service is an external dependency to your system that you **do not** want Nonnative to start (no OS process, no Ruby thread).
547
546
 
548
- 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.
547
+ Services do not get process lifecycle management or TCP readiness/shutdown checks from Nonnative. They provide a named endpoint for a dependency that another tool already manages.
549
548
 
550
549
  Set it up programmatically:
551
550
 
@@ -561,13 +560,13 @@ Nonnative.configure do |config|
561
560
  config.service do |s|
562
561
  s.name = 'postgres'
563
562
  s.host = '127.0.0.1'
564
- s.ports = [5432]
563
+ s.port = 5432
565
564
  end
566
565
 
567
566
  config.service do |s|
568
567
  s.name = 'redis'
569
568
  s.host = '127.0.0.1'
570
- s.ports = [6379]
569
+ s.port = 6379
571
570
  end
572
571
  end
573
572
  ```
@@ -583,13 +582,11 @@ services:
583
582
  -
584
583
  name: postgres
585
584
  host: 127.0.0.1
586
- ports:
587
- - 5432
585
+ port: 5432
588
586
  -
589
587
  name: redis
590
588
  host: 127.0.0.1
591
- ports:
592
- - 6379
589
+ port: 6379
593
590
  ```
594
591
 
595
592
  Then load the file with:
@@ -618,159 +615,43 @@ Custom proxy kinds can be registered through `Nonnative.proxies`:
618
615
  Nonnative.proxies['custom'] = CustomProxy
619
616
  ```
620
617
 
621
- 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.
622
-
623
- ##### ⚙️ Process Proxies
624
-
625
- Add this to an existing process configuration:
626
-
627
- ```ruby
628
- require 'nonnative'
629
-
630
- Nonnative.configure do |config|
631
- config.version = '1.0'
632
- config.name = 'test'
633
- config.url = 'http://localhost:4567'
634
- config.log = 'nonnative.log'
635
-
636
- config.process do |p|
637
- p.host = '127.0.0.1'
638
- p.ports = [20_000]
639
-
640
- p.proxy = {
641
- kind: 'fault_injection',
642
- host: '127.0.0.1',
643
- port: 12_321,
644
- log: 'proxy_server.log',
645
- wait: 1,
646
- options: {
647
- delay: 5
648
- }
649
- }
650
- end
651
- end
652
- ```
653
-
654
- YAML fragment:
618
+ Only services support proxies. For `fault_injection`, keep the service `host`/`port` as the client-facing proxy endpoint and use nested `proxy.host`/`proxy.port` for the upstream target behind the proxy.
655
619
 
656
- ```yaml
657
- version: "1.0"
658
- name: test
659
- url: http://localhost:4567
660
- log: nonnative.log
661
- processes:
662
- -
663
- host: 127.0.0.1
664
- ports:
665
- - 20000
666
- proxy:
667
- kind: fault_injection
668
- host: 127.0.0.1
669
- port: 12321
670
- log: proxy_server.log
671
- wait: 1
672
- options:
673
- delay: 5
674
- ```
620
+ ##### 🧩 Service Proxies
675
621
 
676
- ##### 🖥️ Server Proxies
622
+ ###### Programmatic Configuration
677
623
 
678
- Add this to an existing server configuration:
624
+ Add a proxy to a service configuration:
679
625
 
680
626
  ```ruby
681
- require 'nonnative'
682
-
683
- Nonnative.configure do |config|
684
- config.version = '1.0'
685
- config.name = 'test'
686
- config.url = 'http://localhost:4567'
687
- config.log = 'nonnative.log'
688
-
689
- config.server do |s|
690
- s.host = '127.0.0.1'
691
- s.ports = [20_000]
692
-
693
- s.proxy = {
694
- kind: 'fault_injection',
695
- host: '127.0.0.1',
696
- port: 12_321,
697
- log: 'proxy_server.log',
698
- wait: 1,
699
- options: {
700
- delay: 5
701
- }
627
+ config.service do |s|
628
+ s.name = 'redis'
629
+ s.host = '127.0.0.1'
630
+ s.port = 16_379
631
+
632
+ s.proxy = {
633
+ kind: 'fault_injection',
634
+ host: '127.0.0.1',
635
+ port: 6379,
636
+ log: 'proxy_server.log',
637
+ wait: 1,
638
+ options: {
639
+ delay: 5
702
640
  }
703
- end
641
+ }
704
642
  end
705
643
  ```
706
644
 
707
- YAML fragment:
708
-
709
- ```yaml
710
- version: "1.0"
711
- name: test
712
- url: http://localhost:4567
713
- log: nonnative.log
714
- servers:
715
- -
716
- host: 127.0.0.1
717
- ports:
718
- - 20000
719
- proxy:
720
- kind: fault_injection
721
- host: 127.0.0.1
722
- port: 12321
723
- log: proxy_server.log
724
- wait: 1
725
- options:
726
- delay: 5
727
- ```
728
-
729
- ##### 🧩 Service Proxies
730
-
731
- Add this to an existing service configuration:
732
-
733
- ```ruby
734
- require 'nonnative'
735
-
736
- Nonnative.configure do |config|
737
- config.version = '1.0'
738
- config.name = 'test'
739
- config.url = 'http://localhost:4567'
740
- config.log = 'nonnative.log'
741
-
742
- config.service do |s|
743
- s.name = 'redis'
744
- s.host = '127.0.0.1'
745
- s.ports = [16_379]
746
-
747
- s.proxy = {
748
- kind: 'fault_injection',
749
- host: '127.0.0.1',
750
- port: 6379,
751
- log: 'proxy_server.log',
752
- wait: 1,
753
- options: {
754
- delay: 5
755
- }
756
- }
757
- end
758
- end
759
- ```
645
+ ###### YAML Configuration
760
646
 
761
- YAML fragment:
647
+ Add a proxy to a service YAML entry:
762
648
 
763
649
  ```yaml
764
- version: "1.0"
765
- name: test
766
- url: http://localhost:4567
767
- log: nonnative.log
768
650
  services:
769
651
  -
770
652
  name: redis
771
653
  host: 127.0.0.1
772
- ports:
773
- - 16379
654
+ port: 16379
774
655
  proxy:
775
656
  kind: fault_injection
776
657
  host: 127.0.0.1
@@ -785,53 +666,15 @@ services:
785
666
 
786
667
  The `fault_injection` proxy allows you to simulate failures by injecting them. We currently support the following:
787
668
 
788
- Clients connect to the runner `host` and first configured `ports` entry, while the proxy forwards traffic to nested `proxy.host`/`proxy.port`.
669
+ Clients connect to the service `host`/`port`, while the proxy forwards traffic to nested `proxy.host`/`proxy.port`.
789
670
 
790
671
  - `close_all` - Closes the socket as soon as it connects.
791
672
  - `delay` - Delays traffic on the connection. Defaults to 2 seconds and can be configured through options.
792
673
  - `invalid_data` - Forwards client requests unchanged, then corrupts upstream responses before they reach the client.
793
674
 
794
- ###### ⚙️ Fault Injection Processes
795
-
796
- Set it up programmatically:
797
-
798
- ```ruby
799
- name = 'name of process in configuration'
800
- server = Nonnative.pool.process_by_name(name)
801
-
802
- server.proxy.close_all # To use close_all.
803
- server.proxy.reset # To reset it back to a good state.
804
- ```
805
-
806
- With cucumber:
807
-
808
- ```cucumber
809
- Given I set the proxy for process 'process_1' to 'close_all'
810
- Then I should reset the proxy for process 'process_1'
811
- ```
812
-
813
- ###### 🖥️ Fault Injection Servers
814
-
815
- Set it up programmatically:
816
-
817
- ```ruby
818
- name = 'name of server in configuration'
819
- server = Nonnative.pool.server_by_name(name)
820
-
821
- server.proxy.close_all # To use close_all.
822
- server.proxy.reset # To reset it back to a good state.
823
- ```
824
-
825
- With cucumber:
826
-
827
- ```cucumber
828
- Given I set the proxy for server 'server_1' to 'close_all'
829
- Then I should reset the proxy for server 'server_1'
830
- ```
831
-
832
675
  ###### 🧩 Fault Injection Services
833
676
 
834
- Set it up programmatically:
677
+ Set the proxy state programmatically:
835
678
 
836
679
  ```ruby
837
680
  name = 'name of service in configuration'
@@ -841,7 +684,7 @@ service.proxy.close_all # To use close_all.
841
684
  service.proxy.reset # To reset it back to a good state.
842
685
  ```
843
686
 
844
- With cucumber:
687
+ Use the Cucumber proxy steps:
845
688
 
846
689
  ```cucumber
847
690
  Given I set the proxy for service 'service_1' to 'close_all'
@@ -124,13 +124,13 @@ module Nonnative
124
124
  def add_processes(cfg)
125
125
  processes = cfg.processes || []
126
126
  processes.each do |loaded_process|
127
+ reject_proxy(loaded_process, 'processes')
128
+
127
129
  process do |process_config|
128
130
  process_config.command = command(loaded_process)
129
131
  process_config.signal = loaded_process.signal
130
132
  process_config.environment = loaded_process.environment
131
133
  runner_attributes(process_config, loaded_process)
132
-
133
- assign_proxy(process_config, loaded_process.proxy)
134
134
  end
135
135
  end
136
136
  end
@@ -150,11 +150,11 @@ module Nonnative
150
150
  def add_servers(cfg)
151
151
  servers = cfg.servers || []
152
152
  servers.each do |loaded_server|
153
+ reject_proxy(loaded_server, 'servers')
154
+
153
155
  server do |server_config|
154
156
  server_config.klass = Object.const_get(server_class_name(loaded_server))
155
157
  runner_attributes(server_config, loaded_server)
156
-
157
- assign_proxy(server_config, loaded_server.proxy)
158
158
  end
159
159
  end
160
160
  end
@@ -165,7 +165,7 @@ module Nonnative
165
165
  service do |service_config|
166
166
  service_config.name = loaded_service.name
167
167
  service_config.host = loaded_service.host if loaded_service.host
168
- assign_ports(service_config, loaded_service)
168
+ assign_service_port(service_config, loaded_service)
169
169
 
170
170
  assign_proxy(service_config, loaded_service.proxy)
171
171
  end
@@ -193,7 +193,21 @@ module Nonnative
193
193
  runner.ports = loaded.ports if loaded.ports
194
194
  end
195
195
 
196
- def assign_proxy(runner, loaded_proxy)
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 reject_proxy(loaded, kind)
204
+ values = loaded.to_h
205
+ return unless values.key?(:proxy) || values.key?('proxy')
206
+
207
+ raise ArgumentError, "Use 'services' for proxy configuration; #{kind} do not support 'proxy'"
208
+ end
209
+
210
+ def assign_proxy(service, loaded_proxy)
197
211
  return unless loaded_proxy
198
212
 
199
213
  proxy_attributes = {
@@ -206,7 +220,7 @@ module Nonnative
206
220
  proxy_attributes[:host] = loaded_proxy.host if loaded_proxy.host
207
221
  proxy_attributes[:wait] = loaded_proxy.wait if loaded_proxy.wait
208
222
 
209
- runner.proxy = proxy_attributes
223
+ service.proxy = proxy_attributes
210
224
  end
211
225
  end
212
226
  end
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- # Proxy configuration attached to a runner configuration.
4
+ # Proxy configuration attached to a service configuration.
5
5
  #
6
6
  # A proxy allows you to interpose behavior between a client and a real service. For example,
7
7
  # the built-in `"fault_injection"` proxy can close connections, introduce delays, or corrupt data
8
8
  # for resilience testing.
9
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
10
+ # This object is created automatically for each service via {Nonnative::ConfigurationService}.
11
+ # When `kind` is set to `"none"`, no proxy is started and the service will use its configured
12
12
  # `host`/`port` directly.
13
13
  #
14
- # @see Nonnative::ConfigurationRunner#proxy
14
+ # @see Nonnative::ConfigurationService#proxy
15
15
  # @see Nonnative.proxies
16
16
  class ConfigurationProxy
17
17
  # @return [String] proxy kind name (for example `"none"` or `"fault_injection"`)
@@ -3,8 +3,7 @@
3
3
  module Nonnative
4
4
  # Base configuration for a runnable unit managed by Nonnative.
5
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.
6
+ # This class holds connection and timing attributes common to processes, servers and services.
8
7
  #
9
8
  # Instances of this type are typically created via {Nonnative::Configuration#process},
10
9
  # {Nonnative::Configuration#server}, or {Nonnative::Configuration#service}.
@@ -22,29 +21,18 @@ module Nonnative
22
21
  # @return [Array<Integer>] client-facing ports used for readiness/shutdown checks
23
22
  attr_reader :ports
24
23
 
25
- # Proxy configuration for this runner.
26
- #
27
- # Note that this returns a configuration object even if no proxy is enabled; by default
28
- # the proxy kind is `"none"`.
29
- #
30
- # @return [Nonnative::ConfigurationProxy]
31
- attr_reader :proxy
32
-
33
24
  # Creates a runner configuration with defaults.
34
25
  #
35
26
  # Defaults:
36
27
  # - `host`: `"127.0.0.1"`
37
28
  # - `ports`: `[0]`
38
29
  # - `wait`: `0.1`
39
- # - `proxy`: a new {Nonnative::ConfigurationProxy} with its own defaults
40
30
  #
41
31
  # @return [void]
42
32
  def initialize
43
33
  self.host = '127.0.0.1'
44
- self.ports = [0]
34
+ @ports = [0]
45
35
  self.wait = 0.1
46
-
47
- @proxy = Nonnative::ConfigurationProxy.new
48
36
  end
49
37
 
50
38
  # Sets the client-facing ports for this runner.
@@ -57,34 +45,11 @@ module Nonnative
57
45
 
58
46
  # Returns the primary client-facing port.
59
47
  #
60
- # This preserves a single endpoint for proxy binding and client helpers while the public
61
- # configuration contract uses {#ports}.
48
+ # This preserves a single endpoint for client helpers while the public configuration contract uses {#ports}.
62
49
  #
63
50
  # @return [Integer]
64
51
  def port
65
52
  ports.first
66
53
  end
67
-
68
- # Sets proxy configuration using a hash-like value.
69
- #
70
- # This is primarily used when loading YAML configuration files, where proxy attributes are
71
- # represented as scalar values.
72
- #
73
- # @param value [Hash] proxy attributes
74
- # @option value [String] :kind proxy kind name (for example `"fault_injection"`)
75
- # @option value [String] :host upstream host behind the proxy (optional)
76
- # @option value [Integer] :port upstream port behind the proxy
77
- # @option value [String] :log proxy log file path
78
- # @option value [Numeric] :wait wait interval (seconds) after state changes (optional)
79
- # @option value [Hash] :options proxy implementation specific options
80
- # @return [void]
81
- def proxy=(value)
82
- proxy.kind = value[:kind]
83
- proxy.host = value[:host] if value[:host]
84
- proxy.port = value[:port]
85
- proxy.log = value[:log]
86
- proxy.wait = value[:wait] if value[:wait]
87
- proxy.options = value[:options]
88
- end
89
54
  end
90
55
  end
@@ -11,5 +11,61 @@ 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
+ # Proxy configuration for this service.
18
+ #
19
+ # @return [Nonnative::ConfigurationProxy]
20
+ attr_reader :proxy
21
+
22
+ # Creates a service configuration with defaults.
23
+ #
24
+ # @return [void]
25
+ def initialize
26
+ super
27
+
28
+ self.port = 0
29
+ @proxy = Nonnative::ConfigurationProxy.new
30
+ end
31
+
32
+ # Sets proxy configuration using a hash-like value.
33
+ #
34
+ # This is primarily used when loading YAML configuration files, where proxy attributes are
35
+ # represented as scalar values.
36
+ #
37
+ # @param value [Hash] proxy attributes
38
+ # @option value [String] :kind proxy kind name (for example `"fault_injection"`)
39
+ # @option value [String] :host upstream host behind the proxy (optional)
40
+ # @option value [Integer] :port upstream port behind the proxy
41
+ # @option value [String] :log proxy log file path
42
+ # @option value [Numeric] :wait wait interval (seconds) after state changes (optional)
43
+ # @option value [Hash] :options proxy implementation specific options
44
+ # @return [void]
45
+ def proxy=(value)
46
+ proxy.kind = value[:kind]
47
+ proxy.host = value[:host] if value[:host]
48
+ proxy.port = value[:port]
49
+ proxy.log = value[:log]
50
+ proxy.wait = value[:wait] if value[:wait]
51
+ proxy.options = value[:options]
52
+ end
53
+
54
+ # Services expose a single proxy listener, so plural runner ports are not supported.
55
+ #
56
+ # @return [void]
57
+ # @raise [ArgumentError] when plural service ports are read
58
+ def ports
59
+ raise ArgumentError, "Use 'port' instead of 'ports' for service '#{name}'"
60
+ end
61
+
62
+ # Services expose a single proxy listener, so plural runner ports are not supported.
63
+ #
64
+ # @param _value [Array<Integer>] ignored plural ports
65
+ # @return [void]
66
+ # @raise [ArgumentError] when plural service ports are assigned
67
+ def ports=(_value)
68
+ raise ArgumentError, "Use 'port' instead of 'ports' for service '#{name}'"
69
+ end
14
70
  end
15
71
  end
@@ -45,16 +45,6 @@ module Nonnative
45
45
  end
46
46
 
47
47
  def install_proxy_mutation_steps
48
- Given('I set the proxy for process {string} to {string}') do |name, operation|
49
- process = Nonnative.pool.process_by_name(name)
50
- Nonnative::Cucumber::Registration.apply_proxy_operation(process.proxy, operation)
51
- end
52
-
53
- Given('I set the proxy for server {string} to {string}') do |name, operation|
54
- server = Nonnative.pool.server_by_name(name)
55
- Nonnative::Cucumber::Registration.apply_proxy_operation(server.proxy, operation)
56
- end
57
-
58
48
  Given('I set the proxy for service {string} to {string}') do |name, operation|
59
49
  service = Nonnative.pool.service_by_name(name)
60
50
  Nonnative::Cucumber::Registration.apply_proxy_operation(service.proxy, operation)
@@ -62,16 +52,6 @@ module Nonnative
62
52
  end
63
53
 
64
54
  def install_proxy_reset_steps
65
- Then('I should reset the proxy for process {string}') do |name|
66
- process = Nonnative.pool.process_by_name(name)
67
- process.proxy.reset
68
- end
69
-
70
- Then('I should reset the proxy for server {string}') do |name|
71
- server = Nonnative.pool.server_by_name(name)
72
- server.proxy.reset
73
- end
74
-
75
55
  Then('I should reset the proxy for service {string}') do |name|
76
56
  service = Nonnative.pool.service_by_name(name)
77
57
  service.proxy.reset
@@ -18,12 +18,12 @@ module Nonnative
18
18
  #
19
19
  # ## Wiring
20
20
  #
21
- # When enabled, your test/client should connect to the runner `host` and first configured port (the
22
- # proxy endpoint), and the proxy will forward traffic to the upstream target exposed by {#host}:{#port}.
21
+ # When enabled, your test/client should connect to the service `host` and `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
  #
26
- # The proxy is configured via the runner’s `proxy` hash:
26
+ # The proxy is configured via the service's `proxy` hash:
27
27
  #
28
28
  # - `kind`: `"fault_injection"`
29
29
  # - `host` / `port`: upstream target behind the proxy (exposed via {#host}/{#port})
@@ -50,7 +50,7 @@ module Nonnative
50
50
  end
51
51
  end
52
52
 
53
- # @param service [Nonnative::ConfigurationRunner] runner configuration with proxy settings
53
+ # @param service [Nonnative::ConfigurationService] service configuration with proxy settings
54
54
  def initialize(service)
55
55
  @connections = Concurrent::Hash.new
56
56
  @logger = Logger.new(service.proxy.log)
@@ -62,8 +62,8 @@ 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` and first configured port.
66
- # Clients connect to that runner endpoint, while upstream traffic is forwarded to {#host}:{#port}.
65
+ # This binds a TCP server on the service `host` and `port`.
66
+ # Clients connect to that service endpoint, while upstream traffic is forwarded to {#host}:{#port}.
67
67
  #
68
68
  # @return [void]
69
69
  def start
@@ -4,8 +4,7 @@ module Nonnative
4
4
  # gRPC server runner implemented using {GRPC::RpcServer}.
5
5
  #
6
6
  # This is a convenience server implementation for running a gRPC service in-process under
7
- # Nonnative's server lifecycle. It binds to the configured proxy `host`/`port` and is started/stopped
8
- # by {Nonnative::Server} via {#perform_start} / {#perform_stop}.
7
+ # Nonnative's server lifecycle. It binds to the configured server `host` and first `ports` entry.
9
8
  #
10
9
  # Important note about logging: the `grpc` gem uses a global logger. This implementation sets
11
10
  # `GRPC.logger` to write to the configured `service.log`, and whichever gRPC server is initialized
@@ -33,12 +32,11 @@ module Nonnative
33
32
 
34
33
  # Binds the gRPC server and begins serving requests.
35
34
  #
36
- # The server binds to the upstream proxy host/port so the fault-injection proxy can expose the
37
- # runner host and first configured port as the client-facing endpoint used by readiness checks.
35
+ # The server binds to the configured server host and first configured port.
38
36
  #
39
37
  # @return [void]
40
38
  def perform_start
41
- server.add_http2_port("#{proxy.host}:#{proxy.port}", :this_port_is_insecure)
39
+ server.add_http2_port("#{service.host}:#{service.port}", :this_port_is_insecure)
42
40
  server.run
43
41
  end
44
42
 
@@ -4,8 +4,7 @@ module Nonnative
4
4
  # Puma-based HTTP server runner.
5
5
  #
6
6
  # This is a convenience server implementation for running a Rack/Sinatra application in-process
7
- # under Nonnative's server lifecycle. It binds to the configured proxy `host`/`port` (so it works
8
- # consistently with proxy configuration) and uses Puma for HTTP serving.
7
+ # under Nonnative's server lifecycle. It binds to the configured server `host` and first `ports` entry.
9
8
  #
10
9
  # The server is started and stopped by {Nonnative::Server} via {#perform_start} / {#perform_stop}.
11
10
  #
@@ -48,12 +47,11 @@ module Nonnative
48
47
 
49
48
  # Binds the Puma server and begins serving.
50
49
  #
51
- # The listener binds to the upstream proxy host/port so the fault-injection proxy can expose the
52
- # runner host and first configured port as the client-facing endpoint used by readiness checks.
50
+ # The listener binds to the configured server host and first configured port.
53
51
  #
54
52
  # @return [void]
55
53
  def perform_start
56
- server.add_tcp_listener proxy.host, proxy.port
54
+ server.add_tcp_listener service.host, service.port
57
55
  server.run false
58
56
  end
59
57
 
@@ -7,7 +7,7 @@ module Nonnative
7
7
  # It does not bind/listen or alter traffic; it simply exposes the underlying runner's configured
8
8
  # `host` and primary `port`.
9
9
  #
10
- # Runners can always call `start`, `stop`, and `reset` safely on this proxy.
10
+ # Services can always call `start`, `stop`, and `reset` safely on this proxy.
11
11
  #
12
12
  # @see Nonnative.proxy
13
13
  # @see Nonnative::Proxy
@@ -104,15 +104,13 @@ module Nonnative
104
104
  services[runner_index(configuration.services, name)]
105
105
  end
106
106
 
107
- # Resets proxies for all runners in this pool.
107
+ # Resets service proxies in this pool.
108
108
  #
109
109
  # This is used by the Cucumber `@reset` hook and is safe to call any time after the pool is created.
110
110
  #
111
111
  # @return [void]
112
112
  def reset
113
113
  services.each { |s| s.proxy.reset }
114
- servers.each { |s| s.first.proxy.reset }
115
- processes.each { |p| p.first.proxy.reset }
116
114
  end
117
115
 
118
116
  private
@@ -4,7 +4,6 @@ module Nonnative
4
4
  # Runtime runner that manages an OS-level child process.
5
5
  #
6
6
  # A process runner:
7
- # - starts the configured proxy (if any),
8
7
  # - spawns a child process using the configured command and environment,
9
8
  # - waits briefly (via the runner `wait`), and
10
9
  # - participates in readiness/shutdown via TCP port checks orchestrated by {Nonnative::Pool}.
@@ -21,7 +20,7 @@ module Nonnative
21
20
  @timeout = Nonnative::Timeout.new(service.timeout)
22
21
  end
23
22
 
24
- # Starts the proxy (if any) and spawns the configured process if it is not already running.
23
+ # Spawns the configured process if it is not already running.
25
24
  #
26
25
  # @return [Array<(Integer, Boolean)>]
27
26
  # a tuple of:
@@ -29,7 +28,6 @@ module Nonnative
29
28
  # - whether the process appears to still be running (non-blocking wait result)
30
29
  def start
31
30
  unless process_exists?
32
- proxy.start
33
31
  @pid = process_spawn
34
32
  wait_start
35
33
  end
@@ -37,7 +35,7 @@ module Nonnative
37
35
  [pid, ::Process.waitpid2(pid, ::Process::WNOHANG).nil?]
38
36
  end
39
37
 
40
- # Stops the process (if running) and stops the proxy (if any).
38
+ # Stops the process if it is running.
41
39
  #
42
40
  # The process is signalled using the configured signal (defaults to `INT` when not set).
43
41
  #
@@ -54,8 +52,6 @@ module Nonnative
54
52
  end
55
53
 
56
54
  [pid, stopped]
57
- ensure
58
- proxy.stop
59
55
  end
60
56
 
61
57
  # Returns a memoized memory reader for the spawned process.
@@ -4,8 +4,7 @@ module Nonnative
4
4
  # Base class for proxy implementations.
5
5
  #
6
6
  # A proxy is responsible for interposing behavior between a client and a target service.
7
- # Runners ({Nonnative::Process}, {Nonnative::Server}, and {Nonnative::Service}) create a proxy
8
- # instance via {Nonnative::ProxyFactory} based on `service.proxy.kind`.
7
+ # Runtime services create a proxy instance via {Nonnative::ProxyFactory} based on `service.proxy.kind`.
9
8
  #
10
9
  # Concrete proxies typically implement these public methods:
11
10
  # - `start`: begin proxying (bind/listen, start threads, etc)
@@ -17,7 +16,7 @@ module Nonnative
17
16
  # @see Nonnative::NoProxy
18
17
  # @see Nonnative::FaultInjectionProxy
19
18
  class Proxy
20
- # @param service [Nonnative::ConfigurationRunner] runner configuration with an attached proxy configuration
19
+ # @param service [Nonnative::ConfigurationService] service configuration with an attached proxy configuration
21
20
  def initialize(service)
22
21
  @service = service
23
22
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- # Factory for creating proxy instances for runners.
4
+ # Factory for creating proxy instances for services.
5
5
  #
6
- # Each runtime runner ({Nonnative::Process}, {Nonnative::Server}, {Nonnative::Service}) constructs
7
- # a proxy via this factory. The proxy implementation is selected by `service.proxy.kind` and resolved
8
- # using {Nonnative.proxy}.
6
+ # A runtime service constructs a proxy via this factory. The proxy implementation is selected by
7
+ # `service.proxy.kind` and resolved using {Nonnative.proxy}.
9
8
  #
10
9
  # If the kind is unknown (or `"none"`), {Nonnative.proxy} returns {Nonnative::NoProxy}.
11
10
  #
@@ -15,9 +14,9 @@ module Nonnative
15
14
  # @see Nonnative::NoProxy
16
15
  class ProxyFactory
17
16
  class << self
18
- # Creates a proxy instance for the given runner configuration.
17
+ # Creates a proxy instance for the given service configuration.
19
18
  #
20
- # @param service [Nonnative::ConfigurationRunner] runner configuration with an attached proxy configuration
19
+ # @param service [Nonnative::ConfigurationService] service configuration with an attached proxy configuration
21
20
  # @return [Nonnative::Proxy] proxy instance (may be a {Nonnative::NoProxy})
22
21
  def create(service)
23
22
  proxy = Nonnative.proxy(service.proxy.kind)
@@ -10,21 +10,13 @@ module Nonnative
10
10
  # - {Nonnative::Server} for in-process Ruby servers (threads)
11
11
  # - {Nonnative::Service} for proxy-only external dependencies
12
12
  #
13
- # Each runner has an associated proxy instance created via {Nonnative::ProxyFactory}.
14
- #
15
13
  # @see Nonnative::Process
16
14
  # @see Nonnative::Server
17
15
  # @see Nonnative::Service
18
16
  class Runner
19
- # Returns the proxy instance for this runner.
20
- #
21
- # @return [Nonnative::Proxy]
22
- attr_reader :proxy
23
-
24
17
  # @param service [Nonnative::ConfigurationRunner] runner configuration
25
18
  def initialize(service)
26
19
  @service = service
27
- @proxy = Nonnative::ProxyFactory.create(service)
28
20
  end
29
21
 
30
22
  # Returns the configured runner name.
@@ -4,7 +4,6 @@ module Nonnative
4
4
  # Runtime runner that manages an in-process Ruby server.
5
5
  #
6
6
  # A server runner:
7
- # - starts the configured proxy (if any),
8
7
  # - starts a Ruby thread that runs {#perform_start},
9
8
  # - waits briefly (via the runner `wait`), and
10
9
  # - participates in readiness/shutdown via TCP port checks orchestrated by {Nonnative::Pool}.
@@ -25,7 +24,7 @@ module Nonnative
25
24
  @timeout = Nonnative::Timeout.new(service.timeout)
26
25
  end
27
26
 
28
- # Starts the proxy (if any) and starts the server thread if not already started.
27
+ # Starts the server thread if it is not already started.
29
28
  #
30
29
  # @return [Array<(Integer, TrueClass)>]
31
30
  # a tuple of:
@@ -33,7 +32,6 @@ module Nonnative
33
32
  # - `true` (thread creation itself is considered started; readiness is checked separately)
34
33
  def start
35
34
  unless thread
36
- proxy.start
37
35
  @thread = Thread.new { perform_start }
38
36
 
39
37
  wait_start
@@ -46,14 +44,13 @@ module Nonnative
46
44
 
47
45
  # Stops the server if it is running.
48
46
  #
49
- # Calls {#perform_stop}, terminates the server thread, stops the proxy (if any), and waits briefly.
47
+ # Calls {#perform_stop}, terminates the server thread, and waits briefly.
50
48
  #
51
49
  # @return [Integer] the server identifier (`object_id`)
52
50
  def stop
53
51
  if thread
54
52
  perform_stop
55
53
  thread.terminate
56
- proxy.stop
57
54
 
58
55
  @thread = nil
59
56
  wait_stop
@@ -12,6 +12,18 @@ module Nonnative
12
12
  # @see Nonnative::ConfigurationService
13
13
  # @see Nonnative::Proxy
14
14
  class Service < Runner
15
+ # Returns the proxy instance for this service.
16
+ #
17
+ # @return [Nonnative::Proxy]
18
+ attr_reader :proxy
19
+
20
+ # @param service [Nonnative::ConfigurationService] service configuration
21
+ def initialize(service)
22
+ super
23
+
24
+ @proxy = Nonnative::ProxyFactory.create(service)
25
+ end
26
+
15
27
  # Starts the configured proxy (if any).
16
28
  #
17
29
  # @return [void]
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '3.0.0'
7
+ VERSION = '3.2.0'
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nonnative
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski