haveapi 0.32.1 → 0.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -8
  3. data/README.md +3 -422
  4. data/Rakefile +17 -7
  5. data/benchmarks/performance_compare.rb +27 -6
  6. data/doc/cs/client-examples.md +535 -0
  7. data/doc/cs/create-client.md +118 -0
  8. data/doc/cs/json-schema.html +1182 -0
  9. data/doc/cs/protocol.md +595 -0
  10. data/doc/cs/readme.md +432 -0
  11. data/doc/cs/typed-input-validation.md +122 -0
  12. data/doc/{client-examples.md → en/client-examples.md} +20 -10
  13. data/doc/{documentation-ui.md → en/documentation-ui.md} +42 -6
  14. data/doc/{performance.md → en/performance.md} +11 -0
  15. data/doc/{protocol.md → en/protocol.md} +2 -2
  16. data/doc/en/readme.md +423 -0
  17. data/haveapi.gemspec +2 -2
  18. data/lib/haveapi/client_example.rb +18 -5
  19. data/lib/haveapi/client_examples/curl.rb +5 -5
  20. data/lib/haveapi/client_examples/dynamic_helpers.rb +10 -10
  21. data/lib/haveapi/client_examples/go_client.rb +1 -1
  22. data/lib/haveapi/client_examples/http.rb +4 -4
  23. data/lib/haveapi/client_examples/js_client.rb +10 -10
  24. data/lib/haveapi/client_examples/php_client.rb +24 -25
  25. data/lib/haveapi/client_examples/python_client.rb +11 -11
  26. data/lib/haveapi/client_examples/ruby_cli.rb +6 -9
  27. data/lib/haveapi/client_examples/ruby_client.rb +24 -16
  28. data/lib/haveapi/client_examples/rust_client.rb +1 -1
  29. data/lib/haveapi/client_examples/ts_generated_client.rb +1 -1
  30. data/lib/haveapi/documentation_context.rb +14 -2
  31. data/lib/haveapi/documentation_helpers.rb +92 -15
  32. data/lib/haveapi/documentation_locale.rb +65 -0
  33. data/lib/haveapi/documentation_oauth.rb +12 -12
  34. data/lib/haveapi/documentation_search.rb +5 -5
  35. data/lib/haveapi/documentation_validators.rb +43 -33
  36. data/lib/haveapi/locales/cs.yml +468 -0
  37. data/lib/haveapi/locales/en.yml +467 -0
  38. data/lib/haveapi/public/css/documentation.css +8 -4
  39. data/lib/haveapi/public/js/documentation.js +73 -20
  40. data/lib/haveapi/server.rb +32 -47
  41. data/lib/haveapi/shared_documentation_helpers.rb +53 -5
  42. data/lib/haveapi/version.rb +1 -1
  43. data/lib/haveapi/views/doc_layout.erb +3 -2
  44. data/lib/haveapi/views/doc_sidebars/create-client.erb +10 -10
  45. data/lib/haveapi/views/doc_sidebars/json-schema.erb +2 -2
  46. data/lib/haveapi/views/doc_sidebars/protocol.erb +24 -24
  47. data/lib/haveapi/views/documentation/search_form.erb +5 -4
  48. data/lib/haveapi/views/documentation/search_results.erb +6 -6
  49. data/lib/haveapi/views/index.erb +3 -3
  50. data/lib/haveapi/views/main_layout.erb +4 -4
  51. data/lib/haveapi/views/shared_documentation.erb +8 -5
  52. data/lib/haveapi/views/shared_sidebar.erb +11 -11
  53. data/lib/haveapi/views/version_page/auth_body.erb +7 -7
  54. data/lib/haveapi/views/version_page/client_auth.erb +1 -1
  55. data/lib/haveapi/views/version_page/client_example.erb +1 -1
  56. data/lib/haveapi/views/version_page/client_init.erb +1 -1
  57. data/lib/haveapi/views/version_page/client_overview.erb +7 -5
  58. data/lib/haveapi/views/version_page/example_panels.erb +5 -2
  59. data/lib/haveapi/views/version_page/resource_body.erb +24 -24
  60. data/lib/haveapi/views/version_page/resource_directory.erb +2 -2
  61. data/lib/haveapi/views/version_page/usage.erb +5 -5
  62. data/lib/haveapi/views/version_page.erb +11 -11
  63. data/lib/haveapi/views/version_sidebar/auth_nav.erb +2 -2
  64. data/lib/haveapi/views/version_sidebar/authentication_control.erb +8 -8
  65. data/lib/haveapi/views/version_sidebar/authentication_status.erb +1 -1
  66. data/lib/haveapi/views/version_sidebar/brand.erb +16 -5
  67. data/lib/haveapi/views/version_sidebar.erb +33 -18
  68. data/spec/client_examples/assets/documentation_test.cjs +107 -9
  69. data/spec/client_examples/comment_capture.rb +72 -0
  70. data/spec/client_examples/comments_spec.rb +162 -0
  71. data/spec/client_examples/context_spec.rb +18 -2
  72. data/spec/client_examples/detail_spec.rb +68 -0
  73. data/spec/documentation/client_overview_spec.rb +60 -0
  74. data/spec/documentation/example_selection_spec.rb +192 -0
  75. data/spec/documentation/language_settings_spec.rb +132 -0
  76. data/spec/documentation/locale_spec.rb +367 -0
  77. data/spec/documentation/navigation_spec.rb +61 -3
  78. data/spec/documentation/oauth_spec.rb +42 -0
  79. data/spec/documentation/search_index_routes_spec.rb +8 -4
  80. data/spec/documentation/search_spec.rb +1 -1
  81. data/spec/documentation/shared_pages_spec.rb +63 -2
  82. data/spec/documentation/validators_spec.rb +25 -0
  83. data/spec/extensions/exception_mailer_spec.rb +2 -2
  84. data/spec/packaging_spec.rb +68 -9
  85. data/spec/performance/event_baseline_spec.rb +1 -1
  86. data/spec/performance/source_versions_spec.rb +111 -2
  87. metadata +24 -11
  88. /data/doc/{create-client.md → en/create-client.md} +0 -0
  89. /data/doc/{documentation-oauth.md → en/documentation-oauth.md} +0 -0
  90. /data/doc/{json-schema.html → en/json-schema.html} +0 -0
  91. /data/doc/{typed-input-validation.md → en/typed-input-validation.md} +0 -0
@@ -120,4 +120,29 @@ describe HaveAPI::DocumentationValidators do
120
120
  expect(render(payload, { 'extra' => payload })).to include('<th scope="row">&lt;script&gt;&amp;&quot;</th>')
121
121
  expect(Marshal.dump(options)).to eq(before)
122
122
  end
123
+
124
+ it 'localizes framework presentation while preserving metadata and machine values' do
125
+ validators = {
126
+ present: { empty: false }, include: { values: { 'ready' => 'Application label' } },
127
+ number: { min: 2, step: 3, even: true }, length: { min: 2, max: 8 },
128
+ custom: 'Application rule', extension: { 'supplied' => 'Untranslated metadata' }
129
+ }
130
+ before = Marshal.dump(validators)
131
+ html = I18n.with_locale(:cs) { described_class.render(validators, type: 'String') }
132
+
133
+ expect(html).to include('<caption>Validační pravidla</caption>', '<th scope="col">Pravidlo</th>',
134
+ '<th scope="col">Podrobnosti</th>', 'Musí být uvedeno', 'Povolené hodnoty',
135
+ '<code>ready</code>: Application label', 'Application rule', 'Untranslated metadata')
136
+ expect(html).not_to include('Validation rules', 'Allowed values', 'Minimum length:')
137
+ expect(Marshal.dump(validators)).to eq(before)
138
+ end
139
+
140
+ it 'escapes translated text and interpolated metadata independently' do
141
+ html = I18n.with_locale(:cs) do
142
+ render(:confirm, { parameter: '<script>&', equal: false })
143
+ end
144
+
145
+ expect(html).to include('Musí se lišit od parametru <code>&lt;script&gt;&amp;</code>.')
146
+ expect(html).not_to include('<script>', '&amp;lt;')
147
+ end
123
148
  end
@@ -125,7 +125,7 @@ describe HaveAPI::Extensions::ExceptionMailer do
125
125
  it 'does not mail a missing JSON schema documentation file' do
126
126
  mailer = new_mailer
127
127
  calls = collect_mail_calls(mailer)
128
- doc_path = File.join(app.settings.root, '../../doc/json-schema.html')
128
+ doc_path = File.expand_path('../../doc/en/json-schema.html', app.settings.root)
129
129
  allow(File).to receive(:read).and_call_original
130
130
  allow(File).to receive(:read).with(doc_path).and_raise(Errno::ENOENT, doc_path)
131
131
 
@@ -142,7 +142,7 @@ describe HaveAPI::Extensions::ExceptionMailer do
142
142
  calls = collect_mail_calls(mailer)
143
143
  app.set :raise_errors, false
144
144
  app.set :show_exceptions, false
145
- doc_path = File.join(app.settings.root, '../../doc/json-schema.html')
145
+ doc_path = File.expand_path('../../doc/en/json-schema.html', app.settings.root)
146
146
  allow(File).to receive(:read).and_call_original
147
147
  allow(File).to receive(:read).with(doc_path).and_raise(Errno::EACCES, doc_path)
148
148
 
@@ -9,7 +9,12 @@ require 'tmpdir'
9
9
 
10
10
  describe HaveAPI::Server do
11
11
  def required_docs
12
- %w[json-schema.html protocol.md create-client.md typed-input-validation.md client-examples.md protocol.png]
12
+ %w[protocol.png en/documentation-ui.md en/documentation-oauth.md en/performance.md] +
13
+ %w[en cs].flat_map do |locale|
14
+ %w[readme.md protocol.md create-client.md typed-input-validation.md client-examples.md json-schema.html].map do |name|
15
+ "#{locale}/#{name}"
16
+ end
17
+ end
13
18
  end
14
19
 
15
20
  before(:context) do
@@ -22,14 +27,15 @@ describe HaveAPI::Server do
22
27
  Rake::Task[:build].invoke
23
28
  end
24
29
 
25
- %i[missing empty].each do |condition|
26
- it "rejects a direct build with #{condition} shared documentation" do
30
+ %w[en/json-schema.html cs/json-schema.html cs/protocol.md].product(%i[missing empty]).each do |document, condition|
31
+ it "rejects a direct build with #{condition} #{document}" do
27
32
  Dir.mktmpdir('haveapi-doc-check') do |dir|
28
33
  FileUtils.mkdir_p(File.join(dir, 'doc'))
29
34
  required_docs.each do |name|
30
- next if name == 'json-schema.html' && condition == :missing
35
+ next if name == document && condition == :missing
31
36
 
32
- content = name == 'json-schema.html' && condition == :empty ? '' : 'document'
37
+ content = name == document && condition == :empty ? '' : 'document'
38
+ FileUtils.mkdir_p(File.dirname(File.join(dir, 'doc', name)))
33
39
  File.write(File.join(dir, 'doc', name), content)
34
40
  end
35
41
 
@@ -38,12 +44,42 @@ describe HaveAPI::Server do
38
44
  expect { invoke_build }.to raise_error(SystemExit) do |error|
39
45
  expect(error.status).to eq(1)
40
46
  end
41
- end.to output(/json-schema.html.*Run `make doc`/).to_stderr
47
+ end.to output(/#{Regexp.escape(document)}.*Run `make doc`/).to_stderr
42
48
  end
43
49
  end
44
50
  end
45
51
  end
46
52
 
53
+ it 'copies localized schema documentation without application gems or Bundler' do
54
+ root = File.expand_path('../../..', __dir__)
55
+ Dir.mktmpdir('haveapi-copydoc') do |dir|
56
+ destination = File.join(dir, 'server')
57
+ empty_gems = File.join(dir, 'empty-gems')
58
+ FileUtils.mkdir_p(empty_gems)
59
+ environment = {
60
+ 'GEM_HOME' => empty_gems,
61
+ 'GEM_PATH' => empty_gems,
62
+ 'RUBYLIB' => nil,
63
+ 'RUBYOPT' => nil,
64
+ 'RUBYGEMS_GEMDEPS' => nil
65
+ }
66
+ stdout, stderr, status = Bundler.with_unbundled_env do
67
+ Open3.capture3(environment, File.join(root, 'utils/copydoc.sh'), destination, chdir: root)
68
+ end
69
+ expect(status.success?).to be(true), "Isolated documentation copy failed: #{stdout}#{stderr}"
70
+
71
+ schemas = %w[en cs].map do |locale|
72
+ body = File.read(File.join(destination, 'doc', locale, 'json-schema.html'))
73
+ heading = ::I18n.t('haveapi.documentation.schema.envelope', locale:, raise: true)
74
+ expect(body).to include(%(<h1 id="envelope">#{ERB::Util.html_escape(heading)}</h1>))
75
+ code = body.scan(%r{<pre><code>(.*?)</code></pre>}m).flatten
76
+ expect(code.length).to eq(4)
77
+ code
78
+ end
79
+ expect(schemas[0]).to eq(schemas[1])
80
+ end
81
+ end
82
+
47
83
  it 'ships and serves shared documentation from the built gem' do
48
84
  invoke_build
49
85
  gem_path = File.expand_path("../pkg/haveapi-#{HaveAPI::VERSION}.gem", __dir__)
@@ -85,13 +121,18 @@ describe HaveAPI::Server do
85
121
  server.use_version(1)
86
122
  server.mount('/')
87
123
  request = Rack::MockRequest.new(server.app)
88
- responses = %w[/ /doc /doc/index /doc/index.md /doc/readme /doc/json-schema /doc/protocol.md /doc/create-client.md /doc/client-examples.md /js/highlight-LICENSE].to_h do |path|
89
- response = request.get(path, 'HTTP_HOST' => 'localhost')
124
+ responses = %w[/ /doc /doc/index /doc/index.md /doc/readme /doc/json-schema /doc/protocol.md /doc/create-client.md /doc/client-examples.md /doc/typed-input-validation /js/highlight-LICENSE].to_h do |path|
125
+ response = request.get(path, 'HTTP_HOST' => 'localhost', 'HTTP_ACCEPT_LANGUAGE' => 'en')
90
126
  [path, { status: response.status, type: response.content_type, cache_control: response['Cache-Control'], body: response.body }]
91
127
  end
128
+ translated = %w[/doc /doc/index /doc/index.md /doc/readme /doc/protocol.md /doc/create-client.md /doc/typed-input-validation /doc/client-examples.md /doc/json-schema /doc/documentation-ui].to_h do |path|
129
+ response = request.get(path, 'HTTP_HOST' => 'localhost', 'HTTP_ACCEPT_LANGUAGE' => 'cs-CZ')
130
+ [path, { status: response.status, body: response.body }]
131
+ end
92
132
  puts JSON.generate({
93
133
  framework: HaveAPI::Server.instance_method(:mount).source_location.first,
94
- responses: responses
134
+ responses: responses,
135
+ translated: translated
95
136
  })
96
137
  RUBY
97
138
 
@@ -128,6 +169,7 @@ describe HaveAPI::Server do
128
169
  '/doc/protocol.md' => 'Protocol definition',
129
170
  '/doc/create-client.md' => 'Client definition',
130
171
  '/doc/client-examples.md' => 'Client examples',
172
+ '/doc/typed-input-validation' => 'Typed input validation',
131
173
  '/js/highlight-LICENSE' => 'Copyright'
132
174
  }.each do |path, heading|
133
175
  response = result.fetch('responses').fetch(path)
@@ -140,6 +182,23 @@ describe HaveAPI::Server do
140
182
  expect(response.fetch('body')).to include('data-docs', '/js/documentation.js', '/css/documentation.css')
141
183
  expect(response.fetch('body')).not_to include('/js/main.js', '/css/bootstrap')
142
184
  end
185
+ {
186
+ '/doc' => 'Serverová implementace',
187
+ '/doc/index' => 'Serverová implementace',
188
+ '/doc/index.md' => 'Serverová implementace',
189
+ '/doc/readme' => 'Serverová implementace',
190
+ '/doc/protocol.md' => 'Definice protokolu',
191
+ '/doc/create-client.md' => 'Definice klienta',
192
+ '/doc/typed-input-validation' => 'Validace typovaných vstupů',
193
+ '/doc/client-examples.md' => 'Klientské příklady',
194
+ '/doc/json-schema' => 'Obálka',
195
+ '/doc/documentation-ui' => 'Browsing API documentation'
196
+ }.each do |path, heading|
197
+ response = result.fetch('translated').fetch(path)
198
+ expect(response.fetch('status')).to eq(200), "Packaged Czech route failed: #{path}"
199
+ expect(response.fetch('body')).to include('lang="cs"', heading)
200
+ end
201
+ expect(result.fetch('translated').fetch('/doc/documentation-ui').fetch('body')).to include('lang="en"')
143
202
  %w[/doc /doc/index /doc/index.md /doc/readme].each do |path|
144
203
  body = result.fetch('responses').fetch(path).fetch('body')
145
204
  expect(body).to include('<title>HaveAPI internals · ', 'Server framework features',
@@ -344,7 +344,7 @@ RSpec.describe HaveAPI::PerformanceCI do
344
344
  %w[flake.lock Gemfile Makefile .ruby-version servers/ruby/benchmarks/endpoints.rb
345
345
  servers/ruby/benchmarks/Gemfile servers/ruby/benchmarks/Gemfile.lock servers/ruby/benchmarks/shell.nix
346
346
  servers/ruby/test_support/performance_sqlite_fixture.rb clients/ruby/Gemfile clients/rust/Gemfile
347
- doc/performance.md utils/performance_ci.rb .github/workflows/servers-ruby-performance.yml
347
+ doc/en/performance.md utils/performance_ci.rb .github/workflows/servers-ruby-performance.yml
348
348
  servers/ruby/benchmarks/spec/client_examples_spec.rb servers/ruby/test_support/all_client_performance_fixture.rb
349
349
  clients/go/lib/haveapi/go_client/documentation.rb clients/typescript/lib/haveapi/typescript_client/documentation.rb
350
350
  clients/js/src/client.ts clients/python/haveapi/client.py clients/php/src/Client.php clients/new-client/renderer.rb].each do |path|
@@ -10,6 +10,8 @@ require_relative 'support/samples'
10
10
  describe HaveAPI::PerformanceCI::Campaign do
11
11
  let(:root) { File.expand_path('../../../..', __dir__) }
12
12
  let(:error) { HaveAPI::Performance::ContractError }
13
+ let(:shallow_files) { " s.files = `git ls-files -z`.split(\"\\x0\") + Dir.glob('doc/*')\n" }
14
+ let(:recursive_files) { " s.files = `git ls-files -z`.split(\"\\x0\") + Dir.glob('doc/**/*').select { |path| File.file?(path) }\n" }
13
15
 
14
16
  before do
15
17
  # These metadata fixtures evaluate real gemspecs without loading the optional
@@ -44,6 +46,12 @@ describe HaveAPI::PerformanceCI::Campaign do
44
46
  end
45
47
  end
46
48
 
49
+ def with_files(contents, statement)
50
+ path = 'servers/ruby/haveapi.gemspec'
51
+ previous = contents.fetch(path).lines.find { |line| line.start_with?(' s.files ') }
52
+ contents.merge(path => contents.fetch(path).sub(previous, statement))
53
+ end
54
+
47
55
  def prepare(baseline, candidate)
48
56
  Dir.mktmpdir('haveapi-own-versions-') do |directory|
49
57
  roots = { baseline: File.join(directory, 'baseline'), candidate: File.join(directory, 'candidate') }
@@ -82,11 +90,12 @@ describe HaveAPI::PerformanceCI::Campaign do
82
90
  end
83
91
  end
84
92
 
85
- def replay_samples(baseline = '0.31.0')
93
+ def replay_samples(baseline = '0.31.0', selections: {}, normalize_files: true)
86
94
  samples, budgets = PerformanceSamples.fixture
87
95
  samples.fetch('sources').each do |role, source|
88
96
  contents = source_contents(role == 'baseline' ? baseline : '0.31.1').slice(*HaveAPI::Performance::SourceVersions::PATHS)
89
- receipt = HaveAPI::Performance::SourceVersions.capture(contents)
97
+ contents = with_files(contents, selections.fetch(role)) if selections.has_key?(role)
98
+ receipt = HaveAPI::Performance::SourceVersions.capture(contents, normalize_files:)
90
99
  source['files_sha256'].merge!(receipt.fetch('raw_sha256'))
91
100
  samples.fetch('observations').each_value do |observation|
92
101
  next unless observation.fetch('source').fetch('root') == source.fetch('root')
@@ -165,4 +174,104 @@ describe HaveAPI::PerformanceCI::Campaign do
165
174
  samples, budgets = PerformanceSamples.fixture
166
175
  expect(compare(samples, JSON.parse(budgets)).fetch('status')).to eq('pass')
167
176
  end
177
+
178
+ it 'accepts only the reviewed packaging transition in preparation and live input capture' do
179
+ contents = { baseline: with_files(source_contents('0.31.1'), shallow_files),
180
+ candidate: with_files(source_contents('0.31.1'), recursive_files) }
181
+ prepare(contents[:baseline], contents[:candidate]) do |roots|
182
+ retained = JSON.parse(File.read(File.join(File.dirname(roots[:baseline]), 'dependency-compatibility.json')))
183
+ expect(retained['baseline']['gemspecs']).to eq(retained['candidate']['gemspecs'])
184
+ receipts = retained.values.map { |input| input.fetch('source_versions') }
185
+ expect(receipts.first.fetch('raw_sha256')).not_to eq(receipts.last.fetch('raw_sha256'))
186
+ expect(receipts.first.fetch('normalized_sha256')).to eq(receipts.last.fetch('normalized_sha256'))
187
+ expect(receipts.map { |receipt| receipt.fetch('file_selection') }).to eq(%w[documentation-files-v1 documentation-files-v1])
188
+
189
+ graphs = roots.map do |role, directory|
190
+ %w[HaveAPI::VERSION HaveAPI::Client::VERSION HaveAPI::RustClient::VERSION].each { |constant| stub_const(constant, '0.31.1') }
191
+ inputs = JSON.parse(JSON.generate(HaveAPI::Performance::Worker.inputs(directory, 'quick', benchmark: true)))
192
+ hashes = contents.fetch(role).transform_values { |text| Digest::SHA256.hexdigest(text) }
193
+ expect(inputs.fetch('source_versions')).to eq(retained.fetch(role.to_s).fetch('source_versions'))
194
+ expect do
195
+ HaveAPI::Performance.validate_input_receipts(inputs, { 'files_sha256' => hashes }, profile: 'quick', lineage: nil)
196
+ end.not_to raise_error
197
+ HaveAPI::Performance::Release.graph(inputs)
198
+ end
199
+ expect(graphs.first).to eq(graphs.last)
200
+ end
201
+ end
202
+
203
+ it 'replays the exact old/new packaging pair while retaining raw source hashes' do
204
+ samples, budgets = replay_samples('0.31.1', selections: { 'baseline' => shallow_files, 'candidate' => recursive_files })
205
+
206
+ expect(compare(samples, budgets).fetch('status')).to eq('pass')
207
+ path = 'servers/ruby/haveapi.gemspec'
208
+ expect(samples['sources']['baseline']['files_sha256'][path]).not_to eq(samples['sources']['candidate']['files_sha256'][path])
209
+ end
210
+
211
+ it 'keeps all bytes outside the exact packaging statement and evaluated requirements strict' do
212
+ original = with_files(source_contents('0.31.0'), shallow_files)
213
+ candidate = with_files(source_contents('0.31.1'), recursive_files)
214
+ path = 'servers/ruby/haveapi.gemspec'
215
+ changes = [
216
+ candidate.merge(path => candidate.fetch(path).sub('Framework for creating', 'Framework for describing')),
217
+ candidate.merge(path => candidate.fetch(path).sub("'activesupport', '>= 7.1'", "'activesupport', '>= 7.0'")),
218
+ candidate.merge(path => "#{candidate.fetch(path)}\n# unrelated comment\n"),
219
+ candidate.merge('.ruby-version' => "3.2.1\n")
220
+ ]
221
+ changes.each do |changed|
222
+ expect { prepare(original, changed) }.to raise_error(HaveAPI::PerformanceCI::Error, /Dependency inputs differ/)
223
+ end
224
+ end
225
+
226
+ it 'does not normalize unknown, absent, duplicated or modified file statements' do
227
+ contents = source_contents('0.31.1').slice(*HaveAPI::Performance::SourceVersions::PATHS)
228
+ expected = HaveAPI::Performance::SourceVersions.capture(with_files(contents, shallow_files)).fetch('normalized_sha256')
229
+ variants = [
230
+ recursive_files.sub('doc/**/*', 'doc/**'), recursive_files.sub('File.file?', 'File.symlink?'),
231
+ recursive_files.sub("\n", " # additional behavior\n"), '', shallow_files + recursive_files,
232
+ "#{recursive_files} s.files = []\n"
233
+ ]
234
+ variants.each do |statement|
235
+ changed = with_files(contents, statement)
236
+ receipt = HaveAPI::Performance::SourceVersions.capture(changed)
237
+ expect(receipt).not_to have_key('file_selection')
238
+ expect(receipt.fetch('normalized_sha256')).not_to eq(expected)
239
+ expect(receipt).to eq(HaveAPI::Performance::SourceVersions.capture(changed, normalize_files: false))
240
+ end
241
+ end
242
+
243
+ it 'rejects tampered or stripped packaging markers and source bindings on replay' do
244
+ %w[marker stripped added raw normalized content source manifest].each do |fault|
245
+ selections = fault == 'added' ? {} : { 'baseline' => shallow_files, 'candidate' => recursive_files }
246
+ samples, budgets = replay_samples(selections:, normalize_files: fault != 'added')
247
+ inputs = samples['observations']['candidate']['inputs']
248
+ receipt = inputs.fetch('source_versions')
249
+ path = 'servers/ruby/haveapi.gemspec'
250
+ case fault
251
+ when 'marker' then receipt['file_selection'] = 'unreviewed-policy'
252
+ when 'stripped' then receipt.delete('file_selection')
253
+ when 'added' then receipt['file_selection'] = 'documentation-files-v1'
254
+ when 'raw' then receipt['raw_sha256'][path] = 'f' * 64
255
+ when 'normalized' then receipt['normalized_sha256'][path] = 'f' * 64
256
+ when 'content' then receipt['contents'][path] += "# changed\n"
257
+ when 'source' then samples['sources']['candidate']['files_sha256'][path] = 'f' * 64
258
+ when 'manifest' then inputs['manifests'][path] = 'f' * 64
259
+ end
260
+ expect { compare(samples, budgets) }.to raise_error(error), fault
261
+ end
262
+ end
263
+
264
+ it 'continues to replay legacy own-version receipts with raw file selection' do
265
+ samples, budgets = replay_samples(normalize_files: false)
266
+
267
+ expect(compare(samples, budgets).fetch('status')).to eq('pass')
268
+ end
269
+
270
+ it 'leaves the historical maintenance packaging comparison strict' do
271
+ contents = source_contents('0.30.1')
272
+ old = HaveAPI::Performance::Release.capture(with_files(contents, shallow_files))
273
+ new = HaveAPI::Performance::Release.capture(with_files(contents, recursive_files))
274
+
275
+ expect(old.fetch('normalized_sha256')).not_to eq(new.fetch('normalized_sha256'))
276
+ end
168
277
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.1
4
+ version: 0.32.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.32.1
32
+ version: 0.32.2
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.32.1
39
+ version: 0.32.2
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: i18n
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -218,17 +218,24 @@ files:
218
218
  - benchmarks/performance_compare.rb
219
219
  - benchmarks/shell.nix
220
220
  - benchmarks/spec/client_examples_spec.rb
221
- - doc/client-examples.md
222
- - doc/create-client.md
223
- - doc/documentation-oauth.md
224
- - doc/documentation-ui.md
221
+ - doc/cs/client-examples.md
222
+ - doc/cs/create-client.md
223
+ - doc/cs/json-schema.html
224
+ - doc/cs/protocol.md
225
+ - doc/cs/readme.md
226
+ - doc/cs/typed-input-validation.md
227
+ - doc/en/client-examples.md
228
+ - doc/en/create-client.md
229
+ - doc/en/documentation-oauth.md
230
+ - doc/en/documentation-ui.md
231
+ - doc/en/json-schema.html
232
+ - doc/en/performance.md
233
+ - doc/en/protocol.md
234
+ - doc/en/readme.md
235
+ - doc/en/typed-input-validation.md
225
236
  - doc/hooks.erb
226
237
  - doc/index.md
227
- - doc/json-schema.html
228
- - doc/performance.md
229
- - doc/protocol.md
230
238
  - doc/protocol.png
231
- - doc/typed-input-validation.md
232
239
  - haveapi.gemspec
233
240
  - lib/haveapi.rb
234
241
  - lib/haveapi/action.rb
@@ -271,6 +278,7 @@ files:
271
278
  - lib/haveapi/documentation_clients.rb
272
279
  - lib/haveapi/documentation_context.rb
273
280
  - lib/haveapi/documentation_helpers.rb
281
+ - lib/haveapi/documentation_locale.rb
274
282
  - lib/haveapi/documentation_oauth.rb
275
283
  - lib/haveapi/documentation_schema.rb
276
284
  - lib/haveapi/documentation_search.rb
@@ -381,6 +389,8 @@ files:
381
389
  - spec/client_examples/assets/documentation_test.cjs
382
390
  - spec/client_examples/assets/highlighter_test.cjs
383
391
  - spec/client_examples/assets/tabs_test.cjs
392
+ - spec/client_examples/comment_capture.rb
393
+ - spec/client_examples/comments_spec.rb
384
394
  - spec/client_examples/context_performance_spec.rb
385
395
  - spec/client_examples/context_spec.rb
386
396
  - spec/client_examples/detail_spec.rb
@@ -408,12 +418,15 @@ files:
408
418
  - spec/documentation/client_examples_spec.rb
409
419
  - spec/documentation/client_overview_spec.rb
410
420
  - spec/documentation/current_user_html_escaping_spec.rb
421
+ - spec/documentation/example_selection_spec.rb
411
422
  - spec/documentation/example_variants_spec.rb
412
423
  - spec/documentation/examples_spec.rb
413
424
  - spec/documentation/fixtures/search_index.json
414
425
  - spec/documentation/host_html_escaping_spec.rb
415
426
  - spec/documentation/identity_and_guides_spec.rb
416
427
  - spec/documentation/identity_context_spec.rb
428
+ - spec/documentation/language_settings_spec.rb
429
+ - spec/documentation/locale_spec.rb
417
430
  - spec/documentation/navigation_spec.rb
418
431
  - spec/documentation/oauth_fixture_spec.rb
419
432
  - spec/documentation/oauth_spec.rb
File without changes
File without changes