gem_server_conformance 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +65 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +27 -0
- data/exe/gem_server_conformance +10 -0
- data/exe/gem_server_example +7 -0
- data/lib/gem_server_conformance/server.rb +324 -0
- data/lib/gem_server_conformance/version.rb +5 -0
- data/spec/gem_server_conformance_spec.rb +423 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/request_helpers.rb +364 -0
- data/spec/support/step_helpers.rb +257 -0
- metadata +75 -0
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|