torikago 0.0.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/LICENSE.txt +21 -0
- data/README.ja.md +144 -0
- data/README.md +142 -0
- data/exe/torikago +8 -0
- data/lib/torikago/checker.rb +159 -0
- data/lib/torikago/cli.rb +235 -0
- data/lib/torikago/configuration.rb +45 -0
- data/lib/torikago/current_execution.rb +23 -0
- data/lib/torikago/engine_container.rb +340 -0
- data/lib/torikago/errors.rb +16 -0
- data/lib/torikago/gateway.rb +86 -0
- data/lib/torikago/package_api_updater.rb +101 -0
- data/lib/torikago/registry.rb +34 -0
- data/lib/torikago/version.rb +3 -0
- data/lib/torikago.rb +71 -0
- data/test/test_helper.rb +9 -0
- data/test/torikago/checker.rb +164 -0
- data/test/torikago/cli.rb +143 -0
- data/test/torikago/configuration.rb +74 -0
- data/test/torikago/engine_container.rb +549 -0
- data/test/torikago/gateway.rb +155 -0
- data/test/torikago/package_api_updater.rb +94 -0
- data/test/torikago/registry.rb +83 -0
- data/test/torikago/version.rb +11 -0
- data/test/torikago.rb +110 -0
- metadata +73 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
require_relative "../test_helper"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "open3"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class TorikagoEngineContainerTest < Minitest::Test
|
|
8
|
+
def teardown
|
|
9
|
+
Object.send(:remove_const, :Foo) if Object.const_defined?(:Foo, false)
|
|
10
|
+
Object.send(:remove_const, :SharedDependency) if Object.const_defined?(:SharedDependency, false)
|
|
11
|
+
Object.send(:remove_const, :SetupProbe) if Object.const_defined?(:SetupProbe, false)
|
|
12
|
+
Object.send(:remove_const, :VersionedFormatter) if Object.const_defined?(:VersionedFormatter, false)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_call_loads_the_public_api_class_and_executes_call
|
|
16
|
+
with_module_root do |module_root|
|
|
17
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
18
|
+
|
|
19
|
+
result = container.call("Foo::ListProductsQuery")
|
|
20
|
+
|
|
21
|
+
assert_equal ["coffee-beans", "drip-bag"], result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_call_reuses_loaded_runtime_files_across_calls
|
|
26
|
+
with_module_root do |module_root|
|
|
27
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
28
|
+
|
|
29
|
+
first = container.call("Foo::ListProductsQuery")
|
|
30
|
+
second = container.call("Foo::ListProductsQuery")
|
|
31
|
+
|
|
32
|
+
assert_equal ["coffee-beans", "drip-bag"], first
|
|
33
|
+
assert_equal ["coffee-beans", "drip-bag"], second
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_call_sets_the_current_box_during_execution
|
|
38
|
+
with_module_root do |module_root|
|
|
39
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
40
|
+
|
|
41
|
+
result = container.call("Foo::CurrentBoxQuery")
|
|
42
|
+
|
|
43
|
+
assert_equal :foo, result
|
|
44
|
+
assert_nil Torikago::CurrentExecution.current_box
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_call_uses_a_configured_entrypoint_directory_when_present
|
|
49
|
+
with_custom_entrypoint_module_root do |module_root|
|
|
50
|
+
container = Torikago::EngineContainer.new(
|
|
51
|
+
name: :foo,
|
|
52
|
+
module_root: module_root,
|
|
53
|
+
entrypoint: "components/public_api"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
result = container.call("Foo::CustomEntryPointQuery")
|
|
57
|
+
|
|
58
|
+
assert_equal "custom entrypoint", result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_call_does_not_load_parent_files_when_configured_entrypoint_directory_is_missing
|
|
63
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
64
|
+
FileUtils.mkdir_p(File.join(module_root, "app/controllers/foo"))
|
|
65
|
+
File.write(
|
|
66
|
+
File.join(module_root, "app/controllers/foo/widgets_controller.rb"),
|
|
67
|
+
<<~RUBY
|
|
68
|
+
raise "app/controllers should not be loaded as public API"
|
|
69
|
+
RUBY
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
container = Torikago::EngineContainer.new(
|
|
73
|
+
name: :foo,
|
|
74
|
+
module_root: module_root,
|
|
75
|
+
entrypoint: "app/package_api"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
error = assert_raises(NameError) do
|
|
79
|
+
container.call("Foo::MissingQuery")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
assert_match(/MissingQuery/, error.message)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_call_prepends_explicit_gemfile_require_paths_before_loading_runtime
|
|
87
|
+
with_module_root do |module_root|
|
|
88
|
+
dependency_lib = File.join(module_root, "vendor/example-gem-1.2.3/lib")
|
|
89
|
+
FileUtils.mkdir_p(dependency_lib)
|
|
90
|
+
File.write(
|
|
91
|
+
File.join(dependency_lib, "shared_dependency.rb"),
|
|
92
|
+
<<~RUBY
|
|
93
|
+
module SharedDependency
|
|
94
|
+
VERSION = "module-local"
|
|
95
|
+
end
|
|
96
|
+
RUBY
|
|
97
|
+
)
|
|
98
|
+
File.write(File.join(module_root, "Gemfile"), "")
|
|
99
|
+
File.write(
|
|
100
|
+
File.join(module_root, "app/package_api/foo/dependency_version_query.rb"),
|
|
101
|
+
<<~RUBY
|
|
102
|
+
require "shared_dependency"
|
|
103
|
+
|
|
104
|
+
class Foo::DependencyVersionQuery
|
|
105
|
+
def call
|
|
106
|
+
SharedDependency::VERSION
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
RUBY
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
assert_ruby_box_child_process(
|
|
113
|
+
<<~RUBY,
|
|
114
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
115
|
+
module_root = ARGV.fetch(1)
|
|
116
|
+
dependency_lib = ARGV.fetch(2)
|
|
117
|
+
|
|
118
|
+
require "torikago"
|
|
119
|
+
|
|
120
|
+
container = Torikago::EngineContainer.new(
|
|
121
|
+
name: :foo,
|
|
122
|
+
module_root: module_root,
|
|
123
|
+
gemfile: "Gemfile",
|
|
124
|
+
gemfile_dependency_loader: lambda do |path|
|
|
125
|
+
raise "unexpected Gemfile path: \#{path}" unless path.to_s == File.join(module_root, "Gemfile")
|
|
126
|
+
|
|
127
|
+
[{ name: "example-gem", requirement: "= 1.2.3", require_paths: [dependency_lib] }]
|
|
128
|
+
end,
|
|
129
|
+
gem_activator: lambda do |_dependency|
|
|
130
|
+
raise "gem activator should not be called when Ruby::Box is active"
|
|
131
|
+
end
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
raise "unexpected result" unless container.call("Foo::DependencyVersionQuery") == "module-local"
|
|
135
|
+
|
|
136
|
+
puts "ok"
|
|
137
|
+
RUBY
|
|
138
|
+
module_root,
|
|
139
|
+
dependency_lib
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_call_resolves_path_gem_require_paths_inside_ruby_box
|
|
145
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
146
|
+
dependency_lib = File.join(module_root, "vendor/example-gem-1.0.0/lib")
|
|
147
|
+
FileUtils.mkdir_p(dependency_lib)
|
|
148
|
+
File.write(
|
|
149
|
+
File.join(module_root, "Gemfile"),
|
|
150
|
+
<<~RUBY
|
|
151
|
+
source "https://rubygems.org"
|
|
152
|
+
|
|
153
|
+
gem "example-gem", path: "vendor/example-gem-1.0.0"
|
|
154
|
+
RUBY
|
|
155
|
+
)
|
|
156
|
+
File.write(
|
|
157
|
+
File.join(module_root, "vendor/example-gem-1.0.0/example-gem.gemspec"),
|
|
158
|
+
<<~RUBY
|
|
159
|
+
Gem::Specification.new do |spec|
|
|
160
|
+
spec.name = "example-gem"
|
|
161
|
+
spec.version = "1.0.0"
|
|
162
|
+
spec.summary = "test gem"
|
|
163
|
+
spec.authors = ["torikago"]
|
|
164
|
+
spec.files = ["lib/shared_dependency.rb"]
|
|
165
|
+
spec.require_paths = ["lib"]
|
|
166
|
+
end
|
|
167
|
+
RUBY
|
|
168
|
+
)
|
|
169
|
+
File.write(
|
|
170
|
+
File.join(dependency_lib, "shared_dependency.rb"),
|
|
171
|
+
<<~RUBY
|
|
172
|
+
module SharedDependency
|
|
173
|
+
VERSION = "module-local"
|
|
174
|
+
end
|
|
175
|
+
RUBY
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
179
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
180
|
+
File.write(
|
|
181
|
+
File.join(package_api_dir, "dependency_version_query.rb"),
|
|
182
|
+
<<~RUBY
|
|
183
|
+
require "shared_dependency"
|
|
184
|
+
|
|
185
|
+
class Foo::DependencyVersionQuery
|
|
186
|
+
def call
|
|
187
|
+
SharedDependency::VERSION
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
RUBY
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
assert_ruby_box_child_process(
|
|
194
|
+
<<~RUBY,
|
|
195
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
196
|
+
module_root = ARGV.fetch(1)
|
|
197
|
+
|
|
198
|
+
require "torikago"
|
|
199
|
+
|
|
200
|
+
container = Torikago::EngineContainer.new(
|
|
201
|
+
name: :foo,
|
|
202
|
+
module_root: module_root,
|
|
203
|
+
gemfile: "Gemfile"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
raise "unexpected result" unless container.call("Foo::DependencyVersionQuery") == "module-local"
|
|
207
|
+
|
|
208
|
+
puts "ok"
|
|
209
|
+
RUBY
|
|
210
|
+
module_root
|
|
211
|
+
)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_call_resolves_path_gem_require_paths_from_the_module_gemfile
|
|
216
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
217
|
+
dependency_root = File.join(module_root, "vendor/versioned_formatter")
|
|
218
|
+
dependency_lib = File.join(dependency_root, "lib")
|
|
219
|
+
FileUtils.mkdir_p(dependency_lib)
|
|
220
|
+
File.write(
|
|
221
|
+
File.join(dependency_root, "versioned_formatter.gemspec"),
|
|
222
|
+
<<~RUBY
|
|
223
|
+
Gem::Specification.new do |spec|
|
|
224
|
+
spec.name = "versioned_formatter"
|
|
225
|
+
spec.version = "1.0.0"
|
|
226
|
+
spec.summary = "test gem"
|
|
227
|
+
spec.authors = ["torikago"]
|
|
228
|
+
spec.files = ["lib/versioned_formatter.rb"]
|
|
229
|
+
spec.require_paths = ["lib"]
|
|
230
|
+
end
|
|
231
|
+
RUBY
|
|
232
|
+
)
|
|
233
|
+
File.write(
|
|
234
|
+
File.join(dependency_lib, "versioned_formatter.rb"),
|
|
235
|
+
<<~RUBY
|
|
236
|
+
module VersionedFormatter
|
|
237
|
+
VERSION = "1.0.0"
|
|
238
|
+
end
|
|
239
|
+
RUBY
|
|
240
|
+
)
|
|
241
|
+
File.write(
|
|
242
|
+
File.join(module_root, "Gemfile"),
|
|
243
|
+
<<~RUBY
|
|
244
|
+
source "https://rubygems.org"
|
|
245
|
+
|
|
246
|
+
gem "versioned_formatter", path: "vendor/versioned_formatter"
|
|
247
|
+
RUBY
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
251
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
252
|
+
File.write(
|
|
253
|
+
File.join(package_api_dir, "gemfile_dependency_query.rb"),
|
|
254
|
+
<<~RUBY
|
|
255
|
+
require "versioned_formatter"
|
|
256
|
+
|
|
257
|
+
class Foo::GemfileDependencyQuery
|
|
258
|
+
def call
|
|
259
|
+
VersionedFormatter::VERSION
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
RUBY
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
assert_ruby_box_child_process(
|
|
266
|
+
<<~RUBY,
|
|
267
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
268
|
+
module_root = ARGV.fetch(1)
|
|
269
|
+
|
|
270
|
+
require "torikago"
|
|
271
|
+
|
|
272
|
+
container = Torikago::EngineContainer.new(
|
|
273
|
+
name: :foo,
|
|
274
|
+
module_root: module_root,
|
|
275
|
+
gemfile: "Gemfile"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
raise "unexpected result" unless container.call("Foo::GemfileDependencyQuery") == "1.0.0"
|
|
279
|
+
|
|
280
|
+
puts "ok"
|
|
281
|
+
RUBY
|
|
282
|
+
module_root
|
|
283
|
+
)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def test_call_resolves_exact_installed_gem_require_paths_after_a_different_version_is_active
|
|
288
|
+
skip("requires rake 13.3.1 and 13.4.2") unless installed_gem?("rake", "= 13.3.1") && installed_gem?("rake", "= 13.4.2")
|
|
289
|
+
|
|
290
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
291
|
+
File.write(
|
|
292
|
+
File.join(module_root, "Gemfile"),
|
|
293
|
+
<<~RUBY
|
|
294
|
+
source "https://rubygems.org"
|
|
295
|
+
|
|
296
|
+
gem "rake", "= 13.3.1"
|
|
297
|
+
RUBY
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
Kernel.send(:gem, "rake", "= 13.4.2")
|
|
301
|
+
require "rake"
|
|
302
|
+
|
|
303
|
+
container = Torikago::EngineContainer.new(
|
|
304
|
+
name: :foo,
|
|
305
|
+
module_root: module_root,
|
|
306
|
+
gemfile: "Gemfile"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
dependencies = container.send(:load_gemfile_dependencies, Pathname(File.join(module_root, "Gemfile")))
|
|
310
|
+
|
|
311
|
+
assert_equal "rake", dependencies.fetch(0).fetch(:name)
|
|
312
|
+
assert_equal "= 13.3.1", dependencies.fetch(0).fetch(:requirement)
|
|
313
|
+
assert_match(%r{/rake-13\.3\.1/lib\z}, dependencies.fetch(0).fetch(:require_paths).first)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def test_call_reports_missing_installed_gemfile_dependencies_clearly
|
|
318
|
+
with_module_root do |module_root|
|
|
319
|
+
File.write(
|
|
320
|
+
File.join(module_root, "Gemfile"),
|
|
321
|
+
<<~RUBY
|
|
322
|
+
source "https://rubygems.org"
|
|
323
|
+
|
|
324
|
+
gem "example-gem", "= 9.9.9"
|
|
325
|
+
RUBY
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
container = Torikago::EngineContainer.new(
|
|
329
|
+
name: :foo,
|
|
330
|
+
module_root: module_root,
|
|
331
|
+
gemfile: "Gemfile"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
error = assert_raises(Torikago::GemfileOverrideError) do
|
|
335
|
+
container.call("Foo::ListProductsQuery")
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
assert_match(/example-gem/, error.message)
|
|
339
|
+
assert_match(/foo/, error.message)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def test_call_loads_configured_setup_before_public_api
|
|
344
|
+
with_module_root do |module_root|
|
|
345
|
+
setup_dir = File.join(module_root, "config")
|
|
346
|
+
FileUtils.mkdir_p(setup_dir)
|
|
347
|
+
File.write(
|
|
348
|
+
File.join(setup_dir, "box_setup.rb"),
|
|
349
|
+
<<~RUBY
|
|
350
|
+
module SetupProbe
|
|
351
|
+
VALUE = "patched"
|
|
352
|
+
end
|
|
353
|
+
RUBY
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
File.write(
|
|
357
|
+
File.join(module_root, "app/package_api/foo/setup_aware_query.rb"),
|
|
358
|
+
<<~RUBY
|
|
359
|
+
class Foo::SetupAwareQuery
|
|
360
|
+
def call
|
|
361
|
+
SetupProbe::VALUE
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
RUBY
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
container = Torikago::EngineContainer.new(
|
|
368
|
+
name: :foo,
|
|
369
|
+
module_root: module_root,
|
|
370
|
+
setup: "config/box_setup.rb"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
assert_equal "patched", container.call("Foo::SetupAwareQuery")
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def test_call_loads_setup_only_once
|
|
378
|
+
with_module_root do |module_root|
|
|
379
|
+
setup_dir = File.join(module_root, "config")
|
|
380
|
+
FileUtils.mkdir_p(setup_dir)
|
|
381
|
+
File.write(
|
|
382
|
+
File.join(setup_dir, "box_setup.rb"),
|
|
383
|
+
<<~RUBY
|
|
384
|
+
module SetupProbe
|
|
385
|
+
RUNS = (const_defined?(:RUNS) ? RUNS + 1 : 1)
|
|
386
|
+
end
|
|
387
|
+
RUBY
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
File.write(
|
|
391
|
+
File.join(module_root, "app/package_api/foo/setup_count_query.rb"),
|
|
392
|
+
<<~RUBY
|
|
393
|
+
class Foo::SetupCountQuery
|
|
394
|
+
def call
|
|
395
|
+
SetupProbe::RUNS
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
RUBY
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
container = Torikago::EngineContainer.new(
|
|
402
|
+
name: :foo,
|
|
403
|
+
module_root: module_root,
|
|
404
|
+
setup: "config/box_setup.rb"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
assert_equal 1, container.call("Foo::SetupCountQuery")
|
|
408
|
+
assert_equal 1, container.call("Foo::SetupCountQuery")
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def test_call_raises_load_error_when_setup_is_missing
|
|
413
|
+
with_module_root do |module_root|
|
|
414
|
+
container = Torikago::EngineContainer.new(
|
|
415
|
+
name: :foo,
|
|
416
|
+
module_root: module_root,
|
|
417
|
+
setup: "config/missing_setup.rb"
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
error = assert_raises(LoadError) do
|
|
421
|
+
container.call("Foo::ListProductsQuery")
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
assert_match(/setup not found/, error.message)
|
|
425
|
+
assert_match(/missing_setup\.rb/, error.message)
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def test_call_loads_plain_gateway_models_without_eager_loading_rails_models
|
|
430
|
+
with_module_root do |module_root|
|
|
431
|
+
model_dir = File.join(module_root, "app/models/foo")
|
|
432
|
+
FileUtils.mkdir_p(model_dir)
|
|
433
|
+
File.write(
|
|
434
|
+
File.join(model_dir, "order_store.rb"),
|
|
435
|
+
<<~RUBY
|
|
436
|
+
class Foo::OrderStore
|
|
437
|
+
def self.all
|
|
438
|
+
["order"]
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
RUBY
|
|
442
|
+
)
|
|
443
|
+
File.write(
|
|
444
|
+
File.join(model_dir, "foo_record.rb"),
|
|
445
|
+
<<~RUBY
|
|
446
|
+
raise "ActiveRecord models should not be eager-loaded by Gateway"
|
|
447
|
+
|
|
448
|
+
class Foo::FooRecord < ActiveRecord::Base
|
|
449
|
+
end
|
|
450
|
+
RUBY
|
|
451
|
+
)
|
|
452
|
+
File.write(
|
|
453
|
+
File.join(module_root, "app/package_api/foo/list_orders_query.rb"),
|
|
454
|
+
<<~RUBY
|
|
455
|
+
class Foo::ListOrdersQuery
|
|
456
|
+
def call
|
|
457
|
+
Foo::OrderStore.all
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
RUBY
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
464
|
+
|
|
465
|
+
assert_equal ["order"], container.call("Foo::ListOrdersQuery")
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
private
|
|
470
|
+
|
|
471
|
+
def with_module_root
|
|
472
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
473
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
474
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
475
|
+
|
|
476
|
+
File.write(
|
|
477
|
+
File.join(package_api_dir, "list_products_query.rb"),
|
|
478
|
+
<<~RUBY
|
|
479
|
+
class Foo::ListProductsQuery
|
|
480
|
+
def call
|
|
481
|
+
["coffee-beans", "drip-bag"]
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
RUBY
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
File.write(
|
|
488
|
+
File.join(package_api_dir, "current_box_query.rb"),
|
|
489
|
+
<<~RUBY
|
|
490
|
+
class Foo::CurrentBoxQuery
|
|
491
|
+
def call
|
|
492
|
+
Torikago::CurrentExecution.current_box
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
RUBY
|
|
496
|
+
)
|
|
497
|
+
yield module_root
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def with_custom_entrypoint_module_root
|
|
502
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
503
|
+
package_api_dir = File.join(module_root, "components/public_api/foo")
|
|
504
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
505
|
+
|
|
506
|
+
File.write(
|
|
507
|
+
File.join(package_api_dir, "custom_entry_point_query.rb"),
|
|
508
|
+
<<~RUBY
|
|
509
|
+
class Foo::CustomEntryPointQuery
|
|
510
|
+
def call
|
|
511
|
+
"custom entrypoint"
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
RUBY
|
|
515
|
+
)
|
|
516
|
+
yield module_root
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def assert_ruby_box_child_process(script, *args)
|
|
521
|
+
stdout, stderr, status = Open3.capture3(
|
|
522
|
+
{ "RUBY_BOX" => "1" },
|
|
523
|
+
RbConfig.ruby,
|
|
524
|
+
"-e",
|
|
525
|
+
script,
|
|
526
|
+
File.expand_path("../../lib", __dir__),
|
|
527
|
+
*args
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
assert_predicate status, :success?, stderr
|
|
531
|
+
assert_equal "ok\n", stdout
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def installed_gem?(name, requirement)
|
|
535
|
+
return true unless installed_specs_for(name, requirement).empty?
|
|
536
|
+
|
|
537
|
+
gem_requirement = Gem::Requirement.new(requirement)
|
|
538
|
+
Gem::Specification.dirs.any? do |specification_dir|
|
|
539
|
+
Dir[File.join(specification_dir, "#{name}-*.gemspec")].any? do |gemspec_path|
|
|
540
|
+
spec = Gem::Specification.load(gemspec_path)
|
|
541
|
+
spec && spec.name == name && gem_requirement.satisfied_by?(spec.version)
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def installed_specs_for(name, requirement)
|
|
547
|
+
Gem::Specification.find_all_by_name(name, requirement)
|
|
548
|
+
end
|
|
549
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
require_relative "../test_helper"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "tmpdir"
|
|
4
|
+
|
|
5
|
+
class TorikagoGatewayTest < Minitest::Test
|
|
6
|
+
FakeContainer = Struct.new(:calls) do
|
|
7
|
+
def call(public_api_class_name, *args, **kwargs)
|
|
8
|
+
calls << [public_api_class_name, args, kwargs]
|
|
9
|
+
"result for #{public_api_class_name}"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class FakeRegistry
|
|
14
|
+
def initialize(containers)
|
|
15
|
+
@containers = containers
|
|
16
|
+
@resolved = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :resolved
|
|
20
|
+
|
|
21
|
+
def resolve(name)
|
|
22
|
+
normalized_name = name.to_sym
|
|
23
|
+
resolved << normalized_name
|
|
24
|
+
@containers.fetch(normalized_name)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_class_level_call_delegates_to_the_shared_gateway
|
|
29
|
+
original_gateway = Torikago.instance_variable_get(:@gateway)
|
|
30
|
+
fake_gateway = Object.new
|
|
31
|
+
called_with = nil
|
|
32
|
+
|
|
33
|
+
fake_gateway.define_singleton_method(:call) do |*args, **kwargs|
|
|
34
|
+
called_with = [args, kwargs]
|
|
35
|
+
"ok"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Torikago.instance_variable_set(:@gateway, fake_gateway)
|
|
39
|
+
|
|
40
|
+
result = Torikago::Gateway.call("Foo::ListProductsQuery", page: 1)
|
|
41
|
+
|
|
42
|
+
assert_equal "ok", result
|
|
43
|
+
assert_equal [["Foo::ListProductsQuery"], { page: 1 }], called_with
|
|
44
|
+
ensure
|
|
45
|
+
Torikago.instance_variable_set(:@gateway, original_gateway)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_call_delegates_to_the_target_module_container
|
|
49
|
+
configuration = Torikago::Configuration.new
|
|
50
|
+
configuration.register(:foo, root: "/modules/foo")
|
|
51
|
+
write_package_api_manifest("/modules/foo", "Foo::ListProductsQuery" => { "allowed_callers" => [] })
|
|
52
|
+
|
|
53
|
+
container = FakeContainer.new([])
|
|
54
|
+
registry = FakeRegistry.new(foo: container)
|
|
55
|
+
gateway = Torikago::Gateway.new(
|
|
56
|
+
registry: registry,
|
|
57
|
+
configuration: configuration,
|
|
58
|
+
manifest_loader: ->(_definition) { { "exports" => { "Foo::ListProductsQuery" => { "allowed_callers" => [] } } } }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
result = gateway.call("Foo::ListProductsQuery", page: 1)
|
|
62
|
+
|
|
63
|
+
assert_equal "result for Foo::ListProductsQuery", result
|
|
64
|
+
assert_equal [:foo], registry.resolved
|
|
65
|
+
assert_equal [["Foo::ListProductsQuery", [], { page: 1 }]], container.calls
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_call_rejects_public_api_not_declared_in_package_api_manifest
|
|
69
|
+
configuration = Torikago::Configuration.new
|
|
70
|
+
configuration.register(:foo, root: "/modules/foo")
|
|
71
|
+
registry = FakeRegistry.new(foo: FakeContainer.new([]))
|
|
72
|
+
gateway = Torikago::Gateway.new(
|
|
73
|
+
registry: registry,
|
|
74
|
+
configuration: configuration,
|
|
75
|
+
manifest_loader: ->(_definition) { { "exports" => {} } }
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
error = assert_raises(Torikago::PublicApiError) do
|
|
79
|
+
gateway.call("Foo::MissingCommand")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
assert_match(/Foo::MissingCommand/, error.message)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_call_allows_host_app_invocation_without_allowed_callers
|
|
86
|
+
configuration = Torikago::Configuration.new
|
|
87
|
+
configuration.register(:foo, root: "/modules/foo")
|
|
88
|
+
container = FakeContainer.new([])
|
|
89
|
+
registry = FakeRegistry.new(foo: container)
|
|
90
|
+
gateway = Torikago::Gateway.new(
|
|
91
|
+
registry: registry,
|
|
92
|
+
configuration: configuration,
|
|
93
|
+
manifest_loader: ->(_definition) { { "exports" => { "Foo::ListProductsQuery" => { "allowed_callers" => [] } } } }
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
gateway.call("Foo::ListProductsQuery")
|
|
97
|
+
|
|
98
|
+
assert_equal [["Foo::ListProductsQuery", [], {}]], container.calls
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_call_allows_box_dependency_declared_in_package_api_manifest
|
|
102
|
+
configuration = Torikago::Configuration.new
|
|
103
|
+
configuration.register(:foo, root: "/modules/foo")
|
|
104
|
+
configuration.register(:bar, root: "/modules/bar")
|
|
105
|
+
bar_container = FakeContainer.new([])
|
|
106
|
+
registry = FakeRegistry.new(foo: FakeContainer.new([]), bar: bar_container)
|
|
107
|
+
|
|
108
|
+
gateway = Torikago::Gateway.new(
|
|
109
|
+
registry: registry,
|
|
110
|
+
configuration: configuration,
|
|
111
|
+
manifest_loader: lambda do |definition|
|
|
112
|
+
if definition.name == :bar
|
|
113
|
+
{ "exports" => { "Bar::SubmitOrderCommand" => { "allowed_callers" => ["foo"] } } }
|
|
114
|
+
else
|
|
115
|
+
{ "exports" => {} }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
Torikago::CurrentExecution.with_box(:foo) do
|
|
121
|
+
result = gateway.call("Bar::SubmitOrderCommand", order_id: 1)
|
|
122
|
+
|
|
123
|
+
assert_equal "result for Bar::SubmitOrderCommand", result
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
assert_equal [["Bar::SubmitOrderCommand", [], { order_id: 1 }]], bar_container.calls
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_call_rejects_box_dependency_not_declared_in_package_api_manifest
|
|
130
|
+
configuration = Torikago::Configuration.new
|
|
131
|
+
configuration.register(:foo, root: "/modules/foo")
|
|
132
|
+
configuration.register(:bar, root: "/modules/bar")
|
|
133
|
+
registry = FakeRegistry.new(foo: FakeContainer.new([]), bar: FakeContainer.new([]))
|
|
134
|
+
|
|
135
|
+
gateway = Torikago::Gateway.new(
|
|
136
|
+
registry: registry,
|
|
137
|
+
configuration: configuration,
|
|
138
|
+
manifest_loader: ->(_definition) { { "exports" => { "Bar::SubmitOrderCommand" => { "allowed_callers" => ["admin"] } } } }
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
error = assert_raises(Torikago::DependencyError) do
|
|
142
|
+
Torikago::CurrentExecution.with_box(:foo) do
|
|
143
|
+
gateway.call("Bar::SubmitOrderCommand")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
assert_match(/foo/, error.message)
|
|
148
|
+
assert_match(/bar/i, error.message)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def write_package_api_manifest(_root, _entries)
|
|
154
|
+
end
|
|
155
|
+
end
|