nonnative 2.15.0 → 2.16.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: f556739cd561141b772bdeaa9f7cdc8f9934d734bf7517d4e17ef16f2f809f15
4
- data.tar.gz: fbba3f9c3f5902d2c385a8e509c27f681f6e44876d02ee7d63b00a66edb19a1d
3
+ metadata.gz: 9b79bc3b75303cd4d605a4ef81c7b3c1eedd188204d088864fefb65dc73f0922
4
+ data.tar.gz: c8e4b5ef1bb12213e80d7084d5b3809206279a44bca610947114fd41dd188745
5
5
  SHA512:
6
- metadata.gz: 65e74315f3bb6e81b1c2797fce743c2f0741a52e2edd40ba608a30e4d582e08b1408008249dd76e81972a438054810390b709f785e6ef455b719ef89658a572c
7
- data.tar.gz: 0e43f244c7867e12a512a71aea9f70f2fa0323c62f97f5c417512e131450a7fc714419cb2af0798a692848d5fe95111417ea263b80c1ce5a6f853663a0c03fce
6
+ metadata.gz: a58c635d40b6eeb8b6aa7e9560900bad247fe768f9839b7e141f6be4e500b5fa2ec11267524621db66989f6eaf24601b790716a4508e28203e4845862ad5a289
7
+ data.tar.gz: 111995ab26c566d24df0fb037c094883ac07adf2142254fde6822fb9e4fcea59c855196c650bac382e5ab02fe2610181611040a96f3d13515aef586495ffaf72
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:2.12
6
+ - image: alexfalkowski/ruby:3.8
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:7.13
38
+ - image: alexfalkowski/release:8.8
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:7.13
49
+ - image: alexfalkowski/release:8.8
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:2.12
61
+ - image: alexfalkowski/ruby:3.8
62
62
  steps:
63
63
  - run: echo "all applicable jobs finished"
64
64
  resource_class: arm.large
data/AGENTS.md CHANGED
@@ -1,154 +1,110 @@
1
1
  # AGENTS.md
2
2
 
3
- This repo is the `nonnative` Ruby gem: a Ruby-first harness for end-to-end testing of systems implemented in other languages by starting processes/servers/services, waiting on TCP port readiness, and optionally putting fault-injection proxies in front of them.
3
+ ## Shared skills
4
4
 
5
- ## Shared skill
5
+ This repository uses the shared skills from `bin/skills/`. Read
6
+ `bin/AGENTS.md` for the canonical shared skill list and use the smallest
7
+ matching skill for the task.
6
8
 
7
- Use the shared `coding-standards` skill from `./bin/skills/coding-standards`
8
- for cross-repository coding, review, testing, documentation, and PR
9
- conventions. Treat this `AGENTS.md` as the repo-specific companion to that
10
- skill.
9
+ `nonnative` is a Ruby gem for end-to-end testing systems implemented in other
10
+ languages. It starts processes, in-process servers, and proxy-only services,
11
+ waits on TCP readiness/shutdown, and can place fault-injection proxies in front
12
+ of dependencies.
11
13
 
12
- ## Quick map
14
+ ## Map And Commands
13
15
 
14
- - Library code: `lib/nonnative/**/*.rb`
15
- - Acceptance tests: `features/**/*.feature`, `features/support/**/*.rb`, `features/step_definitions/**/*.rb`
16
- - Generated gRPC Ruby stubs for tests: `test/grpc/**/*`
17
- - Test proto files: `test/nonnative/v1/*.proto`
16
+ - Library: `lib/nonnative/**/*.rb`
17
+ - Cucumber features/support: `features/**/*.feature`, `features/support/**/*.rb`, `features/step_definitions/**/*.rb`
18
+ - Generated gRPC test stubs: `test/grpc/**/*`
19
+ - Test protos: `test/nonnative/v1/*.proto`
18
20
  - Build wiring: root `Makefile` includes `bin/build/make/*.mak`
19
-
20
- ## Key repo dependency
21
-
22
- This repo depends on the `bin/` git submodule.
23
-
24
- - `.gitmodules` points to `git@github.com:alexfalkowski/bin.git`
25
- - CI runs `git submodule sync && git submodule update --init`
26
- - If `bin/` is missing or you do not have SSH access, `make` targets will fail
27
-
28
- ## Core commands
29
-
21
+ - Required submodule: `bin/` from `git@github.com:alexfalkowski/bin.git`; missing SSH/submodule setup breaks `make`
30
22
  - Install deps: `make dep`
31
23
  - Lint: `make lint`
32
- - Run features: `make features`
33
- - Run benchmarks only: `make benchmarks`
34
- - Clean deps: `make clean-dep`
35
- - Clean reports: `make clean-reports`
36
-
37
- ## Runtime model
38
-
39
- Public entry point is `lib/nonnative.rb`.
40
-
41
- Main module API:
42
-
43
- - `configure`
44
- - `start`
45
- - `stop`
46
- - `clear`
47
- - `reset`
48
- - `pool`
24
+ - Features: `make features`
25
+ - Benchmarks only: `make benchmarks`
26
+ - Cleanup: `make clean-dep`, `make clean-reports`
49
27
 
50
- Configuration lives in `Nonnative::Configuration` and is built either:
28
+ ## Runtime Model
51
29
 
52
- - programmatically with `config.process`, `config.server`, `config.service`
53
- - from YAML with `config.load_file(...)`
30
+ Public entry point: `lib/nonnative.rb`.
54
31
 
55
- Runtime runners:
32
+ Main API: `configure`, `start`, `stop`, `clear`, `reset`, `pool`.
56
33
 
57
- - `Nonnative::Process`: manages an OS process
58
- - `Nonnative::Server`: manages an in-process Ruby server thread
59
- - `Nonnative::Service`: manages only proxy lifecycle for an externally managed dependency
34
+ Configuration is `Nonnative::Configuration`, built with
35
+ `config.process`, `config.server`, `config.service`, or
36
+ `config.load_file(...)`.
60
37
 
61
- `Nonnative::Pool` starts services first, then servers/processes, and stops in the reverse direction.
38
+ Runners:
62
39
 
63
- Readiness and shutdown checks are TCP-only via `Nonnative::Port#open?` and `#closed?`.
40
+ - `Nonnative::Process`: OS process
41
+ - `Nonnative::Server`: in-process Ruby server thread
42
+ - `Nonnative::Service`: proxy lifecycle for an externally managed dependency
64
43
 
65
- ## Cucumber integration
44
+ `Nonnative::Pool` starts services first, then servers/processes, and stops in
45
+ reverse. Readiness and shutdown checks are TCP-only via
46
+ `Nonnative::Port#open?` and `#closed?`.
66
47
 
67
- Cucumber integration lives in `lib/nonnative/cucumber.rb`.
48
+ ## Cucumber Surface
68
49
 
69
- Treat `lib/nonnative/cucumber.rb` as a public compatibility surface for library consumers.
70
- Existing hooks and step text should not be removed or renamed unless the user explicitly wants a breaking change.
50
+ `lib/nonnative/cucumber.rb` is public compatibility surface. Do not remove or
51
+ rename hooks/step text unless the user explicitly requests a breaking change.
71
52
 
72
- Supported tags:
53
+ Lifecycle tags:
73
54
 
74
55
  - `@startup`: start before scenario, stop after scenario
75
56
  - `@manual`: scenario starts manually, stop after scenario
76
57
  - `@clear`: call `Nonnative.clear` before scenario
77
58
  - `@reset`: reset proxies after scenario
78
59
 
79
- Repo-owned feature files also use suite taxonomy tags:
60
+ Suite taxonomy tags: `@acceptance`, `@contract`, `@proxy`, `@config`,
61
+ `@service`, `@benchmark`, `@slow`. `make features` excludes `@benchmark`;
62
+ `make benchmarks` runs only `@benchmark`.
80
63
 
81
- - `@acceptance`: end-to-end runner and client flows
82
- - `@contract`: lower-level lifecycle / command coverage
83
- - `@proxy`: proxy-specific coverage
84
- - `@config`: scenarios or example sets that load YAML/configuration
85
- - `@service`: coverage centered on external services
86
- - `@benchmark`: benchmark-only scenarios
87
- - `@slow`: slower-running scenarios, currently benchmarks
64
+ `Nonnative.clear` clears configuration, logger, observability client, and pool.
65
+ `require 'nonnative'` loads Cucumber integration lazily and is safe outside a
66
+ booted Cucumber runtime. For start-once-per-test-run, use
67
+ `require 'nonnative/startup'`.
88
68
 
89
- `make features` excludes `@benchmark`; `make benchmarks` runs only `@benchmark`.
69
+ ## Proxy And Config Gotchas
90
70
 
91
- `Nonnative.clear` now clears:
71
+ Proxy wiring is the easiest mistake:
92
72
 
93
- - configuration
94
- - logger
95
- - observability client
96
- - pool
73
+ - Runner `host` / `port` are client-facing and used for readiness/shutdown
74
+ - For `fault_injection`, nested `proxy.host` / `proxy.port` are the upstream target
75
+ - Clients connect to runner `host` / `port` when a proxy is enabled
97
76
 
98
- `require 'nonnative'` still loads the Cucumber integration, but hook/step registration is lazy, so plain `require 'nonnative'` is safe outside a booted Cucumber runtime.
77
+ Proxy kinds: `none`, `fault_injection`.
78
+ Fault-injection states: `none`, `close_all`, `delay`, `invalid_data`.
99
79
 
100
- For “start once per test run”, use `require 'nonnative/startup'`.
80
+ Config rules:
101
81
 
102
- ## Proxy wiring
103
-
104
- This is the easiest thing to get wrong.
105
-
106
- - Runner `host` / `port` are the client-facing endpoint and the values used by readiness/shutdown checks
107
- - For `fault_injection`, nested `proxy.host` / `proxy.port` are the upstream target behind the proxy
108
- - Clients should connect to the runner `host` / `port` when a proxy is enabled
109
-
110
- Available proxy kinds:
111
-
112
- - `none`
113
- - `fault_injection`
114
-
115
- Fault injection states:
116
-
117
- - `none`
118
- - `close_all`
119
- - `delay`
120
- - `invalid_data`
121
-
122
- ## Config gotchas
123
-
124
- - Services must be declared under `services:` in YAML, not `processes:`
82
+ - YAML services belong under `services:`, not `processes:`
125
83
  - There is no top-level `config.wait`; `wait` is per runner
126
- - Service configs use `config.service do |s| ... end`, so use `s.host` / `s.port`
127
- - Proxy examples need both sides of the split:
128
- - runner `host` / `port` = proxy endpoint
129
- - nested `proxy.host` / `proxy.port` = upstream target
84
+ - Programmatic service config uses `config.service do |s| ... end`, so use `s.host` / `s.port`
85
+ - Proxy examples need both endpoint sides: runner `host` / `port` for the proxy, nested `proxy.host` / `proxy.port` for upstream
86
+
87
+ ## Fixtures And Limitations
130
88
 
131
- ## Test fixtures worth knowing
89
+ Useful fixtures:
132
90
 
133
- - Local process fixture: `features/support/bin/start`
134
- - HTTP fixtures: `features/support/http_server.rb`, `features/support/http_proxy_server.rb`
135
- - TCP fixture: `features/support/tcp_server.rb`
136
- - gRPC fixtures: `features/support/grpc_server.rb`, plus generated stubs under `test/grpc/`
91
+ - Process: `features/support/bin/start`
92
+ - HTTP: `features/support/http_server.rb`, `features/support/http_proxy_server.rb`
93
+ - TCP: `features/support/tcp_server.rb`
94
+ - gRPC: `features/support/grpc_server.rb`, generated stubs in `test/grpc/`
137
95
 
138
- ## Important limitations / gotchas
96
+ Limitations:
139
97
 
140
- - Ruby version is constrained by `nonnative.gemspec` to `>= 4.0.0` and `< 5.0.0`
141
98
  - The `grpc` Ruby library uses a global logger; per-server gRPC loggers are not really supported
142
- - `make` depends on the `bin/` submodule being present
143
99
  - Local Ruby/Bundler mismatches can break native extensions on macOS
144
- - Coverage output and Cucumber reports are written under `test/reports`
100
+ - Coverage and Cucumber reports go under `test/reports`
145
101
  - Port checks can be flaky if tests reuse ports unexpectedly
146
102
 
147
- ## Where to look first
103
+ ## Look First
148
104
 
149
- - Lifecycle orchestration: `lib/nonnative.rb`, `lib/nonnative/pool.rb`
150
- - Readiness / timeouts: `lib/nonnative/port.rb`, `lib/nonnative/timeout.rb`
105
+ - Lifecycle: `lib/nonnative.rb`, `lib/nonnative/pool.rb`
106
+ - Readiness/timeouts: `lib/nonnative/port.rb`, `lib/nonnative/timeout.rb`
151
107
  - Process lifecycle: `lib/nonnative/process.rb`
152
- - Proxies / fault injection: `lib/nonnative/fault_injection_proxy.rb`, `lib/nonnative/socket_pair_factory.rb`
153
- - Cucumber integration: `lib/nonnative/cucumber.rb`, `lib/nonnative/startup.rb`, `features/support/env.rb`
108
+ - Proxies: `lib/nonnative/fault_injection_proxy.rb`, `lib/nonnative/socket_pair_factory.rb`
109
+ - Cucumber: `lib/nonnative/cucumber.rb`, `lib/nonnative/startup.rb`, `features/support/env.rb`
154
110
  - Config loading: `lib/nonnative/configuration.rb`, `lib/nonnative/configuration_runner.rb`, `lib/nonnative/configuration_proxy.rb`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (2.15.0)
4
+ nonnative (2.16.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
@@ -206,4 +206,4 @@ DEPENDENCIES
206
206
  simplecov-cobertura
207
207
 
208
208
  BUNDLED WITH
209
- 4.0.10
209
+ 4.0.11
data/README.md CHANGED
@@ -56,6 +56,8 @@ Runner fields (process/server/service):
56
56
 
57
57
  For `fault_injection`, the nested `proxy.host`/`proxy.port` describe the upstream target behind the proxy. In-process server implementations typically bind there via `proxy.host` / `proxy.port`.
58
58
 
59
+ 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.
60
+
59
61
  ### Lifecycle strategies (Cucumber integration)
60
62
 
61
63
  Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
@@ -547,9 +549,13 @@ Nonnative.configure do |config|
547
549
  config.log = 'nonnative.log'
548
550
 
549
551
  config.process do |p|
552
+ p.host = '127.0.0.1'
553
+ p.port = 20_000
554
+
550
555
  p.proxy = {
551
556
  kind: 'fault_injection',
552
- port: 20_000,
557
+ host: '127.0.0.1',
558
+ port: 12_321,
553
559
  log: 'proxy_server.log',
554
560
  wait: 1,
555
561
  options: {
@@ -569,9 +575,12 @@ url: http://localhost:4567
569
575
  log: nonnative.log
570
576
  processes:
571
577
  -
578
+ host: 127.0.0.1
579
+ port: 20000
572
580
  proxy:
573
581
  kind: fault_injection
574
- port: 20000
582
+ host: 127.0.0.1
583
+ port: 12321
575
584
  log: proxy_server.log
576
585
  wait: 1
577
586
  options:
@@ -592,9 +601,13 @@ Nonnative.configure do |config|
592
601
  config.log = 'nonnative.log'
593
602
 
594
603
  config.server do |s|
604
+ s.host = '127.0.0.1'
605
+ s.port = 20_000
606
+
595
607
  s.proxy = {
596
608
  kind: 'fault_injection',
597
- port: 20_000,
609
+ host: '127.0.0.1',
610
+ port: 12_321,
598
611
  log: 'proxy_server.log',
599
612
  wait: 1,
600
613
  options: {
@@ -614,9 +627,12 @@ url: http://localhost:4567
614
627
  log: nonnative.log
615
628
  servers:
616
629
  -
630
+ host: 127.0.0.1
631
+ port: 20000
617
632
  proxy:
618
633
  kind: fault_injection
619
- port: 20000
634
+ host: 127.0.0.1
635
+ port: 12321
620
636
  log: proxy_server.log
621
637
  wait: 1
622
638
  options:
@@ -15,8 +15,8 @@ module Nonnative
15
15
  # @see Nonnative.proxies
16
16
  class ConfigurationProxy
17
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`)
18
+ # @return [String] upstream host used by proxy implementations (defaults to `"0.0.0.0"`)
19
+ # @return [Integer] upstream port used by proxy implementations (defaults to `0`)
20
20
  # @return [String, nil] path to proxy log file (implementation-dependent)
21
21
  # @return [Numeric] wait interval (seconds) after proxy state changes (defaults to `0.1`)
22
22
  # @return [Hash] proxy implementation options (implementation-dependent)
@@ -51,8 +51,8 @@ module Nonnative
51
51
  #
52
52
  # @param value [Hash] proxy attributes
53
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
54
+ # @option value [String] :host upstream host behind the proxy (optional)
55
+ # @option value [Integer] :port upstream port behind the proxy
56
56
  # @option value [String] :log proxy log file path
57
57
  # @option value [Numeric] :wait wait interval (seconds) after state changes (optional)
58
58
  # @option value [Hash] :options proxy implementation specific options
@@ -32,6 +32,13 @@ module Nonnative
32
32
  end
33
33
 
34
34
  module ProxySteps
35
+ PROXY_OPERATIONS = {
36
+ 'close_all' => :close_all,
37
+ 'delay' => :delay,
38
+ 'invalid_data' => :invalid_data,
39
+ 'reset' => :reset
40
+ }.freeze
41
+
35
42
  def install_proxy_steps
36
43
  install_proxy_mutation_steps
37
44
  install_proxy_reset_steps
@@ -40,17 +47,17 @@ module Nonnative
40
47
  def install_proxy_mutation_steps
41
48
  Given('I set the proxy for process {string} to {string}') do |name, operation|
42
49
  process = Nonnative.pool.process_by_name(name)
43
- process.proxy.send(operation)
50
+ Nonnative::Cucumber::Registration.apply_proxy_operation(process.proxy, operation)
44
51
  end
45
52
 
46
53
  Given('I set the proxy for server {string} to {string}') do |name, operation|
47
54
  server = Nonnative.pool.server_by_name(name)
48
- server.proxy.send(operation)
55
+ Nonnative::Cucumber::Registration.apply_proxy_operation(server.proxy, operation)
49
56
  end
50
57
 
51
58
  Given('I set the proxy for service {string} to {string}') do |name, operation|
52
59
  service = Nonnative.pool.service_by_name(name)
53
- service.proxy.send(operation)
60
+ Nonnative::Cucumber::Registration.apply_proxy_operation(service.proxy, operation)
54
61
  end
55
62
  end
56
63
 
@@ -70,9 +77,19 @@ module Nonnative
70
77
  service.proxy.reset
71
78
  end
72
79
  end
80
+
81
+ def apply_proxy_operation(proxy, operation)
82
+ method = PROXY_OPERATIONS.fetch(operation) do
83
+ raise ArgumentError, "Unsupported proxy operation '#{operation}'"
84
+ end
85
+
86
+ proxy.public_send(method)
87
+ end
73
88
  end
74
89
 
75
90
  module LifecycleSteps
91
+ SERVICE_UNAVAILABLE = 'Service Unavailable'
92
+
76
93
  def install_state_steps
77
94
  install_start_step
78
95
  install_attempt_start_step
@@ -110,7 +127,11 @@ module Nonnative
110
127
 
111
128
  Then('I should see {string} as unhealthy') do |service|
112
129
  wait_for { Nonnative.observability.health(opts).code }.to eq(503)
113
- wait_for { Nonnative.observability.health(opts).body }.to include(service)
130
+ wait_for { Nonnative.observability.health(opts).body }.to satisfy do |body|
131
+ body = body.to_s.strip
132
+
133
+ body == SERVICE_UNAVAILABLE || body.include?(service)
134
+ end
114
135
  end
115
136
  end
116
137
 
@@ -119,7 +140,11 @@ module Nonnative
119
140
 
120
141
  Then('I should see {string} as healthy') do |service|
121
142
  wait_for { Nonnative.observability.health(opts).code }.to eq(200)
122
- wait_for { Nonnative.observability.health(opts).body }.to_not include(service)
143
+ wait_for { Nonnative.observability.health(opts).body }.to satisfy do |body|
144
+ body = body.to_s.strip
145
+
146
+ body != SERVICE_UNAVAILABLE && !body.include?(service)
147
+ end
123
148
  end
124
149
  end
125
150
 
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '2.15.0'
7
+ VERSION = '2.16.0'
8
8
  end
data/lib/nonnative.rb CHANGED
@@ -189,7 +189,7 @@ module Nonnative
189
189
  #
190
190
  # @return [Hash{String=>Class}]
191
191
  def proxies
192
- @proxies ||= { 'fault_injection' => Nonnative::FaultInjectionProxy }.freeze
192
+ @proxies ||= { 'fault_injection' => Nonnative::FaultInjectionProxy }
193
193
  end
194
194
 
195
195
  # Resolves a proxy implementation for a configured kind.
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.15.0
4
+ version: 2.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -345,7 +345,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
345
  - !ruby/object:Gem::Version
346
346
  version: '0'
347
347
  requirements: []
348
- rubygems_version: 4.0.10
348
+ rubygems_version: 4.0.11
349
349
  specification_version: 4
350
350
  summary: Allows you to keep using the power of ruby to test other systems
351
351
  test_files: []