create_tests 1.2.0 → 1.3.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: adf779b9d966308976925449218e2f71071441bc665a7c5ecdd79c9ec25c477e
4
- data.tar.gz: 49cd07ebd23c1dd406ee5db3af61c3a761615ecec746c451d059717ad8a668dd
3
+ metadata.gz: 4ca29473a6da1f495ae3bb61fc97638c049f03a7e7093f54db1ac5a1e602b97b
4
+ data.tar.gz: 829f1a5085c49bc9339095d6fbb9d6f603f6badaa0691f9344e8c0f173ca58f1
5
5
  SHA512:
6
- metadata.gz: 948a204d7595c2b5e5ddbd42e18029b870f0fd9212df3a20dd970b29416d69306332b2f31ecd57fc9b82d8a1ad3b6e15424312248a5c80d26f9ae0ca14e90f00
7
- data.tar.gz: 9510e4c3f35eba997841db074bd32b8ef07712bacd88cda7bb49581deea70d1381148d833e3791c5b3c8b21a2d7259fb2fffacfae157dfd15080ad46ca9e5d9c
6
+ metadata.gz: 99a571060b7ab3971fff6ee6b0e8c67db21df52cc0ae00dbc57da0a26517aa94c8b63e8e606926bccb07a83c7d92e5f2a639495daf0c530f6bc6fe5b2189abfb
7
+ data.tar.gz: 785a8211a25f007e77b961868208081065b0f04e4d962382aa4b644acb757a6f6fc37b9c48e06b032e637906dcebac7127fabdfb0599a44e2719d65811bb7a3d
data/README.md CHANGED
@@ -75,7 +75,7 @@ CreateTests.from "./requests/uber.yaml.rb"
75
75
 
76
76
  For each API endpoint, `create_tests` generates up to 7 test types:
77
77
 
78
- 1. **"has correct structure in successful response"** -- Calls the endpoint and validates the response structure using `NiceHttp.validate_response` with diff details on failure.
78
+ 1. **"has correct structure in successful response"** -- Calls the endpoint and validates the response structure using `NiceHttp.validate_response` with diff details on failure. The expected status is the first 2xx response key, else `default`, else the first key. When `:data_default` is present, missing (or nil) keys in `:data` are filled before the call; existing example values are not overwritten.
79
79
 
80
80
  2. **"doesn't retrieve data if not authenticated"** -- Calls with empty headers and asserts a 4xx response.
81
81
 
@@ -85,9 +85,9 @@ For each API endpoint, `create_tests` generates up to 7 test types:
85
85
 
86
86
  5. **"handles multiple valid data variations"** -- For POST/PUT/PATCH endpoints with a payload source, uses `generate_n(5, :correct)` from nice_hash to test 5 different valid payload variations. Prefers `:data_pattern` when present (real string_pattern / enum / range values from open_api_import); otherwise uses `:data`. Generated values are assigned to `request[:data]` before the call.
87
87
 
88
- 6. **"returns error when individual data fields are invalid"** -- Uses `NiceHash.change_one_by_one` on `:data_pattern` (preferred) or `:data` to systematically test each field being wrong one at a time, asserting the server rejects each one.
88
+ 6. **"returns error when individual data fields are invalid"** -- Uses `NiceHash.change_one_by_one` on `:data_pattern` (preferred) or `:data` to systematically test each field being wrong one at a time, asserting the server rejects each one. Fields listed in `:data_read_only` are skipped.
89
89
 
90
- 7. **"returns error if required parameter on data empty/missing"** -- For endpoints with `data_required`, tests both empty and missing values for each required data field. When `:data` is absent but `:data_examples` is present (common for form uploads), the first example is used as the body for success and required-data tests.
90
+ 7. **"returns error if required parameter on data empty/missing"** -- For endpoints with `data_required`, tests both empty and missing values for each required data field. When `:data` is absent but `:data_examples` is present (common for form uploads), the first example is used as the body for success and required-data tests. Otherwise `:data_default` can seed missing body fields the same way as the success example.
91
91
 
92
92
  Required keyword parameters from open_api_import `create_constants: true` (e.g. `def self.get_item(id: ID)`) get Helper stubs and are called as keywords (`id: @id`), same as positional required args.
93
93
 
@@ -149,6 +149,16 @@ class CreateTests
149
149
  has_data_examples = request.key?(:data_examples) &&
150
150
  request[:data_examples].is_a?(Array) &&
151
151
  !request[:data_examples].empty?
152
+ has_data_default = request.key?(:data_default) &&
153
+ request[:data_default].is_a?(Hash) &&
154
+ !request[:data_default].empty?
155
+ # Prefer data_examples over defaults when :data is absent
156
+ apply_data_default = has_data_default && (request.key?(:data) || !has_data_examples)
157
+ apply_defaults_txt = if apply_data_default
158
+ "@request[:data] ||= {}\n @request[:data_default].each do |k, v|\n @request[:data][k] = v if !@request[:data].key?(k) || @request[:data][k].nil?\n end\n "
159
+ else
160
+ ""
161
+ end
152
162
  # Prefer data_pattern for variation/invalid-field tests; fall back to :data
153
163
  payload_source = if request.key?(:data_pattern)
154
164
  ":data_pattern"
@@ -156,16 +166,17 @@ class CreateTests
156
166
  ":data"
157
167
  end
158
168
 
159
- # first response on responses is the one expected to be returned when success
169
+ # Success code: first 2xx key, else "default", else first key
160
170
  if include_kind?(only_kinds, :success) && request.key?(:responses) and request[:responses].size > 0
161
- code = request[:responses].keys[0]
171
+ code = success_response_code(request[:responses])
172
+ code_src = format_response_code(code)
162
173
  title = example_title("has correct structure in successful response", test, trailing_space: true)
163
174
  if !request.key?(:data) && has_data_examples
164
175
  tests[title] = "do
165
176
  request = @request.deep_copy
166
177
  request[:data] = @request[:data_examples].first
167
178
  resp = @http.#{request[:method]}(request)
168
- #{assert_eq('resp.code', code, test)}\n"
179
+ #{assert_eq('resp.code', code_src, test)}\n"
169
180
  if request[:responses][code].is_a?(Hash) and request[:responses][code].key?(:data)
170
181
  tests[title] +="result = NiceHttp.validate_response(resp, @request.responses._#{code}.data, include_diff: true)\n"
171
182
  tests[title] += "#{assert_be_true('result[:ok]', 'Structure mismatch: #{result[:diff]}', test)}\n"
@@ -173,8 +184,8 @@ class CreateTests
173
184
  tests[title] += "end\n"
174
185
  else
175
186
  tests[title] = "do
176
- resp = @http.#{request[:method]}(@request)
177
- #{assert_eq('resp.code', code, test)}\n"
187
+ #{apply_defaults_txt}resp = @http.#{request[:method]}(@request)
188
+ #{assert_eq('resp.code', code_src, test)}\n"
178
189
 
179
190
  if request[:responses][code].is_a?(Hash) and request[:responses][code].key?(:data)
180
191
  tests[title] +="result = NiceHttp.validate_response(resp, @request.responses._#{code}.data, include_diff: true)\n"
@@ -253,8 +264,20 @@ class CreateTests
253
264
  end
254
265
 
255
266
  if include_kind?(only_kinds, :invalid_fields) && payload_source
256
- title = example_title("returns error when individual data fields are invalid", test, trailing_space: true)
257
- tests[title] = "do
267
+ payload_key = payload_source == ":data_pattern" ? :data_pattern : :data
268
+ read_only_names = Array(request[:data_read_only]).map(&:to_s)
269
+ payload_for_invalid = request[payload_key]
270
+ if payload_for_invalid.is_a?(Hash) && !read_only_names.empty?
271
+ payload_for_invalid = payload_for_invalid.deep_copy
272
+ read_only_names.each do |name|
273
+ payload_for_invalid.delete(name.to_sym)
274
+ payload_for_invalid.delete(name)
275
+ end
276
+ end
277
+ unless payload_for_invalid.nil? || (payload_for_invalid.respond_to?(:empty?) && payload_for_invalid.empty?)
278
+ title = example_title("returns error when individual data fields are invalid", test, trailing_space: true)
279
+ if read_only_names.empty?
280
+ tests[title] = "do
258
281
  wrong = @request[#{payload_source}].generate(:correct, errors: :min_length)
259
282
  NiceHash.change_one_by_one([@request[#{payload_source}], :correct], wrong).each do |one_wrong|
260
283
  request = @request.deep_copy
@@ -263,11 +286,32 @@ class CreateTests
263
286
  #{assert_between('resp.code.to_i', '200', '299', test, negate: true)}
264
287
  end
265
288
  end\n"
289
+ else
290
+ read_only_lit = request[:data_read_only].map(&:inspect).join(", ")
291
+ tests[title] = "do
292
+ payload = @request[#{payload_source}].deep_copy
293
+ [#{read_only_lit}].each do |f|
294
+ payload.delete(f)
295
+ payload.delete(f.to_s)
296
+ payload.delete(f.to_s.to_sym)
297
+ end
298
+ wrong = payload.generate(:correct, errors: :min_length)
299
+ NiceHash.change_one_by_one([payload, :correct], wrong).each do |one_wrong|
300
+ request = @request.deep_copy
301
+ request[:data] = one_wrong
302
+ resp = @http.#{request[:method]}(request)
303
+ #{assert_between('resp.code.to_i', '200', '299', test, negate: true)}
304
+ end
305
+ end\n"
306
+ end
307
+ end
266
308
  end
267
309
 
268
- if include_kind?(only_kinds, :required_data) && request.key?(:data_required) && (request.key?(:data) || has_data_examples)
310
+ if include_kind?(only_kinds, :required_data) && request.key?(:data_required) && (request.key?(:data) || has_data_examples || apply_data_default)
269
311
  seed_data_line = if !request.key?(:data) && has_data_examples
270
312
  "request[:data] = request[:data_examples].first\n "
313
+ elsif apply_data_default
314
+ "request[:data] ||= {}\n request[:data_default].each do |k, v|\n request[:data][k] = v if !request[:data].key?(k) || request[:data][k].nil?\n end\n "
271
315
  else
272
316
  ""
273
317
  end
@@ -334,6 +378,30 @@ class CreateTests
334
378
  return modified, output
335
379
  end
336
380
 
381
+ private def success_response_code(responses)
382
+ keys = responses.keys
383
+ twoxx = keys.find do |k|
384
+ n = k.to_s
385
+ n.match?(/\A\d+\z/) && (200..299).include?(n.to_i)
386
+ end
387
+ return twoxx if twoxx
388
+
389
+ default_key = keys.find { |k| k.to_s == "default" }
390
+ return default_key if default_key
391
+
392
+ keys[0]
393
+ end
394
+
395
+ # Numeric status codes stay unquoted (eq 200); non-numeric keys use inspect.
396
+ private def format_response_code(code)
397
+ s = code.to_s
398
+ if s.match?(/\A\d+\z/)
399
+ s
400
+ else
401
+ code.inspect
402
+ end
403
+ end
404
+
337
405
  private def normalize_only(only)
338
406
  return nil if only.nil?
339
407
  list = case only
@@ -1,3 +1,3 @@
1
1
  class CreateTests
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: create_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Ruiz