gemvault 0.1.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +181 -0
- data/.ruby-version +1 -0
- data/ASSESSMENT.md +88 -0
- data/CLAUDE.md +76 -0
- data/Dockerfile.test +11 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/MACROPLAN.md +63 -0
- data/README.md +74 -0
- data/Rakefile +39 -0
- data/docs/superpowers/plans/2026-04-16-container-integration-tests.md +605 -0
- data/exe/gemvault +5 -0
- data/lib/bundler/plugin/vault_source.rb +117 -0
- data/lib/gemvault/cli/command.rb +17 -0
- data/lib/gemvault/cli/commands/add.rb +30 -0
- data/lib/gemvault/cli/commands/extract.rb +49 -0
- data/lib/gemvault/cli/commands/list.rb +26 -0
- data/lib/gemvault/cli/commands/new.rb +28 -0
- data/lib/gemvault/cli/commands/remove.rb +34 -0
- data/lib/gemvault/cli.rb +45 -0
- data/lib/gemvault/gem_entry.rb +37 -0
- data/lib/gemvault/vault.rb +178 -0
- data/lib/gemvault/version.rb +3 -0
- data/lib/gemvault.rb +10 -0
- data/lib/rubygems/resolver/vault_set.rb +39 -0
- data/lib/rubygems/source/vault.rb +124 -0
- data/lib/rubygems_plugin.rb +72 -0
- metadata +116 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
# Container Integration Tests — Remaining Work
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Migrate all subprocess-based integration tests from `test/integration_test.rb` and `test/rubygems_plugin_test.rb` into Podman container tests, then remove the old tests.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Each container test is a self-contained minitest file in `test/containers/` that runs inside a disposable `podman run --rm` container. The project is mounted read-only at `/gem`. A cached Docker image (`gemvault-test:latest`) has gemvault pre-installed; tests detect this and skip the build/install step. The host-side `rake test:containers` task orchestrates execution.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Podman, minitest (stdlib), Docker image `ruby:4.0.1-slim`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Map
|
|
14
|
+
|
|
15
|
+
| File | Action | Responsibility |
|
|
16
|
+
|------|--------|----------------|
|
|
17
|
+
| `test/containers/test_helper.rb` | Modify | Add `run_bundle` and `run_bundle!` helpers |
|
|
18
|
+
| `test/containers/test_bundle_install.rb` | Rewrite | Expand from spike to cover all Bundler integration scenarios |
|
|
19
|
+
| `test/containers/test_gem_install.rb` | Rewrite | Expand from spike to cover all RubyGems plugin scenarios |
|
|
20
|
+
| `test/containers/test_bundler_inline.rb` | Create | Bundler inline (gemfile block) test |
|
|
21
|
+
| `test/containers/test_plugin_root_deps.rb` | Create | plugins.rb preamble test (Bundler::Plugin.root workaround) |
|
|
22
|
+
| `test/integration_test.rb` | Delete | Replaced by container tests |
|
|
23
|
+
| `test/rubygems_plugin_test.rb` | Modify | Remove `RubygemsPluginIntegrationTest` class (keep unit test classes) |
|
|
24
|
+
|
|
25
|
+
## Test Migration Map
|
|
26
|
+
|
|
27
|
+
Each old test maps to a container test method:
|
|
28
|
+
|
|
29
|
+
**`test/integration_test.rb` → `test/containers/test_bundle_install.rb`:**
|
|
30
|
+
|
|
31
|
+
| Old test | New method | Notes |
|
|
32
|
+
|----------|-----------|-------|
|
|
33
|
+
| `test_basic_install` | Already done in spike | |
|
|
34
|
+
| `test_multiple_gems` | `test_multiple_gems` | Three gems, all install |
|
|
35
|
+
| `test_lockfile_correct` | `test_lockfile_format` | Assert PLUGIN SOURCE, type, version |
|
|
36
|
+
| `test_lockfile_round_trip` | `test_lockfile_idempotent` | Two installs, same lockfile |
|
|
37
|
+
| `test_gem_loadable` | Already done in spike (asserts `bundle exec ruby -e`) | |
|
|
38
|
+
| `test_alongside_rubygems_source` | `test_mixed_sources` | Vault + rubygems.org together |
|
|
39
|
+
| `test_subset_of_vault` | `test_partial_vault_install` | 3 in vault, only 2 in Gemfile |
|
|
40
|
+
| `test_dependency_resolution` | `test_intra_vault_dependency` | gem A depends on gem B, both in vault |
|
|
41
|
+
| `test_multi_version_resolution` | `test_version_constraint` | Two versions, `~> 2.0` picks correct one |
|
|
42
|
+
| `test_version_constraint_unsatisfied` | `test_unsatisfied_constraint` | Expect failure |
|
|
43
|
+
|
|
44
|
+
**`test/integration_test.rb` → `test/containers/test_bundler_inline.rb`:**
|
|
45
|
+
|
|
46
|
+
| Old test | New method | Notes |
|
|
47
|
+
|----------|-----------|-------|
|
|
48
|
+
| `test_bundler_inline` | `test_bundler_inline_with_vault` | No `plugin path:` hack needed — gem is installed |
|
|
49
|
+
|
|
50
|
+
**`test/integration_test.rb` → `test/containers/test_plugin_root_deps.rb`:**
|
|
51
|
+
|
|
52
|
+
| Old test | New method | Notes |
|
|
53
|
+
|----------|-----------|-------|
|
|
54
|
+
| `test_plugins_rb_makes_plugin_root_deps_activatable` | `test_plugin_root_specs_discoverable` | Same preamble-extraction approach |
|
|
55
|
+
|
|
56
|
+
**`test/rubygems_plugin_test.rb` → `test/containers/test_gem_install.rb`:**
|
|
57
|
+
|
|
58
|
+
| Old test | New method | Notes |
|
|
59
|
+
|----------|-----------|-------|
|
|
60
|
+
| `test_gem_install_from_vault` | Already done in spike | |
|
|
61
|
+
| `test_gem_install_verbose_shows_vault_messages` | `test_verbose_output` | `--verbose` flag shows vault messages |
|
|
62
|
+
| `test_gem_install_file_uri_source` | `test_file_uri_source` | `file://` prefix works |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Task 1: Add bundle helpers to test_helper.rb
|
|
67
|
+
|
|
68
|
+
**Files:**
|
|
69
|
+
- Modify: `test/containers/test_helper.rb`
|
|
70
|
+
|
|
71
|
+
The existing Bundler tests in `test_bundle_install.rb` call `Open3.capture2e("bundle", "install", chdir:)` inline. Multiple tests need this — extract `run_bundle` and `run_bundle!` into the helper, matching the old tests' pattern.
|
|
72
|
+
|
|
73
|
+
- [ ] **Step 1: Add run_bundle and run_bundle! to ContainerTestHelper**
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# In test/containers/test_helper.rb, add to ContainerTestHelper before `private`:
|
|
77
|
+
|
|
78
|
+
def run_bundle(*args, chdir:)
|
|
79
|
+
Open3.capture2e("bundle", *args, chdir: chdir.to_s)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def run_bundle!(*args, chdir:)
|
|
83
|
+
output, status = run_bundle(*args, chdir: chdir)
|
|
84
|
+
assert_predicate status, :success?, "bundle #{args.join(" ")} failed:\n#{output}"
|
|
85
|
+
[output, status]
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- [ ] **Step 2: Update test_bundle_install.rb to use the helper**
|
|
90
|
+
|
|
91
|
+
Replace the inline `Open3.capture2e("bundle", ...)` calls in the existing spike test with `run_bundle` / `run_bundle!`.
|
|
92
|
+
|
|
93
|
+
- [ ] **Step 3: Run container tests to verify refactor**
|
|
94
|
+
|
|
95
|
+
Run: `bundle exec rake test:containers`
|
|
96
|
+
Expected: both existing tests still pass.
|
|
97
|
+
|
|
98
|
+
- [ ] **Step 4: Commit**
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git add test/containers/test_helper.rb test/containers/test_bundle_install.rb
|
|
102
|
+
git commit -m "Extract run_bundle helpers into container test helper"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Task 2: Expand test_bundle_install.rb with all Bundler scenarios
|
|
108
|
+
|
|
109
|
+
**Files:**
|
|
110
|
+
- Rewrite: `test/containers/test_bundle_install.rb`
|
|
111
|
+
|
|
112
|
+
Add the remaining 8 Bundler integration tests. Each test follows the same pattern: build fixture gems, create vault, write Gemfile, `run_bundle!("install", chdir:)`, assert results.
|
|
113
|
+
|
|
114
|
+
- [ ] **Step 1: Write test_multiple_gems**
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
def test_multiple_gems
|
|
118
|
+
gem1 = build_gem("alpha_vault", "1.0.0", dir: @gem_build_dir)
|
|
119
|
+
gem2 = build_gem("beta_vault", "2.0.0", dir: @gem_build_dir / "b")
|
|
120
|
+
gem3 = build_gem("gamma_vault", "3.0.0", dir: @gem_build_dir / "c")
|
|
121
|
+
|
|
122
|
+
vault_path = create_vault(@workdir / "multi.gemv", gem1, gem2, gem3)
|
|
123
|
+
|
|
124
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
125
|
+
source "#{vault_path}", type: :vault do
|
|
126
|
+
gem "alpha_vault"
|
|
127
|
+
gem "beta_vault"
|
|
128
|
+
gem "gamma_vault"
|
|
129
|
+
end
|
|
130
|
+
GEMFILE
|
|
131
|
+
|
|
132
|
+
run_bundle!("install", chdir: @workdir)
|
|
133
|
+
|
|
134
|
+
%w[alpha_vault-1.0.0 beta_vault-2.0.0 gamma_vault-3.0.0].each do |name|
|
|
135
|
+
dirs = @workdir.glob("**/gems/#{name}")
|
|
136
|
+
refute_empty dirs, "Expected #{name} to be installed"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- [ ] **Step 2: Write test_lockfile_format**
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
def test_lockfile_format
|
|
145
|
+
gem_path = build_gem("locktest", "1.0.0", dir: @gem_build_dir)
|
|
146
|
+
vault_path = create_vault(@workdir / "lock.gemv", gem_path)
|
|
147
|
+
|
|
148
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
149
|
+
source "#{vault_path}", type: :vault do
|
|
150
|
+
gem "locktest"
|
|
151
|
+
end
|
|
152
|
+
GEMFILE
|
|
153
|
+
|
|
154
|
+
run_bundle!("install", chdir: @workdir)
|
|
155
|
+
|
|
156
|
+
lockfile = (@workdir / "Gemfile.lock").read
|
|
157
|
+
assert_includes lockfile, "PLUGIN SOURCE"
|
|
158
|
+
assert_includes lockfile, "type: vault"
|
|
159
|
+
assert_includes lockfile, "locktest (1.0.0)"
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- [ ] **Step 3: Write test_lockfile_idempotent**
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
def test_lockfile_idempotent
|
|
167
|
+
gem_path = build_gem("roundtrip", "1.0.0", dir: @gem_build_dir)
|
|
168
|
+
vault_path = create_vault(@workdir / "rt.gemv", gem_path)
|
|
169
|
+
|
|
170
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
171
|
+
source "#{vault_path}", type: :vault do
|
|
172
|
+
gem "roundtrip"
|
|
173
|
+
end
|
|
174
|
+
GEMFILE
|
|
175
|
+
|
|
176
|
+
run_bundle!("install", chdir: @workdir)
|
|
177
|
+
lockfile1 = (@workdir / "Gemfile.lock").read
|
|
178
|
+
|
|
179
|
+
run_bundle!("install", chdir: @workdir)
|
|
180
|
+
lockfile2 = (@workdir / "Gemfile.lock").read
|
|
181
|
+
|
|
182
|
+
assert_equal lockfile1, lockfile2, "Lockfile changed after second install"
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- [ ] **Step 4: Write test_mixed_sources**
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
def test_mixed_sources
|
|
190
|
+
gem_path = build_gem("vaultgem", "1.0.0", dir: @gem_build_dir)
|
|
191
|
+
vault_path = create_vault(@workdir / "mixed.gemv", gem_path)
|
|
192
|
+
|
|
193
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
194
|
+
source "https://rubygems.org"
|
|
195
|
+
|
|
196
|
+
source "#{vault_path}", type: :vault do
|
|
197
|
+
gem "vaultgem"
|
|
198
|
+
end
|
|
199
|
+
GEMFILE
|
|
200
|
+
|
|
201
|
+
output, status = run_bundle("install", chdir: @workdir)
|
|
202
|
+
assert_predicate status, :success?, "bundle install with mixed sources failed:\n#{output}"
|
|
203
|
+
assert_match(/Bundle complete!/, output)
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- [ ] **Step 5: Write test_partial_vault_install**
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
def test_partial_vault_install
|
|
211
|
+
gem1 = build_gem("want1", "1.0.0", dir: @gem_build_dir)
|
|
212
|
+
gem2 = build_gem("want2", "1.0.0", dir: @gem_build_dir / "w2")
|
|
213
|
+
gem3 = build_gem("skipme", "1.0.0", dir: @gem_build_dir / "sk")
|
|
214
|
+
|
|
215
|
+
vault_path = create_vault(@workdir / "subset.gemv", gem1, gem2, gem3)
|
|
216
|
+
|
|
217
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
218
|
+
source "#{vault_path}", type: :vault do
|
|
219
|
+
gem "want1"
|
|
220
|
+
gem "want2"
|
|
221
|
+
end
|
|
222
|
+
GEMFILE
|
|
223
|
+
|
|
224
|
+
run_bundle!("install", chdir: @workdir)
|
|
225
|
+
|
|
226
|
+
refute_empty @workdir.glob("**/gems/want1-1.0.0")
|
|
227
|
+
refute_empty @workdir.glob("**/gems/want2-1.0.0")
|
|
228
|
+
assert_empty @workdir.glob("**/gems/skipme-1.0.0"), "skipme should not be installed"
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- [ ] **Step 6: Write test_intra_vault_dependency**
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
def test_intra_vault_dependency
|
|
236
|
+
gem_b = build_gem("depb", "1.0.0", dir: @gem_build_dir / "b")
|
|
237
|
+
gem_a = build_gem("depa", "1.0.0", dir: @gem_build_dir,
|
|
238
|
+
files: {"lib/depa.rb" => "require 'depb'; module Depa; end"},
|
|
239
|
+
dependencies: [["depb", "~> 1.0"]])
|
|
240
|
+
|
|
241
|
+
vault_path = create_vault(@workdir / "deps.gemv", gem_a, gem_b)
|
|
242
|
+
|
|
243
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
244
|
+
source "#{vault_path}", type: :vault do
|
|
245
|
+
gem "depa"
|
|
246
|
+
gem "depb"
|
|
247
|
+
end
|
|
248
|
+
GEMFILE
|
|
249
|
+
|
|
250
|
+
output, status = run_bundle("install", chdir: @workdir)
|
|
251
|
+
assert_predicate status, :success?, "bundle install with dependencies failed:\n#{output}"
|
|
252
|
+
|
|
253
|
+
refute_empty @workdir.glob("**/gems/depa-1.0.0")
|
|
254
|
+
refute_empty @workdir.glob("**/gems/depb-1.0.0")
|
|
255
|
+
end
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- [ ] **Step 7: Write test_version_constraint**
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
def test_version_constraint
|
|
262
|
+
gem_v1 = build_gem("multiver", "1.0.0", dir: @gem_build_dir / "v1",
|
|
263
|
+
files: {"lib/multiver.rb" => 'module Multiver; VERSION = "1.0.0"; end'})
|
|
264
|
+
gem_v2 = build_gem("multiver", "2.0.0", dir: @gem_build_dir / "v2",
|
|
265
|
+
files: {"lib/multiver.rb" => 'module Multiver; VERSION = "2.0.0"; end'})
|
|
266
|
+
|
|
267
|
+
vault_path = create_vault(@workdir / "mv.gemv", gem_v1, gem_v2)
|
|
268
|
+
|
|
269
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
270
|
+
source "#{vault_path}", type: :vault do
|
|
271
|
+
gem "multiver", "~> 2.0"
|
|
272
|
+
end
|
|
273
|
+
GEMFILE
|
|
274
|
+
|
|
275
|
+
run_bundle!("install", chdir: @workdir)
|
|
276
|
+
|
|
277
|
+
refute_empty @workdir.glob("**/gems/multiver-2.0.0")
|
|
278
|
+
assert_empty @workdir.glob("**/gems/multiver-1.0.0"), "1.0.0 should not be installed"
|
|
279
|
+
|
|
280
|
+
output, status = run_bundle("exec", "ruby", "-e",
|
|
281
|
+
"require 'multiver'; puts Multiver::VERSION", chdir: @workdir)
|
|
282
|
+
assert_predicate status, :success?, "bundle exec failed:\n#{output}"
|
|
283
|
+
assert_match(/2\.0\.0/, output)
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- [ ] **Step 8: Write test_unsatisfied_constraint**
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
def test_unsatisfied_constraint
|
|
291
|
+
gem_path = build_gem("constrained", "1.0.0", dir: @gem_build_dir)
|
|
292
|
+
vault_path = create_vault(@workdir / "constraint.gemv", gem_path)
|
|
293
|
+
|
|
294
|
+
(@workdir / "Gemfile").write(<<~GEMFILE)
|
|
295
|
+
source "#{vault_path}", type: :vault do
|
|
296
|
+
gem "constrained", "~> 2.0"
|
|
297
|
+
end
|
|
298
|
+
GEMFILE
|
|
299
|
+
|
|
300
|
+
output, status = run_bundle("install", chdir: @workdir)
|
|
301
|
+
refute_predicate status, :success?, "Expected bundle install to fail with unsatisfied constraint"
|
|
302
|
+
assert_match(/could not find/i, output)
|
|
303
|
+
end
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- [ ] **Step 9: Run container tests**
|
|
307
|
+
|
|
308
|
+
Run: `bundle exec rake test:containers`
|
|
309
|
+
Expected: all tests in `test_bundle_install.rb` pass (9 total methods).
|
|
310
|
+
|
|
311
|
+
- [ ] **Step 10: Commit**
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
git add test/containers/test_bundle_install.rb
|
|
315
|
+
git commit -m "Add full Bundler integration suite to container tests"
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### Task 3: Expand test_gem_install.rb with RubyGems plugin scenarios
|
|
321
|
+
|
|
322
|
+
**Files:**
|
|
323
|
+
- Modify: `test/containers/test_gem_install.rb`
|
|
324
|
+
|
|
325
|
+
Add verbose output and file:// URI tests from `RubygemsPluginIntegrationTest`.
|
|
326
|
+
|
|
327
|
+
- [ ] **Step 1: Write test_verbose_output**
|
|
328
|
+
|
|
329
|
+
```ruby
|
|
330
|
+
def test_verbose_output
|
|
331
|
+
gem_path = build_gem("vault_verbose", "1.0.0", dir: @gem_build_dir,
|
|
332
|
+
files: {"lib/vault_verbose.rb" => 'module VaultVerbose; VERSION = "1.0.0"; end'})
|
|
333
|
+
|
|
334
|
+
vault_path = create_vault(@workdir / "verbose.gemv", gem_path)
|
|
335
|
+
|
|
336
|
+
env = gem_env_for(@gem_home)
|
|
337
|
+
output, status = Open3.capture2e(
|
|
338
|
+
env,
|
|
339
|
+
"gem", "install", "--verbose", "--source", vault_path.to_s,
|
|
340
|
+
"--no-document", "vault_verbose",
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
assert_predicate status, :success?, "gem install --verbose failed:\n#{output}"
|
|
344
|
+
assert_match(/Loading .* specs from vault at/, output)
|
|
345
|
+
assert_match(/Extracting vault_verbose-1\.0\.0\.gem from vault at/, output)
|
|
346
|
+
end
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- [ ] **Step 2: Write test_file_uri_source**
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
def test_file_uri_source
|
|
353
|
+
gem_path = build_gem("vault_fileuri", "1.0.0", dir: @gem_build_dir,
|
|
354
|
+
files: {"lib/vault_fileuri.rb" => 'module VaultFileuri; VERSION = "1.0.0"; end'})
|
|
355
|
+
|
|
356
|
+
vault_path = create_vault(@workdir / "fileuri.gemv", gem_path)
|
|
357
|
+
|
|
358
|
+
env = gem_env_for(@gem_home)
|
|
359
|
+
output, status = Open3.capture2e(
|
|
360
|
+
env,
|
|
361
|
+
"gem", "install", "--source", "file://#{vault_path}",
|
|
362
|
+
"--no-document", "vault_fileuri",
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
assert_predicate status, :success?, "gem install with file:// URI failed:\n#{output}"
|
|
366
|
+
assert_match(/installed vault_fileuri/i, output)
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
- [ ] **Step 3: Run container tests**
|
|
371
|
+
|
|
372
|
+
Run: `bundle exec rake test:containers`
|
|
373
|
+
Expected: all `test_gem_install.rb` tests pass (3 total methods).
|
|
374
|
+
|
|
375
|
+
- [ ] **Step 4: Commit**
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
git add test/containers/test_gem_install.rb
|
|
379
|
+
git commit -m "Add verbose and file:// URI tests to container gem install suite"
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
### Task 4: Write test_bundler_inline.rb
|
|
385
|
+
|
|
386
|
+
**Files:**
|
|
387
|
+
- Create: `test/containers/test_bundler_inline.rb`
|
|
388
|
+
|
|
389
|
+
The inline test is special: `bundler/inline` handles its own plugin installation. In the container, the `bundler-source-vault` gem is already installed, so we don't need the `plugin "...", path:` hack. The inline gemfile block should discover the plugin automatically.
|
|
390
|
+
|
|
391
|
+
- [ ] **Step 1: Write the test file**
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
require_relative "test_helper"
|
|
395
|
+
|
|
396
|
+
class BundlerInlineTest < Minitest::Test
|
|
397
|
+
include ContainerTestHelper
|
|
398
|
+
|
|
399
|
+
def setup
|
|
400
|
+
install_gemvault!
|
|
401
|
+
@workdir = Pathname(Dir.mktmpdir("bundler_inline_test"))
|
|
402
|
+
@gem_build_dir = @workdir / "gems"
|
|
403
|
+
@gem_build_dir.mkpath
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def teardown
|
|
407
|
+
@workdir.rmtree
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def test_bundler_inline_with_vault
|
|
411
|
+
gem_path = build_gem("inline_gem", "1.0.0", dir: @gem_build_dir,
|
|
412
|
+
files: {"lib/inline_gem.rb" => 'module InlineGem; VERSION = "1.0.0"; end'})
|
|
413
|
+
|
|
414
|
+
vault_path = create_vault(@workdir / "inline.gemv", gem_path)
|
|
415
|
+
|
|
416
|
+
script = <<~RUBY
|
|
417
|
+
require "bundler/inline"
|
|
418
|
+
|
|
419
|
+
gemfile(true) do
|
|
420
|
+
source "#{vault_path}", type: :vault do
|
|
421
|
+
gem "inline_gem"
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
require "inline_gem"
|
|
426
|
+
puts InlineGem::VERSION
|
|
427
|
+
RUBY
|
|
428
|
+
|
|
429
|
+
script_path = @workdir / "inline_test.rb"
|
|
430
|
+
script_path.write(script)
|
|
431
|
+
|
|
432
|
+
output, status = Open3.capture2e("ruby", script_path.to_s, chdir: @workdir.to_s)
|
|
433
|
+
assert_predicate status, :success?, "bundler/inline failed:\n#{output}"
|
|
434
|
+
assert_match(/1\.0\.0/, output)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
- [ ] **Step 2: Run container tests**
|
|
440
|
+
|
|
441
|
+
Run: `bundle exec rake test:containers`
|
|
442
|
+
Expected: `test_bundler_inline.rb` passes. If `bundler/inline` cannot auto-discover the plugin from installed gems, this test will reveal it — that itself is a valuable finding.
|
|
443
|
+
|
|
444
|
+
- [ ] **Step 3: Commit**
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
git add test/containers/test_bundler_inline.rb
|
|
448
|
+
git commit -m "Add bundler/inline container test"
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
### Task 5: Write test_plugin_root_deps.rb
|
|
454
|
+
|
|
455
|
+
**Files:**
|
|
456
|
+
- Create: `test/containers/test_plugin_root_deps.rb`
|
|
457
|
+
|
|
458
|
+
This test verifies the `shim/plugins.rb` preamble that patches `Gem::Specification.dirs` to include `Bundler::Plugin.root`. The test extracts the preamble (code before the first `require` line), fakes a Plugin.root with a phantom gem spec, runs the preamble, and asserts the phantom dep becomes discoverable.
|
|
459
|
+
|
|
460
|
+
- [ ] **Step 1: Write the test file**
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
require_relative "test_helper"
|
|
464
|
+
|
|
465
|
+
class PluginRootDepsTest < Minitest::Test
|
|
466
|
+
include ContainerTestHelper
|
|
467
|
+
|
|
468
|
+
def setup
|
|
469
|
+
install_gemvault!
|
|
470
|
+
@workdir = Pathname(Dir.mktmpdir("plugin_root_test"))
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def teardown
|
|
474
|
+
@workdir.rmtree
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def test_plugin_root_specs_discoverable
|
|
478
|
+
fake_root = @workdir / "fake_plugin_root"
|
|
479
|
+
fake_specs = fake_root / "specifications"
|
|
480
|
+
fake_specs.mkpath
|
|
481
|
+
|
|
482
|
+
plugins_rb = GEM_SOURCE.join("shim", "plugins.rb").read
|
|
483
|
+
preamble = plugins_rb.lines.take_while { |l| !l.match?(/^require\b/) }.join
|
|
484
|
+
|
|
485
|
+
preamble_path = @workdir / "preamble.rb"
|
|
486
|
+
preamble_path.write(preamble)
|
|
487
|
+
|
|
488
|
+
script_path = @workdir / "test_plugin_root_deps.rb"
|
|
489
|
+
script_path.write(<<~RUBY)
|
|
490
|
+
require "bundler"
|
|
491
|
+
|
|
492
|
+
fake_spec = Gem::Specification.new do |s|
|
|
493
|
+
s.name = "phantom_dep"
|
|
494
|
+
s.version = "1.0.0"
|
|
495
|
+
s.summary = "Simulated plugin dependency"
|
|
496
|
+
s.authors = ["Test"]
|
|
497
|
+
s.files = []
|
|
498
|
+
end
|
|
499
|
+
File.write("#{fake_specs / "phantom_dep-1.0.0.gemspec"}", fake_spec.to_ruby)
|
|
500
|
+
|
|
501
|
+
begin
|
|
502
|
+
Gem::Specification.find_by_name("phantom_dep")
|
|
503
|
+
$stderr.puts "SETUP ERROR: phantom_dep already visible"
|
|
504
|
+
exit 2
|
|
505
|
+
rescue Gem::MissingSpecError
|
|
506
|
+
# Expected
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
module Bundler::Plugin
|
|
510
|
+
remove_method :root if method_defined?(:root)
|
|
511
|
+
define_method(:root) { Pathname.new("#{fake_root}") }
|
|
512
|
+
module_function :root
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
load "#{preamble_path}"
|
|
516
|
+
|
|
517
|
+
begin
|
|
518
|
+
Gem::Specification.find_by_name("phantom_dep")
|
|
519
|
+
puts "PASS"
|
|
520
|
+
rescue Gem::MissingSpecError
|
|
521
|
+
$stderr.puts "FAIL: phantom_dep not findable after plugins.rb preamble"
|
|
522
|
+
exit 1
|
|
523
|
+
end
|
|
524
|
+
RUBY
|
|
525
|
+
|
|
526
|
+
output, status = Open3.capture2e("ruby", script_path.to_s)
|
|
527
|
+
assert_predicate status, :success?,
|
|
528
|
+
"plugins.rb should make Plugin.root deps findable:\n#{output}"
|
|
529
|
+
assert_match(/PASS/, output)
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
- [ ] **Step 2: Run container tests**
|
|
535
|
+
|
|
536
|
+
Run: `bundle exec rake test:containers`
|
|
537
|
+
Expected: `test_plugin_root_deps.rb` passes.
|
|
538
|
+
|
|
539
|
+
- [ ] **Step 3: Commit**
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
git add test/containers/test_plugin_root_deps.rb
|
|
543
|
+
git commit -m "Add plugin root dependency discovery container test"
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
### Task 6: Remove old integration tests
|
|
549
|
+
|
|
550
|
+
**Files:**
|
|
551
|
+
- Delete: `test/integration_test.rb`
|
|
552
|
+
- Modify: `test/rubygems_plugin_test.rb` (remove `RubygemsPluginIntegrationTest`, keep other 3 classes)
|
|
553
|
+
|
|
554
|
+
- [ ] **Step 1: Delete test/integration_test.rb**
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
rm test/integration_test.rb
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
- [ ] **Step 2: Remove RubygemsPluginIntegrationTest from rubygems_plugin_test.rb**
|
|
561
|
+
|
|
562
|
+
Delete lines 281-378 (`class RubygemsPluginIntegrationTest ... end`). Keep:
|
|
563
|
+
- `RubygemsSourceVaultTest` (lines 9-184) — unit tests for `Gem::Source::Vault`
|
|
564
|
+
- `RubygemsResolverVaultSetTest` (lines 186-249) — unit tests for `Gem::Resolver::VaultSet`
|
|
565
|
+
- `RubygemsPluginMonkeyPatchTest` (lines 251-279) — unit tests for monkey-patches
|
|
566
|
+
|
|
567
|
+
- [ ] **Step 3: Run all test suites to confirm nothing broke**
|
|
568
|
+
|
|
569
|
+
Run: `bundle exec rake test && bundle exec rspec && bundle exec rake test:containers`
|
|
570
|
+
Expected:
|
|
571
|
+
- `rake test`: ~107 runs (was 122 — 12 integration + 3 rubygems integration removed)
|
|
572
|
+
- `rspec`: 1 example, 0 failures
|
|
573
|
+
- `rake test:containers`: all container tests pass
|
|
574
|
+
|
|
575
|
+
- [ ] **Step 4: Commit**
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
git add -A
|
|
579
|
+
git commit -m "Remove old integration tests, replaced by container tests"
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
### Task 7: Rebuild cached Docker image
|
|
585
|
+
|
|
586
|
+
After all test files are finalized, rebuild the cached image so CI can use it.
|
|
587
|
+
|
|
588
|
+
- [ ] **Step 1: Rebuild the image**
|
|
589
|
+
|
|
590
|
+
Run: `bundle exec rake test:containers:build`
|
|
591
|
+
|
|
592
|
+
- [ ] **Step 2: Run full container suite with cached image**
|
|
593
|
+
|
|
594
|
+
Run: `bundle exec rake test:containers`
|
|
595
|
+
Expected: all container tests pass, output shows `(cached)`.
|
|
596
|
+
|
|
597
|
+
- [ ] **Step 3: Final verification — all three suites green**
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
bundle exec rake test
|
|
601
|
+
bundle exec rspec
|
|
602
|
+
bundle exec rake test:containers
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
All three must pass with zero failures.
|