nonnative 3.13.0 → 3.14.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: 304a9d9d01fafb9f0822e4510c647f87095f78987feecb5a9d2eddc43317a880
4
- data.tar.gz: 366157abd34f6ffa48cce159dffd49075e4a98653ea5d3f3b11b2494ad47b4a1
3
+ metadata.gz: 802f0ee020ba8e7c6cef867895d30c6491ace313fec8103aa6b99726c8402666
4
+ data.tar.gz: 70d522685a3c9cfbc872ee36549d9ba8274be60138a79db68e64c83115fd7cb5
5
5
  SHA512:
6
- metadata.gz: 6fa7d92393e3a4df9f400a7f35c857998c13dc026ec85be407fa37d6e8ba2189c5992af70e001c1cdfaf9589d1b6d33a2daf11abf8361f84f07ab8bc3a72fed1
7
- data.tar.gz: bf37673b803ff66338009fbbd458d707c6eed9ef0ba618bb53b97e2f5770788708343f6bd34b67758fd6d1a13e464f63edffced76c26591f8ac4fe4ec2267a3e
6
+ metadata.gz: a0b3e8502437cfd6d3a556a8a6f10901a2a89d85a4689fbad3c68d6819e03b4cccf5a51de3cfd66b86827d66f768891f68ee41c91b3ea504345c7f25d22178c8
7
+ data.tar.gz: b16ef6cfe6099e9a62be954178c71a743fb297ece661be2651239c8501707af30e1609e58abfe99bc58d007777a45c99505eb374484c0ba9788e886d0b876e42
data/.circleci/config.yml CHANGED
@@ -17,7 +17,7 @@ executors:
17
17
  - image: alexfalkowski/ruby:3.19
18
18
  release:
19
19
  docker:
20
- - image: alexfalkowski/release:8.19
20
+ - image: alexfalkowski/release:8.20
21
21
  alpine:
22
22
  docker:
23
23
  - image: alpine:latest
data/AGENTS.md CHANGED
@@ -79,6 +79,32 @@ of dependencies.
79
79
  thread. Do not flag the listener `join` as an unbounded reliability gap unless
80
80
  the task is explicitly about proxy shutdown behavior or there is a normal-use
81
81
  reproducer, failing CI evidence, or platform-specific hang evidence.
82
+ - Service readiness is intentionally TCP-only for externally managed
83
+ dependencies. Do not flag missing HTTP/gRPC service readiness as a feature gap
84
+ unless the task is explicitly about changing the `Nonnative::Service`
85
+ readiness model. HTTP/gRPC readiness belongs to managed processes, where the
86
+ user explicitly models the application health endpoints.
87
+ - Process runners intentionally inherit the parent working directory. Nonnative
88
+ is normally run from the test folder that owns `test/nonnative.yml`, relative
89
+ config paths, logs, and reports. Do not flag missing process cwd/chdir support
90
+ as a feature gap unless the task is explicitly about changing process working
91
+ directory behavior.
92
+ - Message-specific start/stop assertions and attempted-stop success assertions
93
+ in `features/step_definitions/lifecycle_steps.rb` are repository-local
94
+ Cucumber helpers. Do not flag their absence from `lib/nonnative/cucumber.rb`
95
+ as a feature gap unless downstream usage evidence exists or the task is
96
+ explicitly about expanding public lifecycle assertion steps.
97
+ - Liveness, readiness, and metrics endpoint assertion steps in
98
+ `features/step_definitions/servers_steps.rb` are repository-local Cucumber
99
+ helpers for testing nonnative's own framework assumptions. Do not flag their
100
+ absence from `lib/nonnative/cucumber.rb` as a feature gap unless downstream
101
+ usage evidence exists or the task is explicitly about expanding public
102
+ observability assertion steps.
103
+ - `Nonnative::Configuration` intentionally exposes `process_by_name` without
104
+ matching `server_by_name` or `service_by_name` helpers. Do not flag missing
105
+ server/service configuration lookup helpers as a feature gap unless downstream
106
+ usage evidence exists or the task is explicitly about changing the
107
+ pre-start configuration lookup API.
82
108
 
83
109
  ## Runtime Model
84
110
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (3.13.0)
4
+ nonnative (3.14.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
data/README.md CHANGED
@@ -758,6 +758,7 @@ Clients connect to the service `host`/`port`, while the proxy forwards traffic t
758
758
 
759
759
  - `close_all` - Closes the socket as soon as it connects.
760
760
  - `delay` - Delays traffic on the connection. Defaults to 2 seconds and can be configured through options.
761
+ - `timeout` - Accepts the connection and stalls traffic until reset or stop closes the connection, so clients exercise their own read timeout behavior.
761
762
  - `invalid_data` - Forwards client requests unchanged, then corrupts upstream responses before they reach the client.
762
763
 
763
764
  ###### 🧩 Fault Injection Services
@@ -769,6 +770,7 @@ name = 'name of service in configuration'
769
770
  service = Nonnative.pool.service_by_name(name)
770
771
 
771
772
  service.proxy.close_all # To use close_all.
773
+ service.proxy.timeout # To stall traffic until reset or stop.
772
774
  service.proxy.reset # To reset it back to a good state.
773
775
  ```
774
776
 
@@ -776,6 +778,7 @@ Use the Cucumber proxy steps:
776
778
 
777
779
  ```cucumber
778
780
  Given I set the proxy for service 'service_1' to 'close_all'
781
+ Given I set the proxy for service 'service_1' to 'timeout'
779
782
  Then I should reset the proxy for service 'service_1'
780
783
  ```
781
784
 
@@ -4,8 +4,8 @@ module Nonnative
4
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
- # the built-in `"fault_injection"` proxy can close connections, introduce delays, or corrupt data
8
- # for resilience testing.
7
+ # the built-in `"fault_injection"` proxy can close connections, introduce delays, stall traffic,
8
+ # or corrupt data for resilience testing.
9
9
  #
10
10
  # This object is created automatically for each service via {Nonnative::ConfigurationService}.
11
11
  # When `kind` is set to `"none"`, no proxy is started and the service will use its configured
@@ -56,6 +56,7 @@ module Nonnative
56
56
  PROXY_OPERATIONS = {
57
57
  'close_all' => :close_all,
58
58
  'delay' => :delay,
59
+ 'timeout' => :timeout,
59
60
  'invalid_data' => :invalid_data,
60
61
  'reset' => :reset
61
62
  }.freeze
@@ -11,6 +11,7 @@ module Nonnative
11
11
  #
12
12
  # - {#close_all}: close connections immediately on accept
13
13
  # - {#delay}: delay reads by a configured duration (default: 2 seconds)
14
+ # - {#timeout}: accept connections and keep them silent until clients time out
14
15
  # - {#invalid_data}: forward requests unchanged and mutate upstream responses before they reach clients
15
16
  # - {#reset}: return to healthy pass-through behavior
16
17
  #
@@ -107,6 +108,17 @@ module Nonnative
107
108
  apply_state :delay
108
109
  end
109
110
 
111
+ # Accepts connections and stalls without forwarding bytes.
112
+ #
113
+ # This simulates a dependency that accepts a TCP connection but leaves clients waiting until
114
+ # their own read timeout fires. The proxy keeps the connection silent until reset or stop closes
115
+ # active connections.
116
+ #
117
+ # @return [void]
118
+ def timeout
119
+ apply_state :timeout
120
+ end
121
+
110
122
  # Mutates upstream responses while forwarding client requests unchanged.
111
123
  #
112
124
  # @return [void]
@@ -16,6 +16,7 @@ module Nonnative
16
16
  # @see Nonnative::SocketPairFactory
17
17
  # @see Nonnative::CloseAllSocketPair
18
18
  # @see Nonnative::DelaySocketPair
19
+ # @see Nonnative::TimeoutSocketPair
19
20
  # @see Nonnative::InvalidDataSocketPair
20
21
  class SocketPair
21
22
  # @param proxy [#host, #port, #options] proxy configuration used to connect upstream
@@ -10,18 +10,20 @@ module Nonnative
10
10
  # - `:none` (or any unknown value) -> {Nonnative::SocketPair} (pass-through)
11
11
  # - `:close_all` -> {Nonnative::CloseAllSocketPair}
12
12
  # - `:delay` -> {Nonnative::DelaySocketPair}
13
+ # - `:timeout` -> {Nonnative::TimeoutSocketPair}
13
14
  # - `:invalid_data` -> {Nonnative::InvalidDataSocketPair}
14
15
  #
15
16
  # @see Nonnative::FaultInjectionProxy
16
17
  # @see Nonnative::SocketPair
17
18
  # @see Nonnative::CloseAllSocketPair
18
19
  # @see Nonnative::DelaySocketPair
20
+ # @see Nonnative::TimeoutSocketPair
19
21
  # @see Nonnative::InvalidDataSocketPair
20
22
  class SocketPairFactory
21
23
  class << self
22
24
  # Creates a socket-pair instance for the given proxy state.
23
25
  #
24
- # @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:delay`, `:invalid_data`)
26
+ # @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:delay`, `:timeout`, `:invalid_data`)
25
27
  # @param proxy [Nonnative::ConfigurationProxy] proxy configuration (host/port/options)
26
28
  # @return [Nonnative::SocketPair] a socket-pair implementation instance
27
29
  def create(kind, proxy)
@@ -30,6 +32,8 @@ module Nonnative
30
32
  CloseAllSocketPair
31
33
  when :delay
32
34
  DelaySocketPair
35
+ when :timeout
36
+ TimeoutSocketPair
33
37
  when :invalid_data
34
38
  InvalidDataSocketPair
35
39
  else
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ # Socket-pair variant used by the fault-injection proxy to simulate read timeouts.
5
+ #
6
+ # When active, the proxy accepts the client connection and keeps it open without forwarding bytes.
7
+ # Clients with read deadlines should observe their own timeout behavior.
8
+ #
9
+ # This behavior is enabled by calling {Nonnative::FaultInjectionProxy#timeout}.
10
+ #
11
+ # @see Nonnative::FaultInjectionProxy
12
+ # @see Nonnative::SocketPairFactory
13
+ # @see Nonnative::SocketPair
14
+ class TimeoutSocketPair < SocketPair
15
+ # Keeps the accepted socket silent until reset or stop closes active proxy connections.
16
+ #
17
+ # @param local_socket [TCPSocket] the accepted client socket
18
+ # @return [void]
19
+ def connect(local_socket)
20
+ @local_socket = local_socket
21
+
22
+ Nonnative.logger.info "stalling socket '#{local_socket.inspect}' for 'timeout' pair"
23
+
24
+ # Do not call super: the base implementation opens the upstream socket and forwards traffic.
25
+ sleep 0.1 until local_socket.closed?
26
+ ensure
27
+ close
28
+ end
29
+ end
30
+ end
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '3.13.0'
7
+ VERSION = '3.14.0'
8
8
  end
data/lib/nonnative.rb CHANGED
@@ -102,6 +102,7 @@ require 'nonnative/fault_injection_proxy'
102
102
  require 'nonnative/socket_pair'
103
103
  require 'nonnative/close_all_socket_pair'
104
104
  require 'nonnative/delay_socket_pair'
105
+ require 'nonnative/timeout_socket_pair'
105
106
  require 'nonnative/invalid_data_socket_pair'
106
107
  require 'nonnative/socket_pair_factory'
107
108
  require 'nonnative/go_executable'
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.13.0
4
+ version: 3.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -330,6 +330,7 @@ files:
330
330
  - lib/nonnative/stop_error.rb
331
331
  - lib/nonnative/tcp_probe.rb
332
332
  - lib/nonnative/timeout.rb
333
+ - lib/nonnative/timeout_socket_pair.rb
333
334
  - lib/nonnative/version.rb
334
335
  - nonnative.gemspec
335
336
  homepage: https://github.com/alexfalkowski/nonnative