nonnative 3.19.0 → 3.26.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 +4 -4
- data/README.md +61 -1
- data/lib/nonnative/bandwidth_socket_pair.rb +42 -0
- data/lib/nonnative/cucumber.rb +1 -0
- data/lib/nonnative/delay_socket_pair.rb +19 -3
- data/lib/nonnative/fault_injection_proxy.rb +13 -0
- data/lib/nonnative/grpc_server.rb +8 -3
- data/lib/nonnative/http_client.rb +12 -0
- data/lib/nonnative/http_proxy_server.rb +61 -9
- data/lib/nonnative/http_server.rb +8 -1
- data/lib/nonnative/jwt_token.rb +8 -5
- data/lib/nonnative/paseto_token.rb +7 -4
- data/lib/nonnative/pool.rb +51 -28
- data/lib/nonnative/process.rb +40 -1
- data/lib/nonnative/runner.rb +9 -0
- data/lib/nonnative/socket_pair_factory.rb +5 -1
- data/lib/nonnative/ssh_token.rb +16 -5
- data/lib/nonnative/token.rb +11 -2
- data/lib/nonnative/version.rb +1 -1
- data/lib/nonnative.rb +4 -14
- metadata +2 -17
- data/.circleci/config.yml +0 -97
- data/.claude/skills +0 -1
- data/.codecov.yml +0 -6
- data/.config/cucumber.yml +0 -1
- data/.editorconfig +0 -13
- data/.github/dependabot.yml +0 -20
- data/.gitignore +0 -18
- data/.gitmodules +0 -3
- data/.goreleaser.yml +0 -17
- data/.rubocop.yml +0 -35
- data/AGENTS.md +0 -208
- data/CLAUDE.md +0 -4
- data/Gemfile +0 -14
- data/Gemfile.lock +0 -232
- data/Makefile +0 -4
- data/nonnative.gemspec +0 -46
data/lib/nonnative/runner.rb
CHANGED
|
@@ -26,6 +26,15 @@ module Nonnative
|
|
|
26
26
|
service.name
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# Describes how the runner terminated before becoming ready, for lifecycle diagnostics.
|
|
30
|
+
#
|
|
31
|
+
# Base runners report nothing; {Nonnative::Process} overrides this to describe an early exit.
|
|
32
|
+
#
|
|
33
|
+
# @return [String, nil]
|
|
34
|
+
def termination
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
protected
|
|
30
39
|
|
|
31
40
|
# Returns the underlying configuration object.
|
|
@@ -13,6 +13,7 @@ module Nonnative
|
|
|
13
13
|
# - `:delay` -> {Nonnative::DelaySocketPair}
|
|
14
14
|
# - `:timeout` -> {Nonnative::TimeoutSocketPair}
|
|
15
15
|
# - `:invalid_data` -> {Nonnative::InvalidDataSocketPair}
|
|
16
|
+
# - `:bandwidth` -> {Nonnative::BandwidthSocketPair}
|
|
16
17
|
#
|
|
17
18
|
# @see Nonnative::FaultInjectionProxy
|
|
18
19
|
# @see Nonnative::SocketPair
|
|
@@ -21,11 +22,12 @@ module Nonnative
|
|
|
21
22
|
# @see Nonnative::DelaySocketPair
|
|
22
23
|
# @see Nonnative::TimeoutSocketPair
|
|
23
24
|
# @see Nonnative::InvalidDataSocketPair
|
|
25
|
+
# @see Nonnative::BandwidthSocketPair
|
|
24
26
|
class SocketPairFactory
|
|
25
27
|
class << self
|
|
26
28
|
# Creates a socket-pair instance for the given proxy state.
|
|
27
29
|
#
|
|
28
|
-
# @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:reset_peer`, `:delay`, `:timeout`, `:invalid_data`)
|
|
30
|
+
# @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:reset_peer`, `:delay`, `:timeout`, `:invalid_data`, `:bandwidth`)
|
|
29
31
|
# @param proxy [Nonnative::ConfigurationProxy] proxy configuration (host/port/options)
|
|
30
32
|
# @return [Nonnative::SocketPair] a socket-pair implementation instance
|
|
31
33
|
def create(kind, proxy)
|
|
@@ -40,6 +42,8 @@ module Nonnative
|
|
|
40
42
|
TimeoutSocketPair
|
|
41
43
|
when :invalid_data
|
|
42
44
|
InvalidDataSocketPair
|
|
45
|
+
when :bandwidth
|
|
46
|
+
BandwidthSocketPair
|
|
43
47
|
else
|
|
44
48
|
SocketPair
|
|
45
49
|
end
|
data/lib/nonnative/ssh_token.rb
CHANGED
|
@@ -30,18 +30,25 @@ module Nonnative
|
|
|
30
30
|
|
|
31
31
|
# Generates a signed SSH token.
|
|
32
32
|
#
|
|
33
|
+
# SSH tokens carry only `iat`/`exp` (in Unix nanoseconds); there is no `nbf` claim, so
|
|
34
|
+
# `not_before` is rejected.
|
|
35
|
+
#
|
|
33
36
|
# @param aud [String] the `aud` claim (for example `"GET /v1/things"` or a gRPC full method)
|
|
37
|
+
# @param issued_at [Time, nil] overrides the `iat` claim (default: now)
|
|
38
|
+
# @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
|
|
39
|
+
# @raise [ArgumentError] if `not_before` is given (SSH tokens have no `nbf` claim)
|
|
34
40
|
# @return [String] the token, `"<base64(claims)>.<base64(signature)>"`
|
|
35
|
-
def generate(aud:, **)
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
def generate(aud:, issued_at: nil, expires_at: nil, not_before: nil, **)
|
|
42
|
+
raise ArgumentError, "ssh tokens do not support 'not_before' (no nbf claim)" unless not_before.nil?
|
|
43
|
+
|
|
44
|
+
iat = nanoseconds(issued_at || Time.now)
|
|
38
45
|
claims = {
|
|
39
46
|
ver: TOKEN_VERSION,
|
|
40
47
|
kid: @key,
|
|
41
48
|
sub: @key,
|
|
42
49
|
aud: aud,
|
|
43
|
-
iat:
|
|
44
|
-
exp:
|
|
50
|
+
iat: iat,
|
|
51
|
+
exp: expires_at ? nanoseconds(expires_at) : iat + (@expiration * 1_000_000_000)
|
|
45
52
|
}.to_json
|
|
46
53
|
|
|
47
54
|
signature = Ed25519::SigningKey.new(seed).sign(claims)
|
|
@@ -51,6 +58,10 @@ module Nonnative
|
|
|
51
58
|
|
|
52
59
|
private
|
|
53
60
|
|
|
61
|
+
def nanoseconds(time)
|
|
62
|
+
(time.to_i * 1_000_000_000) + time.nsec
|
|
63
|
+
end
|
|
64
|
+
|
|
54
65
|
def seed
|
|
55
66
|
SSHData::PrivateKey.parse_openssh(File.read(@private_key)).first.sk[0, 32]
|
|
56
67
|
end
|
data/lib/nonnative/token.rb
CHANGED
|
@@ -54,11 +54,20 @@ module Nonnative
|
|
|
54
54
|
|
|
55
55
|
# Generates a signed token.
|
|
56
56
|
#
|
|
57
|
+
# The optional time claims default to the current time (and the constructor `expiration`), so
|
|
58
|
+
# omitting them reproduces the token's normal claims. Supply them to mint tokens with specific
|
|
59
|
+
# time claims for negative auth tests, such as a not-yet-valid (future `not_before`) or
|
|
60
|
+
# clock-skewed token. Times are absolute; the `ssh` kind has no `nbf` claim and rejects
|
|
61
|
+
# `not_before`.
|
|
62
|
+
#
|
|
57
63
|
# @param aud [String] the `aud` claim
|
|
58
64
|
# @param sub [String] the `sub` claim
|
|
65
|
+
# @param issued_at [Time, nil] overrides the `iat` claim (default: now)
|
|
66
|
+
# @param not_before [Time, nil] overrides the `nbf` claim (default: `issued_at`); unsupported by `ssh`
|
|
67
|
+
# @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
|
|
59
68
|
# @return [String] the signed token
|
|
60
|
-
def generate(aud:, sub:)
|
|
61
|
-
@token.generate(aud: aud, sub: sub)
|
|
69
|
+
def generate(aud:, sub:, issued_at: nil, not_before: nil, expires_at: nil)
|
|
70
|
+
@token.generate(aud: aud, sub: sub, issued_at: issued_at, not_before: not_before, expires_at: expires_at)
|
|
62
71
|
end
|
|
63
72
|
end
|
|
64
73
|
end
|
data/lib/nonnative/version.rb
CHANGED
data/lib/nonnative.rb
CHANGED
|
@@ -115,6 +115,7 @@ require 'nonnative/reset_peer_socket_pair'
|
|
|
115
115
|
require 'nonnative/delay_socket_pair'
|
|
116
116
|
require 'nonnative/timeout_socket_pair'
|
|
117
117
|
require 'nonnative/invalid_data_socket_pair'
|
|
118
|
+
require 'nonnative/bandwidth_socket_pair'
|
|
118
119
|
require 'nonnative/socket_pair_factory'
|
|
119
120
|
require 'nonnative/go_executable'
|
|
120
121
|
require 'nonnative/cucumber'
|
|
@@ -270,10 +271,7 @@ module Nonnative
|
|
|
270
271
|
def start
|
|
271
272
|
@pool ||= Nonnative::Pool.new(configuration)
|
|
272
273
|
errors = []
|
|
273
|
-
errors.concat(@pool.start
|
|
274
|
-
id, started = values
|
|
275
|
-
errors << "Started #{name} with id #{id}, though did not respond in time for #{ports.description}" if !started || !result
|
|
276
|
-
end)
|
|
274
|
+
errors.concat(@pool.start)
|
|
277
275
|
nil
|
|
278
276
|
rescue StandardError => e
|
|
279
277
|
errors << unexpected_lifecycle_error(:start, e)
|
|
@@ -293,11 +291,7 @@ module Nonnative
|
|
|
293
291
|
errors = []
|
|
294
292
|
return if @pool.nil?
|
|
295
293
|
|
|
296
|
-
errors.concat(@pool.stop
|
|
297
|
-
id, stopped = Array(values).then { |v| [v.first, v.fetch(1, true)] }
|
|
298
|
-
errors << "Stopped #{name} with id #{id}, though did not respond in time for #{ports.description}" unless result
|
|
299
|
-
errors << "Stopped #{name} with id #{id}, though the process did not exit in time" unless stopped
|
|
300
|
-
end)
|
|
294
|
+
errors.concat(@pool.stop)
|
|
301
295
|
nil
|
|
302
296
|
rescue StandardError => e
|
|
303
297
|
errors << unexpected_lifecycle_error(:stop, e)
|
|
@@ -362,11 +356,7 @@ module Nonnative
|
|
|
362
356
|
errors = []
|
|
363
357
|
return errors if @pool.nil?
|
|
364
358
|
|
|
365
|
-
errors.concat(@pool.rollback
|
|
366
|
-
id, stopped = Array(values).then { |v| [v.first, v.fetch(1, true)] }
|
|
367
|
-
errors << "Rollback failed for #{name} with id #{id}, because it did not stop in time for #{ports.description}" unless result
|
|
368
|
-
errors << "Rollback failed for #{name} with id #{id}, because the process did not exit in time" unless stopped
|
|
369
|
-
end)
|
|
359
|
+
errors.concat(@pool.rollback)
|
|
370
360
|
rescue StandardError => e
|
|
371
361
|
errors << unexpected_lifecycle_error(:rollback, e)
|
|
372
362
|
ensure
|
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.
|
|
4
|
+
version: 3.26.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alejandro Falkowski
|
|
@@ -391,24 +391,10 @@ executables: []
|
|
|
391
391
|
extensions: []
|
|
392
392
|
extra_rdoc_files: []
|
|
393
393
|
files:
|
|
394
|
-
- ".circleci/config.yml"
|
|
395
|
-
- ".claude/skills"
|
|
396
|
-
- ".codecov.yml"
|
|
397
|
-
- ".config/cucumber.yml"
|
|
398
|
-
- ".editorconfig"
|
|
399
|
-
- ".github/dependabot.yml"
|
|
400
|
-
- ".gitignore"
|
|
401
|
-
- ".gitmodules"
|
|
402
|
-
- ".goreleaser.yml"
|
|
403
|
-
- ".rubocop.yml"
|
|
404
|
-
- AGENTS.md
|
|
405
|
-
- CLAUDE.md
|
|
406
|
-
- Gemfile
|
|
407
|
-
- Gemfile.lock
|
|
408
394
|
- LICENSE
|
|
409
|
-
- Makefile
|
|
410
395
|
- README.md
|
|
411
396
|
- lib/nonnative.rb
|
|
397
|
+
- lib/nonnative/bandwidth_socket_pair.rb
|
|
412
398
|
- lib/nonnative/close_all_socket_pair.rb
|
|
413
399
|
- lib/nonnative/configuration.rb
|
|
414
400
|
- lib/nonnative/configuration_file.rb
|
|
@@ -461,7 +447,6 @@ files:
|
|
|
461
447
|
- lib/nonnative/timeout_socket_pair.rb
|
|
462
448
|
- lib/nonnative/token.rb
|
|
463
449
|
- lib/nonnative/version.rb
|
|
464
|
-
- nonnative.gemspec
|
|
465
450
|
homepage: https://github.com/alexfalkowski/nonnative
|
|
466
451
|
licenses:
|
|
467
452
|
- MIT
|
data/.circleci/config.yml
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
version: 2.1
|
|
2
|
-
|
|
3
|
-
commands:
|
|
4
|
-
sync-submodules:
|
|
5
|
-
steps:
|
|
6
|
-
- run: git submodule sync
|
|
7
|
-
- run: git submodule update --init
|
|
8
|
-
checkout-submodules:
|
|
9
|
-
steps:
|
|
10
|
-
- checkout:
|
|
11
|
-
method: blobless
|
|
12
|
-
- sync-submodules
|
|
13
|
-
|
|
14
|
-
executors:
|
|
15
|
-
ruby:
|
|
16
|
-
docker:
|
|
17
|
-
- image: alexfalkowski/ruby:3.20
|
|
18
|
-
release:
|
|
19
|
-
docker:
|
|
20
|
-
- image: alexfalkowski/release:8.22
|
|
21
|
-
alpine:
|
|
22
|
-
docker:
|
|
23
|
-
- image: alpine:latest
|
|
24
|
-
|
|
25
|
-
jobs:
|
|
26
|
-
build:
|
|
27
|
-
executor: ruby
|
|
28
|
-
working_directory: ~/nonnative
|
|
29
|
-
steps:
|
|
30
|
-
- checkout-submodules
|
|
31
|
-
- run: make source-key
|
|
32
|
-
- restore_cache:
|
|
33
|
-
name: restore deps
|
|
34
|
-
keys:
|
|
35
|
-
- nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
36
|
-
- nonnative-ruby-cache-
|
|
37
|
-
- run: make dep
|
|
38
|
-
- run: make clean-dep
|
|
39
|
-
- save_cache:
|
|
40
|
-
name: save deps
|
|
41
|
-
key: nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
42
|
-
paths:
|
|
43
|
-
- vendor
|
|
44
|
-
- run: make lint
|
|
45
|
-
- run: make sec
|
|
46
|
-
- run: make features
|
|
47
|
-
- run: make benchmarks
|
|
48
|
-
- store_test_results:
|
|
49
|
-
path: test/reports
|
|
50
|
-
- store_artifacts:
|
|
51
|
-
path: test/reports
|
|
52
|
-
- run: make codecov-upload
|
|
53
|
-
resource_class: arm.large
|
|
54
|
-
sync:
|
|
55
|
-
executor: release
|
|
56
|
-
working_directory: ~/nonnative
|
|
57
|
-
steps:
|
|
58
|
-
- checkout-submodules
|
|
59
|
-
- run: make sync push
|
|
60
|
-
resource_class: arm.large
|
|
61
|
-
version:
|
|
62
|
-
executor: release
|
|
63
|
-
working_directory: ~/nonnative
|
|
64
|
-
steps:
|
|
65
|
-
- checkout-submodules
|
|
66
|
-
- run: version
|
|
67
|
-
- run: package
|
|
68
|
-
resource_class: arm.large
|
|
69
|
-
wait-all:
|
|
70
|
-
executor: alpine
|
|
71
|
-
resource_class: small
|
|
72
|
-
steps:
|
|
73
|
-
- run: echo "all applicable jobs finished"
|
|
74
|
-
|
|
75
|
-
workflows:
|
|
76
|
-
nonnative:
|
|
77
|
-
jobs:
|
|
78
|
-
- build
|
|
79
|
-
- sync:
|
|
80
|
-
requires:
|
|
81
|
-
- build
|
|
82
|
-
filters:
|
|
83
|
-
branches:
|
|
84
|
-
ignore: master
|
|
85
|
-
- version:
|
|
86
|
-
context: gh
|
|
87
|
-
serial-group: << pipeline.project.slug >>/version
|
|
88
|
-
requires:
|
|
89
|
-
- build
|
|
90
|
-
filters:
|
|
91
|
-
branches:
|
|
92
|
-
only: master
|
|
93
|
-
- wait-all:
|
|
94
|
-
requires:
|
|
95
|
-
- build
|
|
96
|
-
- sync
|
|
97
|
-
- version
|
data/.claude/skills
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
../bin/skills
|
data/.codecov.yml
DELETED
data/.config/cucumber.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
report: --format junit --out test/reports --format html --out test/reports/index.html --format pretty --publish-quiet
|
data/.editorconfig
DELETED
data/.github/dependabot.yml
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
updates:
|
|
3
|
-
- package-ecosystem: "bundler"
|
|
4
|
-
directory: "/"
|
|
5
|
-
schedule:
|
|
6
|
-
interval: "daily"
|
|
7
|
-
commit-message:
|
|
8
|
-
prefix: "test"
|
|
9
|
-
prefix-development: "build"
|
|
10
|
-
include: "scope"
|
|
11
|
-
- package-ecosystem: "gitsubmodule"
|
|
12
|
-
directory: "/"
|
|
13
|
-
schedule:
|
|
14
|
-
interval: "daily"
|
|
15
|
-
commit-message:
|
|
16
|
-
prefix: "build"
|
|
17
|
-
prefix-development: "build"
|
|
18
|
-
include: "scope"
|
|
19
|
-
allow:
|
|
20
|
-
- dependency-name: "bin"
|
data/.gitignore
DELETED
data/.gitmodules
DELETED
data/.goreleaser.yml
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 4.0
|
|
3
|
-
DisplayCopNames: true
|
|
4
|
-
NewCops: enable
|
|
5
|
-
Exclude:
|
|
6
|
-
- "bin/**/*"
|
|
7
|
-
- "vendor/**/*"
|
|
8
|
-
- "test/grpc/**/*"
|
|
9
|
-
|
|
10
|
-
Layout/LineLength:
|
|
11
|
-
Max: 150
|
|
12
|
-
|
|
13
|
-
Lint/HashCompareByIdentity:
|
|
14
|
-
Enabled: false
|
|
15
|
-
|
|
16
|
-
Metrics/MethodLength:
|
|
17
|
-
Max: 20
|
|
18
|
-
|
|
19
|
-
Metrics/BlockLength:
|
|
20
|
-
Max: 90
|
|
21
|
-
|
|
22
|
-
Metrics/AbcSize:
|
|
23
|
-
Max: 25
|
|
24
|
-
|
|
25
|
-
Metrics/ClassLength:
|
|
26
|
-
Max: 150
|
|
27
|
-
|
|
28
|
-
Style/Documentation:
|
|
29
|
-
Enabled: false
|
|
30
|
-
|
|
31
|
-
Style/NumericPredicate:
|
|
32
|
-
Enabled: false
|
|
33
|
-
|
|
34
|
-
Style/FileOpen:
|
|
35
|
-
Enabled: false
|
data/AGENTS.md
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
## Shared guidance
|
|
4
|
-
|
|
5
|
-
Use `bin/AGENTS.md` for shared skills and cross-repository defaults.
|
|
6
|
-
|
|
7
|
-
`nonnative` is a Ruby gem for end-to-end testing systems implemented in other
|
|
8
|
-
languages. It starts processes, in-process servers, and proxy-only services,
|
|
9
|
-
waits on TCP readiness/shutdown, and can place fault-injection proxies in front
|
|
10
|
-
of dependencies.
|
|
11
|
-
|
|
12
|
-
## Map And Commands
|
|
13
|
-
|
|
14
|
-
- Library: `lib/nonnative/**/*.rb`
|
|
15
|
-
- Cucumber features/support: `features/**/*.feature`, `features/support/**/*.rb`, `features/step_definitions/**/*.rb`
|
|
16
|
-
- Generated gRPC test stubs: `test/grpc/**/*`
|
|
17
|
-
- Test protos: `test/nonnative/v1/*.proto`
|
|
18
|
-
- Build wiring: root `Makefile` includes `bin/build/make/*.mak`
|
|
19
|
-
- Required submodule: `bin/`; missing submodule setup breaks `make`
|
|
20
|
-
- Install deps: `make dep`
|
|
21
|
-
- Lint: `make lint`
|
|
22
|
-
- Security checks: `make sec`
|
|
23
|
-
- Features: `make features`
|
|
24
|
-
- Benchmarks only: `make benchmarks`
|
|
25
|
-
- Cleanup: `make clean-dep`, `make clean-reports`
|
|
26
|
-
|
|
27
|
-
## Intentional Design Choices
|
|
28
|
-
|
|
29
|
-
- The configured HTTP proxy feature intentionally uses the external
|
|
30
|
-
`www.afalkowski.com` host through `features/support/http_proxy_server.rb`.
|
|
31
|
-
Do not flag this external dependency as a code issue unless the task is
|
|
32
|
-
explicitly about making HTTP proxy fixtures fully hermetic.
|
|
33
|
-
- `test/Makefile` intentionally consumes the shared Buf make fragment as-is.
|
|
34
|
-
Do not flag its inherited `breaking` target's `subdir=api` comparison as a
|
|
35
|
-
code issue unless the task is explicitly about changing test proto breaking
|
|
36
|
-
checks.
|
|
37
|
-
- `nonnative` is a test-support gem used across many downstream projects.
|
|
38
|
-
Do not flag the absence of an isolated gem build/install smoke test as a
|
|
39
|
-
test gap unless the task is explicitly about release packaging or gem
|
|
40
|
-
publication validation.
|
|
41
|
-
- The Cucumber `@startup` lifecycle tag is exercised by downstream projects
|
|
42
|
-
that use this gem. Do not flag missing in-repository `@startup` acceptance
|
|
43
|
-
coverage as a test gap unless the task is explicitly about changing
|
|
44
|
-
Cucumber startup hook behavior.
|
|
45
|
-
- `Nonnative.go_argv` and `Nonnative.go_command` flag combinations are checked
|
|
46
|
-
by external/downstream usage. Do not flag missing in-repository exhaustive
|
|
47
|
-
`-test.*` flag or tool-filtering assertions as a test gap unless the task is
|
|
48
|
-
explicitly about changing Go command generation.
|
|
49
|
-
- Generated gRPC test stubs under `test/grpc/` are updated on demand when the
|
|
50
|
-
test proto changes. Do not flag the absence of an automatic generated-stub
|
|
51
|
-
freshness check as a test gap unless the task is explicitly about changing
|
|
52
|
-
test proto generation or generated-file validation.
|
|
53
|
-
- Generated gRPC test stubs may carry manual require-path adjustments after
|
|
54
|
-
generation. Do not treat `make -C test stale` as a required validation target
|
|
55
|
-
unless the task is explicitly about changing test proto generation; verify
|
|
56
|
-
the generated Ruby loads before accepting generator-only rewrites.
|
|
57
|
-
- Buf linting for the test-only proto module is intentionally not part of the
|
|
58
|
-
required local validation surface. Do not flag missing root-level validation
|
|
59
|
-
for `test/buf.yaml` as a test gap unless the task is explicitly about test
|
|
60
|
-
proto linting or Buf validation.
|
|
61
|
-
- The public proxy reset Cucumber steps and `@reset` hook are tested by
|
|
62
|
-
external/downstream suites. Do not flag missing in-repository direct proxy
|
|
63
|
-
reset step coverage as a test gap unless the task is explicitly about
|
|
64
|
-
changing proxy reset behavior.
|
|
65
|
-
- `Nonnative.clear` configuration and pool reset behavior is covered by
|
|
66
|
-
external/downstream suites. Do not flag missing in-repository assertions for
|
|
67
|
-
configuration or pool clearing as a test gap unless the task is explicitly
|
|
68
|
-
about changing clear/reset lifecycle behavior.
|
|
69
|
-
- Full repository validation is expected to run both regular features and
|
|
70
|
-
benchmark-tagged lifecycle scenarios. Do not flag rollback/timeout lifecycle
|
|
71
|
-
coverage living under `features/benchmark.feature` as a test gap unless the
|
|
72
|
-
task is explicitly about changing validation target composition.
|
|
73
|
-
- Service proxy round-trip behavior is validated by downstream projects that
|
|
74
|
-
use `nonnative` against real dependencies. Do not flag the in-repository
|
|
75
|
-
service proxy success scenario's connection-only assertion as a test gap
|
|
76
|
-
unless the task is explicitly about changing service proxy forwarding.
|
|
77
|
-
- `Nonnative::FaultInjectionProxy` owns its listener thread and stops it by
|
|
78
|
-
closing the owned `TCPServer`, which wakes the accept loop before joining the
|
|
79
|
-
thread. Do not flag the listener `join` as an unbounded reliability gap unless
|
|
80
|
-
the task is explicitly about proxy shutdown behavior or there is a normal-use
|
|
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.
|
|
108
|
-
|
|
109
|
-
## Runtime Model
|
|
110
|
-
|
|
111
|
-
Public entry point: `lib/nonnative.rb`.
|
|
112
|
-
|
|
113
|
-
Main API: `configure`, `start`, `stop`, `clear`, `reset`, `pool`,
|
|
114
|
-
`go_argv`, `go_command`, `token`.
|
|
115
|
-
|
|
116
|
-
Configuration is `Nonnative::Configuration`, built with
|
|
117
|
-
`config.process`, `config.server`, `config.service`, or
|
|
118
|
-
`config.load_file(...)`.
|
|
119
|
-
|
|
120
|
-
Runners:
|
|
121
|
-
|
|
122
|
-
- `Nonnative::Process`: OS process
|
|
123
|
-
- `Nonnative::Server`: in-process Ruby server thread
|
|
124
|
-
- `Nonnative::Service`: proxy lifecycle for an externally managed dependency
|
|
125
|
-
|
|
126
|
-
`Nonnative::Pool` starts services first, then servers/processes, and stops in
|
|
127
|
-
reverse. Readiness and shutdown checks are TCP-only via
|
|
128
|
-
`Nonnative::Port#open?` and `#closed?`.
|
|
129
|
-
|
|
130
|
-
Token generation: `Nonnative.token(kind:, issuer:, key:, private_key:, expiration:)`
|
|
131
|
-
returns a `Nonnative::Token` whose `generate(aud:, sub:)` produces a signed token for
|
|
132
|
-
authenticating against services under test; it feeds `Nonnative::Header.auth_bearer`.
|
|
133
|
-
Kinds are `jwt` (EdDSA, `kid` header), `paseto` (v4.public, `kid` footer), and `ssh`
|
|
134
|
-
(go-service style raw-Ed25519 `base64(claims).base64(signature)`). All Ed25519 and
|
|
135
|
-
generation-only. `jwt`/`paseto` take a PKCS#8 PEM key; `ssh` takes an OpenSSH-format
|
|
136
|
-
key. PASETO needs system libsodium (via `rbnacl`), required lazily so `require
|
|
137
|
-
'nonnative'` works without it until a PASETO token is generated.
|
|
138
|
-
`Nonnative::Token.http_audience` / `grpc_audience` build the endpoint-scoped `aud`.
|
|
139
|
-
|
|
140
|
-
## Cucumber Surface
|
|
141
|
-
|
|
142
|
-
`lib/nonnative/cucumber.rb` is public compatibility surface. Do not remove or
|
|
143
|
-
rename hooks/step text unless the user explicitly requests a breaking change.
|
|
144
|
-
|
|
145
|
-
Lifecycle tags:
|
|
146
|
-
|
|
147
|
-
- `@startup`: start before scenario, stop after scenario
|
|
148
|
-
- `@manual`: scenario starts manually, stop after scenario
|
|
149
|
-
- `@clear`: call `Nonnative.clear` before scenario
|
|
150
|
-
- `@reset`: reset proxies after scenario
|
|
151
|
-
|
|
152
|
-
Suite taxonomy tags: `@acceptance`, `@contract`, `@proxy`, `@config`,
|
|
153
|
-
`@service`, `@benchmark`, `@slow`. `make features` excludes `@benchmark`;
|
|
154
|
-
`make benchmarks` runs only `@benchmark`.
|
|
155
|
-
|
|
156
|
-
`Nonnative.clear` clears configuration, logger, observability client, and pool.
|
|
157
|
-
`require 'nonnative'` loads Cucumber integration lazily and is safe outside a
|
|
158
|
-
booted Cucumber runtime. For start-once-per-test-run, use
|
|
159
|
-
`require 'nonnative/startup'`.
|
|
160
|
-
|
|
161
|
-
## Proxy And Config Gotchas
|
|
162
|
-
|
|
163
|
-
Proxy wiring is the easiest mistake:
|
|
164
|
-
|
|
165
|
-
- Runner `host` / `port` are client-facing and used for readiness/shutdown
|
|
166
|
-
- For `fault_injection`, nested `proxy.host` / `proxy.port` are the upstream target
|
|
167
|
-
- Clients connect to runner `host` / `port` when a proxy is enabled
|
|
168
|
-
|
|
169
|
-
Proxy kinds: `none`, `fault_injection`.
|
|
170
|
-
Fault-injection states: `none`, `close_all`, `delay`, `invalid_data`.
|
|
171
|
-
|
|
172
|
-
Config rules:
|
|
173
|
-
|
|
174
|
-
- YAML config is loaded as data only via `Nonnative::ConfigurationFile`; ERB is not evaluated and arbitrary Ruby object tags are rejected
|
|
175
|
-
- Runner `host` and nested `proxy.host` default to `127.0.0.1`; use explicit `0.0.0.0` only when external access is intended
|
|
176
|
-
- Process `command` can be a legacy shell string or an argv array; prefer argv arrays for new config, and `go:` config builds argv internally with `Nonnative.go_argv`
|
|
177
|
-
- Use `Nonnative.go_argv` for no-shell Go executable argv entries and `Nonnative.go_command` only when a caller needs Ruby shell-style command string spawning
|
|
178
|
-
- YAML services belong under `services:`, not `processes:`
|
|
179
|
-
- There is no top-level `config.wait`; `wait` is per runner
|
|
180
|
-
- Programmatic service config uses `config.service do |s| ... end`, so use `s.host` / `s.port`
|
|
181
|
-
- Proxy examples need both endpoint sides: runner `host` / `port` for the proxy, nested `proxy.host` / `proxy.port` for upstream
|
|
182
|
-
|
|
183
|
-
## Fixtures And Limitations
|
|
184
|
-
|
|
185
|
-
Useful fixtures:
|
|
186
|
-
|
|
187
|
-
- Process: `features/support/bin/start`
|
|
188
|
-
- HTTP: `features/support/http_server.rb`, `features/support/http_proxy_server.rb`
|
|
189
|
-
- TCP: `features/support/tcp_server.rb`
|
|
190
|
-
- gRPC: `features/support/grpc_server.rb`, generated stubs in `test/grpc/`
|
|
191
|
-
|
|
192
|
-
Limitations:
|
|
193
|
-
|
|
194
|
-
- The `grpc` Ruby library uses a global logger; per-server gRPC loggers are not really supported
|
|
195
|
-
- Local Ruby/Bundler mismatches can break native extensions on macOS
|
|
196
|
-
- Coverage and Cucumber reports go under `test/reports`
|
|
197
|
-
- Port checks can be flaky if tests reuse ports unexpectedly
|
|
198
|
-
|
|
199
|
-
## Look First
|
|
200
|
-
|
|
201
|
-
- Lifecycle: `lib/nonnative.rb`, `lib/nonnative/pool.rb`
|
|
202
|
-
- Readiness/timeouts: `lib/nonnative/port.rb`, `lib/nonnative/timeout.rb`
|
|
203
|
-
- Process lifecycle: `lib/nonnative/process.rb`
|
|
204
|
-
- Go executable command/argv building: `lib/nonnative/go_executable.rb`
|
|
205
|
-
- Token generation: `lib/nonnative/token.rb`, `lib/nonnative/jwt_token.rb`, `lib/nonnative/paseto_token.rb`, `lib/nonnative/ssh_token.rb`, `lib/nonnative/ed25519_key.rb`
|
|
206
|
-
- Proxies: `lib/nonnative/fault_injection_proxy.rb`, `lib/nonnative/socket_pair_factory.rb`
|
|
207
|
-
- Cucumber: `lib/nonnative/cucumber.rb`, `lib/nonnative/startup.rb`, `features/support/env.rb`
|
|
208
|
-
- Config loading: `lib/nonnative/configuration.rb`, `lib/nonnative/configuration_file.rb`, `lib/nonnative/configuration_runner.rb`, `lib/nonnative/configuration_proxy.rb`
|
data/CLAUDE.md
DELETED
data/Gemfile
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source 'https://rubygems.org'
|
|
4
|
-
|
|
5
|
-
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
6
|
-
|
|
7
|
-
# Specify your gem's dependencies in nonnative.gemspec
|
|
8
|
-
gemspec
|
|
9
|
-
|
|
10
|
-
gem 'bundler'
|
|
11
|
-
gem 'rubocop'
|
|
12
|
-
gem 'ruby-lsp'
|
|
13
|
-
gem 'simplecov'
|
|
14
|
-
gem 'simplecov-cobertura'
|