gem_server_conformance 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,423 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/step_helpers"
4
+
5
+ RSpec.describe GemServerConformance do # rubocop:disable RSpec/EmptyExampleGroup
6
+ include StepHelpers
7
+
8
+ before(:all) do
9
+ Gem.configuration.verbose = false
10
+ ENV["SOURCE_DATE_EPOCH"] = "0"
11
+ end
12
+
13
+ StepHelpers::Step
14
+ .new(context("with conformance runner"))
15
+ .then "after rebuilding empty versions list", before: ->(_) { rebuild_versions_list } do
16
+ request :get_versions do
17
+ it { is_expected.to be_valid_compact_index_reponse }
18
+
19
+ it {
20
+ is_expected.to have_body(
21
+ "created_at: 1990-01-01T00:00:00Z\n---\n"
22
+ )
23
+ }
24
+ end
25
+
26
+ request :get_names do
27
+ it { is_expected.to be_valid_compact_index_reponse }
28
+ it { is_expected.to have_body(eq("---\n\n")) }
29
+ end
30
+
31
+ request :get_specs do
32
+ it { is_expected.to be_ok }
33
+ it { is_expected.to unmarshal_as([]) }
34
+ end
35
+
36
+ request :get_specs, :prerelease do
37
+ it { is_expected.to be_ok }
38
+ it { is_expected.to unmarshal_as([]) }
39
+ end
40
+
41
+ request :get_specs, :latest do
42
+ it { is_expected.to be_ok }
43
+ it { is_expected.to unmarshal_as([]) }
44
+ end
45
+
46
+ request :get_info, "missing" do
47
+ it { is_expected.to be_not_found }
48
+ end
49
+
50
+ request :get_gem, "missing" do
51
+ it { is_expected.to be_not_found }
52
+ end
53
+
54
+ request :get_quick_spec, "missing" do
55
+ it { is_expected.to be_not_found }
56
+ end
57
+ end
58
+ .then "after first push", before: lambda { |_|
59
+ @gem_a_1_0_0 = build_gem("a", "1.0.0")
60
+ push_gem(@gem_a_1_0_0, expected_to: be_ok)
61
+ } do
62
+ pushed_gem("a-1.0.0")
63
+
64
+ request :get_versions, compact_index: true do
65
+ it { is_expected.to be_valid_compact_index_reponse }
66
+
67
+ it {
68
+ is_expected.to have_body(
69
+ parent_response.body + "a 1.0.0 8761412e66a014fe80723e251d96be29\n"
70
+ )
71
+ }
72
+ end
73
+
74
+ request :get_info, "a", compact_index: true do
75
+ it { is_expected.to be_ok }
76
+ it { is_expected.to have_body(<<~INFO) }
77
+ ---
78
+ 1.0.0 |checksum:2dfc054a348d36faae6e98e8c0222a76c07cfa0620b3c47acb154cb3d2de149b
79
+ INFO
80
+ it { is_expected.to have_header("content-type").with_value("text/plain; charset=utf-8") }
81
+ end
82
+
83
+ request :get_names, compact_index: true do
84
+ it { is_expected.to be_valid_compact_index_reponse }
85
+ it { is_expected.to have_body(eq("---\na\n")) }
86
+ end
87
+
88
+ request :get_specs do
89
+ it { is_expected.to be_ok, last_response.body }
90
+ it { is_expected.to unmarshal_as([["a", Gem::Version.new("1.0.0"), "ruby"]]) }
91
+ end
92
+
93
+ request :get_specs, :prerelease do
94
+ it { is_expected.to be_ok }
95
+ it { is_expected.to unmarshal_as([]) }
96
+ end
97
+
98
+ request :get_specs, :latest do
99
+ it { is_expected.to be_ok }
100
+ it { is_expected.to unmarshal_as([["a", Gem::Version.new("1.0.0"), "ruby"]]) }
101
+ end
102
+ end
103
+ .then "after yanking only gem", before: ->(_) { yank_gem(@gem_a_1_0_0, expected_to: be_ok) } do
104
+ yanked_gem("a-1.0.0")
105
+
106
+ request :get_versions, compact_index: true do
107
+ it { is_expected.to be_valid_compact_index_reponse }
108
+
109
+ it {
110
+ is_expected.to have_body(parent_response.body + "a -1.0.0 6105347ebb9825ac754615ca55ff3b0c\n")
111
+ }
112
+ end
113
+
114
+ request :get_info, "a", compact_index: true do
115
+ it { is_expected.to be_valid_compact_index_reponse }
116
+ it { is_expected.to have_body(<<~INFO) }
117
+ ---
118
+ INFO
119
+ end
120
+
121
+ request :get_names, compact_index: true do
122
+ it { is_expected.to be_valid_compact_index_reponse }
123
+ it { is_expected.to have_body(eq("---\n\n")) }
124
+ end
125
+
126
+ request :get_specs do
127
+ it { is_expected.to be_ok }
128
+ it { is_expected.to unmarshal_as([]) }
129
+ end
130
+
131
+ request :get_specs, :prerelease do
132
+ it { is_expected.to be_ok }
133
+ it { is_expected.to unmarshal_as([]) }
134
+ end
135
+
136
+ request :get_specs, :latest do
137
+ it { is_expected.to be_ok }
138
+ it { is_expected.to unmarshal_as([]) }
139
+ end
140
+ end
141
+ .then "after second push", before: lambda { |_|
142
+ push_gem(@a_0_0_1 = build_gem("a", "0.0.1"), expected_to: be_ok)
143
+ push_gem(@b_1_0_0_pre = build_gem("b", "1.0.0.pre") do |spec|
144
+ spec.add_runtime_dependency "a", "< 1.0.0", ">= 0.1.0"
145
+ spec.required_ruby_version = ">= 2.0"
146
+ spec.required_rubygems_version = ">= 2.0"
147
+ end, expected_to: be_ok)
148
+ } do
149
+ pushed_gem("a-0.0.1")
150
+ pushed_gem("b-1.0.0.pre")
151
+
152
+ request :get_versions, compact_index: true do
153
+ it { is_expected.to be_valid_compact_index_reponse }
154
+ it { is_expected.to have_body(parent_response.body + <<~BODY) }
155
+ a 0.0.1 22428c91ad748146bec818307104ed33
156
+ b 1.0.0.pre 688f5cdf79887aff5d87c86f36cfe063
157
+ BODY
158
+ end
159
+
160
+ request :get_info, "a", compact_index: true do
161
+ it { is_expected.to be_valid_compact_index_reponse }
162
+ it { is_expected.to have_body(parent_response.body + <<~INFO) }
163
+ 0.0.1 |checksum:5e25d516b8c19c9d26ef95efad565c2097865a0d3dba5ef3fade650a2e690b35
164
+ INFO
165
+ end
166
+
167
+ request :get_info, "b", compact_index: true do
168
+ it { is_expected.to be_valid_compact_index_reponse }
169
+ it { is_expected.to have_body(<<~INFO) }
170
+ ---
171
+ 1.0.0.pre a:< 1.0.0&>= 0.1.0|checksum:3f97419b7c35257f7aab3ae37521ab64ef8ec7646ef55b9f6a5e41d479bc128c,ruby:>= 2.0,rubygems:>= 2.0
172
+ INFO
173
+ end
174
+
175
+ request :get_names, compact_index: true do
176
+ it { is_expected.to be_valid_compact_index_reponse }
177
+ it { is_expected.to have_body(eq("---\na\nb\n")) }
178
+ end
179
+
180
+ request :get_specs do
181
+ it { is_expected.to be_ok }
182
+ it { is_expected.to unmarshal_as([["a", Gem::Version.new("0.0.1"), "ruby"]]) }
183
+ end
184
+
185
+ request :get_specs, :prerelease do
186
+ it { is_expected.to be_ok }
187
+ it { is_expected.to unmarshal_as([["b", Gem::Version.new("1.0.0.pre"), "ruby"]]) }
188
+ end
189
+
190
+ request :get_specs, :latest do
191
+ it { is_expected.to be_ok }
192
+ it { is_expected.to unmarshal_as([["a", Gem::Version.new("0.0.1"), "ruby"]]) }
193
+ end
194
+
195
+ request :get_gem, "a-0.0.1" do
196
+ it { is_expected.to be_ok }
197
+
198
+ it { is_expected.to have_content_length }
199
+ .metadata[:content_length_header] = true
200
+ it { is_expected.to have_body(eq(@a_0_0_1.contents)) }
201
+ end
202
+
203
+ request :get_gem, "b-1.0.0.pre" do
204
+ it { is_expected.to be_ok }
205
+
206
+ it { is_expected.to have_content_length }
207
+ .metadata[:content_length_header] = true
208
+ it { is_expected.to have_body(eq(@b_1_0_0_pre.contents)) }
209
+ end
210
+
211
+ request :get_quick_spec, "a-0.0.1" do
212
+ it { is_expected.to be_ok }
213
+
214
+ it { is_expected.to have_content_length }
215
+ .metadata[:content_length_header] = true
216
+ it {
217
+ is_expected.to unmarshal_as(Gem::Specification.new do |s|
218
+ s.name = "a"
219
+ s.version = Gem::Version.new("0.0.1")
220
+ s.installed_by_version = Gem::Version.new("0")
221
+ s.authors = ["Conformance"]
222
+ s.date = Time.utc(2024, 7, 9)
223
+ s.description = ""
224
+ s.require_paths = ["lib"]
225
+ s.rubygems_version = "3.5.11"
226
+ s.specification_version = 4
227
+ s.summary = "Conformance test"
228
+ end).inflate(true)
229
+ }
230
+ end
231
+
232
+ request :get_quick_spec, "b-1.0.0.pre" do
233
+ it { is_expected.to be_ok }
234
+
235
+ it { is_expected.to have_content_length }
236
+ .metadata[:content_length_header] = true
237
+ it {
238
+ is_expected.to unmarshal_as(Gem::Specification.new do |s|
239
+ s.name = "b"
240
+ s.version = Gem::Version.new("1.0.0.pre")
241
+ s.installed_by_version = Gem::Version.new("0")
242
+ s.authors = ["Conformance"]
243
+ s.date = Time.utc(2024, 7, 9)
244
+ s.description = ""
245
+ s.require_paths = ["lib"]
246
+ s.rubygems_version = "3.5.11"
247
+ s.specification_version = 4
248
+ s.summary = "Conformance test"
249
+ s.add_dependency "a", ">= 0.1.0", "< 1.0.0"
250
+ s.required_ruby_version = ">= 2.0"
251
+ s.required_rubygems_version = ">= 2.0"
252
+ end).inflate(true)
253
+ }
254
+ end
255
+ end
256
+ .then "third push", before: lambda { |_|
257
+ push_gem(build_gem("a", "0.0.1") { |s| s.add_runtime_dependency "b", ">= 1.0.0" },
258
+ expected_to: be_conflict)
259
+ @all_gems.pop
260
+ push_gem(@a_0_2_0 = build_gem("a", "0.2.0"), expected_to: be_ok)
261
+ push_gem(build_gem("a", "0.2.0", platform: "x86-mingw32"), expected_to: be_ok)
262
+ push_gem(build_gem("a", "0.2.0", platform: "java"), expected_to: be_ok)
263
+ } do
264
+ pushed_gem("a-0.2.0")
265
+ pushed_gem("a-0.2.0-x86-mingw32")
266
+ pushed_gem("a-0.2.0-java")
267
+
268
+ request :get_versions, compact_index: true do
269
+ it { is_expected.to be_valid_compact_index_reponse }
270
+ it { is_expected.to have_body(parent_response.body + <<~BODY) }
271
+ a 0.2.0 7a7528379bbd1e0420ea7f1305ba526a
272
+ a 0.2.0-x86-mingw32 17f9c2882d6f0a244f8bba2df1d14107
273
+ a 0.2.0-java ca5c12bc8ba4457ada41c71bee282bfb
274
+ BODY
275
+ end
276
+
277
+ request :get_info, "a", compact_index: true do
278
+ it { is_expected.to be_valid_compact_index_reponse }
279
+ it { is_expected.to have_body(parent_response.body + <<~INFO) }
280
+ 0.2.0 |checksum:a1753a0e8b6f0515a15e9cfa4ea143e36de235525f6f68c4ff45c4ae70be072f
281
+ 0.2.0-x86-mingw32 |checksum:e330e73d0dec030107c5656bbe89aecae738ba483471bf87f1bd943093fc9f27
282
+ 0.2.0-java |checksum:897332272ac159bf200a690dae5039df1e60355124848f2a6f889563311421f4
283
+ INFO
284
+ end
285
+
286
+ request :get_specs do
287
+ it { is_expected.to be_ok, last_response.body }
288
+
289
+ it {
290
+ is_expected.to unmarshal_as(
291
+ a_collection_containing_exactly(*(Marshal.load(Zlib.gunzip(parent_response.body)) + [
292
+ ["a", Gem::Version.new("0.2.0"), "x86-mingw32"],
293
+ ["a", Gem::Version.new("0.2.0"), "ruby"],
294
+ ["a", Gem::Version.new("0.2.0"), "java"]
295
+ ]))
296
+ )
297
+ }
298
+ end
299
+
300
+ request :get_specs, :latest do
301
+ it { is_expected.to be_ok }
302
+
303
+ it {
304
+ is_expected.to unmarshal_as a_collection_containing_exactly(
305
+ ["a", Gem::Version.new("0.2.0"), "x86-mingw32"],
306
+ ["a", Gem::Version.new("0.2.0"), "ruby"],
307
+ ["a", Gem::Version.new("0.2.0"), "java"]
308
+ )
309
+ }
310
+ end
311
+
312
+ request :get_specs, :prerelease do
313
+ it { is_expected.to be_ok }
314
+
315
+ it {
316
+ is_expected.to unmarshal_as([["b", Gem::Version.new("1.0.0.pre"), "ruby"]])
317
+ }
318
+ end
319
+ end
320
+ .then "after rebuilding versions list", before: ->(_) { rebuild_versions_list } do
321
+ request :get_versions, compact_index: true do
322
+ it { is_expected.to be_valid_compact_index_reponse }
323
+ it { is_expected.to have_body(<<~BODY) }
324
+ created_at: 1990-01-01T01:08:00Z
325
+ ---
326
+ a 0.0.1,0.2.0,0.2.0-x86-mingw32,0.2.0-java ca5c12bc8ba4457ada41c71bee282bfb
327
+ b 1.0.0.pre 688f5cdf79887aff5d87c86f36cfe063
328
+ BODY
329
+ end
330
+ end
331
+ .then "after fourth push", before: ->(_) { push_gem(build_gem("a", "0.3.0"), expected_to: be_ok) } do
332
+ pushed_gem("a-0.3.0")
333
+
334
+ request :get_versions, compact_index: true do
335
+ it { is_expected.to be_valid_compact_index_reponse }
336
+ it { is_expected.to have_body(parent_response.body + <<~BODY) }
337
+ a 0.3.0 6263c53d5a23dfe0339a3ebae0fed8da
338
+ BODY
339
+ end
340
+
341
+ request :get_info, "a", compact_index: true do
342
+ it { is_expected.to be_valid_compact_index_reponse }
343
+ it { is_expected.to have_body(parent_response.body + <<~INFO) }
344
+ 0.3.0 |checksum:40f19de3ce5c3fc5930fbc5dc3a08cd0b31572852d4885b37a19039bad7d9784
345
+ INFO
346
+ end
347
+
348
+ request :get_specs do
349
+ it { is_expected.to be_ok }
350
+
351
+ it {
352
+ is_expected.to unmarshal_as a_collection_containing_exactly(
353
+ *(Marshal.load(Zlib.gunzip(parent_response.body)) + [["a", Gem::Version.new("0.3.0"), "ruby"]])
354
+ )
355
+ }
356
+ end
357
+
358
+ request :get_specs, :latest do
359
+ it { is_expected.to be_ok }
360
+
361
+ it {
362
+ is_expected.to unmarshal_as a_collection_containing_exactly(
363
+ ["a", Gem::Version.new("0.2.0"), "x86-mingw32"],
364
+ ["a", Gem::Version.new("0.2.0"), "java"],
365
+ ["a", Gem::Version.new("0.3.0"), "ruby"]
366
+ )
367
+ }
368
+ end
369
+ end
370
+ .then "after yanking a gem", before: ->(_) { yank_gem(@a_0_2_0, expected_to: be_ok) } do
371
+ yanked_gem("a-0.2.0")
372
+
373
+ request :get_versions, compact_index: true do
374
+ it { is_expected.to be_valid_compact_index_reponse }
375
+ it { is_expected.to have_body(parent_response.body + "a -0.2.0 1fdcc4d621638a6ba75d8ed88b09f97a\n") }
376
+ end
377
+
378
+ request :get_info, "a", compact_index: true do
379
+ it { is_expected.to be_valid_compact_index_reponse }
380
+
381
+ it {
382
+ is_expected.to have_body(
383
+ parent_response.body.lines.tap do |lines|
384
+ lines.delete("0.2.0 |checksum:a1753a0e8b6f0515a15e9cfa4ea143e36de235525f6f68c4ff45c4ae70be072f\n")
385
+ end.join
386
+ )
387
+ }
388
+ end
389
+
390
+ request :get_specs do
391
+ it { is_expected.to be_ok }
392
+
393
+ it { is_expected.to have_content_length }
394
+ .metadata[:content_length_header] = true
395
+ it {
396
+ is_expected.to unmarshal_as(
397
+ a_collection_containing_exactly( \
398
+ *Marshal.load(Zlib.gunzip(parent_response.body)).tap do |entries|
399
+ entries.delete(["a", Gem::Version.new("0.2.0"), "ruby"])
400
+ end
401
+ )
402
+ )
403
+ }
404
+ end
405
+ end
406
+ .then "after rebuilding versions list", before: ->(_) { rebuild_versions_list } do
407
+ request :get_versions, compact_index: true do
408
+ it { is_expected.to be_valid_compact_index_reponse }
409
+ it { is_expected.to have_body(<<~VERSIONS) }
410
+ created_at: 1990-01-01T02:10:00Z
411
+ ---
412
+ a 0.0.1,0.2.0-x86-mingw32,0.2.0-java,0.3.0 1fdcc4d621638a6ba75d8ed88b09f97a
413
+ b 1.0.0.pre 688f5cdf79887aff5d87c86f36cfe063
414
+ VERSIONS
415
+ end
416
+ end
417
+ .then "after yanking a missing gem", before:
418
+ lambda { |_|
419
+ yank_gem(RequestHelpers::MockGem.new(name: "missing", version: "1.0.0"), expected_to: be_not_found)
420
+ } do
421
+ nil
422
+ end
423
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["RACK_ENV"] = ENV["APP_ENV"] = "test"
4
+ require "gem_server_conformance/server"
5
+ require_relative "support/request_helpers"
6
+
7
+ RSpec.configure do |config|
8
+ # Enable flags like --only-failures and --next-failure
9
+ config.example_status_persistence_file_path = ".gem_server_conformance.rspec_status"
10
+
11
+ # Disable RSpec exposing methods globally on `Module` and `main`
12
+ config.disable_monkey_patching!
13
+
14
+ config.include RequestHelpers
15
+
16
+ config.expect_with :rspec do |c|
17
+ c.syntax = :expect
18
+ c.include_chain_clauses_in_custom_matcher_descriptions = true
19
+ end
20
+ end