torikago 0.0.1 → 0.1.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 +4 -4
- data/README.ja.md +102 -15
- data/README.md +106 -15
- data/lib/torikago/checker.rb +279 -30
- data/lib/torikago/cli.rb +24 -10
- data/lib/torikago/configuration.rb +4 -3
- data/lib/torikago/controller_endpoint.rb +30 -0
- data/lib/torikago/engine_container.rb +485 -58
- data/lib/torikago/gateway.rb +75 -14
- data/lib/torikago/invocation.rb +26 -0
- data/lib/torikago/package_api_updater.rb +32 -11
- data/lib/torikago/rack_endpoint.rb +23 -0
- data/lib/torikago/registry.rb +4 -2
- data/lib/torikago/root_constant_resolver.rb +69 -0
- data/lib/torikago/version.rb +1 -1
- data/lib/torikago.rb +8 -0
- data/sig/torikago.rbs +440 -0
- data/test/test_helper.rb +0 -1
- data/test/torikago/checker.rb +119 -93
- data/test/torikago/cli.rb +19 -4
- data/test/torikago/configuration.rb +2 -0
- data/test/torikago/controller_endpoint.rb +30 -0
- data/test/torikago/engine_container.rb +824 -30
- data/test/torikago/gateway.rb +138 -92
- data/test/torikago/package_api_updater.rb +6 -3
- data/test/torikago/rack_endpoint.rb +25 -0
- data/test/torikago/registry.rb +15 -0
- data/test/torikago/root_constant_resolver.rb +114 -0
- metadata +9 -1
|
@@ -10,42 +10,426 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
10
10
|
Object.send(:remove_const, :SharedDependency) if Object.const_defined?(:SharedDependency, false)
|
|
11
11
|
Object.send(:remove_const, :SetupProbe) if Object.const_defined?(:SetupProbe, false)
|
|
12
12
|
Object.send(:remove_const, :VersionedFormatter) if Object.const_defined?(:VersionedFormatter, false)
|
|
13
|
+
Object.send(:remove_const, :WidgetsController) if Object.const_defined?(:WidgetsController, false)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
def
|
|
16
|
+
def test_invoke_loads_the_public_api_class_and_executes_call
|
|
16
17
|
with_module_root do |module_root|
|
|
17
18
|
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
18
19
|
|
|
19
|
-
result = container.
|
|
20
|
+
result = container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
20
21
|
|
|
21
22
|
assert_equal ["coffee-beans", "drip-bag"], result
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
def
|
|
26
|
+
def test_invoke_reuses_loaded_runtime_files_across_calls
|
|
26
27
|
with_module_root do |module_root|
|
|
27
28
|
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
28
29
|
|
|
29
|
-
first = container.
|
|
30
|
-
second = container.
|
|
30
|
+
first = container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
31
|
+
second = container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
31
32
|
|
|
32
33
|
assert_equal ["coffee-beans", "drip-bag"], first
|
|
33
34
|
assert_equal ["coffee-beans", "drip-bag"], second
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
def
|
|
38
|
+
def test_invoke_sets_the_current_box_during_execution
|
|
38
39
|
with_module_root do |module_root|
|
|
39
40
|
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
40
41
|
|
|
41
|
-
result = container.
|
|
42
|
+
result = container.invoke("Foo::CurrentBoxQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
42
43
|
|
|
43
44
|
assert_equal :foo, result
|
|
44
45
|
assert_nil Torikago::CurrentExecution.current_box
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def
|
|
49
|
+
def test_invoke_shares_root_constants_and_runtime_gateway_with_a_ruby_box
|
|
50
|
+
Dir.mktmpdir("torikago-root-access") do |application_root|
|
|
51
|
+
module_root = File.join(application_root, "modules/foo")
|
|
52
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
53
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
54
|
+
|
|
55
|
+
root_constant_file = File.join(application_root, "root_constants.rb")
|
|
56
|
+
File.write(
|
|
57
|
+
root_constant_file,
|
|
58
|
+
<<~RUBY
|
|
59
|
+
class RootOrder
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class RootCustomerQuery
|
|
63
|
+
def self.call
|
|
64
|
+
"root customer"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
RUBY
|
|
68
|
+
)
|
|
69
|
+
lazy_constant_file = File.join(application_root, "root_lazy_query.rb")
|
|
70
|
+
File.write(
|
|
71
|
+
lazy_constant_file,
|
|
72
|
+
<<~RUBY
|
|
73
|
+
class RootLazyQuery
|
|
74
|
+
def self.call
|
|
75
|
+
"lazy root"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
RUBY
|
|
79
|
+
)
|
|
80
|
+
File.write(
|
|
81
|
+
File.join(package_api_dir, "root_access_query.rb"),
|
|
82
|
+
<<~RUBY
|
|
83
|
+
class Foo::RootAccessQuery < ::RootOrder
|
|
84
|
+
def call
|
|
85
|
+
[
|
|
86
|
+
::RootCustomerQuery.call,
|
|
87
|
+
::RootLazyQuery.call,
|
|
88
|
+
self.class.superclass.object_id,
|
|
89
|
+
Torikago.object_id,
|
|
90
|
+
Torikago::Gateway.object_id,
|
|
91
|
+
Torikago::DependencyError.object_id,
|
|
92
|
+
Torikago::Gateway.invoke("Bar::Ping", :call)
|
|
93
|
+
]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
RUBY
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
assert_ruby_box_child_process(
|
|
100
|
+
<<~RUBY,
|
|
101
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
102
|
+
module_root = ARGV.fetch(1)
|
|
103
|
+
root_constant_file = ARGV.fetch(2)
|
|
104
|
+
lazy_constant_file = ARGV.fetch(3)
|
|
105
|
+
|
|
106
|
+
require "torikago"
|
|
107
|
+
load root_constant_file
|
|
108
|
+
autoload :RootLazyQuery, lazy_constant_file
|
|
109
|
+
gateway_probe = Object.new
|
|
110
|
+
gateway_probe.define_singleton_method(:invoke) { |class_name, method_name| [class_name, method_name] }
|
|
111
|
+
Torikago.instance_variable_set(:@gateway, gateway_probe)
|
|
112
|
+
|
|
113
|
+
container = Torikago::EngineContainer.new(
|
|
114
|
+
name: :foo,
|
|
115
|
+
module_root: module_root,
|
|
116
|
+
registered_roots: [module_root]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
result = container.invoke("Foo::RootAccessQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
120
|
+
expected_shared_values = [
|
|
121
|
+
"root customer",
|
|
122
|
+
"lazy root",
|
|
123
|
+
RootOrder.object_id,
|
|
124
|
+
Torikago::Gateway.object_id,
|
|
125
|
+
Torikago::DependencyError.object_id,
|
|
126
|
+
["Bar::Ping", :call]
|
|
127
|
+
]
|
|
128
|
+
shared_values = result.values_at(0, 1, 2, 4, 5, 6)
|
|
129
|
+
raise "unexpected result: \#{result.inspect}" unless shared_values == expected_shared_values
|
|
130
|
+
raise "host Torikago namespace leaked into module Box" if result.fetch(3) == Torikago.object_id
|
|
131
|
+
|
|
132
|
+
Object.send(:remove_const, :RootCustomerQuery)
|
|
133
|
+
class RootCustomerQuery
|
|
134
|
+
def self.call
|
|
135
|
+
"reloaded root customer"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
reloaded_result = container.invoke("Foo::RootAccessQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
139
|
+
raise "stale root constant: \#{reloaded_result.inspect}" unless reloaded_result.first == "reloaded root customer"
|
|
140
|
+
|
|
141
|
+
puts "ok"
|
|
142
|
+
RUBY
|
|
143
|
+
module_root,
|
|
144
|
+
root_constant_file,
|
|
145
|
+
lazy_constant_file
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def test_invoke_prefers_module_local_constants_over_root_constants
|
|
151
|
+
Dir.mktmpdir("torikago-root-access") do |application_root|
|
|
152
|
+
module_root = File.join(application_root, "modules/foo")
|
|
153
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
154
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
155
|
+
|
|
156
|
+
root_constant_file = File.join(application_root, "root_constant.rb")
|
|
157
|
+
File.write(root_constant_file, "RootPriority = \"root\"\n")
|
|
158
|
+
File.write(
|
|
159
|
+
File.join(package_api_dir, "priority_query.rb"),
|
|
160
|
+
<<~RUBY
|
|
161
|
+
RootPriority = "module"
|
|
162
|
+
|
|
163
|
+
class Foo::PriorityQuery
|
|
164
|
+
def call
|
|
165
|
+
RootPriority
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
RUBY
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
assert_ruby_box_child_process(
|
|
172
|
+
<<~RUBY,
|
|
173
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
174
|
+
module_root = ARGV.fetch(1)
|
|
175
|
+
root_constant_file = ARGV.fetch(2)
|
|
176
|
+
|
|
177
|
+
require "torikago"
|
|
178
|
+
load root_constant_file
|
|
179
|
+
|
|
180
|
+
container = Torikago::EngineContainer.new(
|
|
181
|
+
name: :foo,
|
|
182
|
+
module_root: module_root,
|
|
183
|
+
registered_roots: [module_root]
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
result = container.invoke("Foo::PriorityQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
187
|
+
raise "unexpected result: \#{result.inspect}" unless result == "module"
|
|
188
|
+
|
|
189
|
+
puts "ok"
|
|
190
|
+
RUBY
|
|
191
|
+
module_root,
|
|
192
|
+
root_constant_file
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def test_invoke_does_not_share_constants_owned_by_a_registered_root
|
|
198
|
+
Dir.mktmpdir("torikago-root-access") do |application_root|
|
|
199
|
+
foo_root = File.join(application_root, "packs/foo")
|
|
200
|
+
bar_root = File.join(application_root, "components/bar")
|
|
201
|
+
package_api_dir = File.join(foo_root, "app/package_api/foo")
|
|
202
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
203
|
+
FileUtils.mkdir_p(bar_root)
|
|
204
|
+
|
|
205
|
+
registered_constant_file = File.join(bar_root, "bar_internal.rb")
|
|
206
|
+
File.write(registered_constant_file, "class BarInternal; end\n")
|
|
207
|
+
File.write(
|
|
208
|
+
File.join(package_api_dir, "forbidden_root_query.rb"),
|
|
209
|
+
<<~RUBY
|
|
210
|
+
class Foo::ForbiddenRootQuery
|
|
211
|
+
def call
|
|
212
|
+
::BarInternal
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
RUBY
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert_ruby_box_child_process(
|
|
219
|
+
<<~RUBY,
|
|
220
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
221
|
+
foo_root = ARGV.fetch(1)
|
|
222
|
+
bar_root = ARGV.fetch(2)
|
|
223
|
+
registered_constant_file = ARGV.fetch(3)
|
|
224
|
+
|
|
225
|
+
require "torikago"
|
|
226
|
+
load registered_constant_file
|
|
227
|
+
|
|
228
|
+
container = Torikago::EngineContainer.new(
|
|
229
|
+
name: :foo,
|
|
230
|
+
module_root: foo_root,
|
|
231
|
+
registered_roots: [foo_root, bar_root]
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
begin
|
|
235
|
+
container.invoke("Foo::ForbiddenRootQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
236
|
+
raise "registered constant was unexpectedly shared"
|
|
237
|
+
rescue NameError => error
|
|
238
|
+
raise unless error.message.include?("BarInternal")
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
puts "ok"
|
|
242
|
+
RUBY
|
|
243
|
+
foo_root,
|
|
244
|
+
bar_root,
|
|
245
|
+
registered_constant_file
|
|
246
|
+
)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def test_invoke_does_not_resolve_a_qualified_module_constant_from_root
|
|
251
|
+
Dir.mktmpdir("torikago-root-access") do |application_root|
|
|
252
|
+
module_root = File.join(application_root, "modules/foo")
|
|
253
|
+
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
254
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
255
|
+
|
|
256
|
+
root_constant_file = File.join(application_root, "order.rb")
|
|
257
|
+
File.write(root_constant_file, "class Order; end\n")
|
|
258
|
+
File.write(
|
|
259
|
+
File.join(package_api_dir, "qualified_constant_query.rb"),
|
|
260
|
+
<<~RUBY
|
|
261
|
+
class Foo::QualifiedConstantQuery
|
|
262
|
+
def call
|
|
263
|
+
Foo::Order
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
RUBY
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
assert_ruby_box_child_process(
|
|
270
|
+
<<~RUBY,
|
|
271
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
272
|
+
module_root = ARGV.fetch(1)
|
|
273
|
+
root_constant_file = ARGV.fetch(2)
|
|
274
|
+
|
|
275
|
+
require "torikago"
|
|
276
|
+
load root_constant_file
|
|
277
|
+
|
|
278
|
+
container = Torikago::EngineContainer.new(
|
|
279
|
+
name: :foo,
|
|
280
|
+
module_root: module_root,
|
|
281
|
+
registered_roots: [module_root]
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
begin
|
|
285
|
+
container.invoke("Foo::QualifiedConstantQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
286
|
+
raise "Foo::Order unexpectedly resolved to ::Order"
|
|
287
|
+
rescue NameError => error
|
|
288
|
+
raise unless error.message.include?("Foo::Order")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
puts "ok"
|
|
292
|
+
RUBY
|
|
293
|
+
module_root,
|
|
294
|
+
root_constant_file
|
|
295
|
+
)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def test_invoke_does_not_share_a_root_namespace_with_a_registered_descendant
|
|
300
|
+
Dir.mktmpdir("torikago-root-access") do |application_root|
|
|
301
|
+
foo_root = File.join(application_root, "modules/foo")
|
|
302
|
+
bar_root = File.join(application_root, "modules/bar")
|
|
303
|
+
package_api_dir = File.join(foo_root, "app/package_api/foo")
|
|
304
|
+
FileUtils.mkdir_p(package_api_dir)
|
|
305
|
+
FileUtils.mkdir_p(bar_root)
|
|
306
|
+
|
|
307
|
+
root_namespace_file = File.join(application_root, "shared_namespace.rb")
|
|
308
|
+
registered_constant_file = File.join(bar_root, "bar_internal.rb")
|
|
309
|
+
File.write(root_namespace_file, "module SharedNamespace; end\n")
|
|
310
|
+
File.write(registered_constant_file, "class SharedNamespace::BarInternal; end\n")
|
|
311
|
+
File.write(
|
|
312
|
+
File.join(package_api_dir, "shared_namespace_query.rb"),
|
|
313
|
+
<<~RUBY
|
|
314
|
+
class Foo::SharedNamespaceQuery
|
|
315
|
+
def call
|
|
316
|
+
::SharedNamespace::BarInternal
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
RUBY
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
assert_ruby_box_child_process(
|
|
323
|
+
<<~RUBY,
|
|
324
|
+
$LOAD_PATH.unshift(ARGV.fetch(0))
|
|
325
|
+
foo_root = ARGV.fetch(1)
|
|
326
|
+
bar_root = ARGV.fetch(2)
|
|
327
|
+
root_namespace_file = ARGV.fetch(3)
|
|
328
|
+
registered_constant_file = ARGV.fetch(4)
|
|
329
|
+
|
|
330
|
+
require "torikago"
|
|
331
|
+
load root_namespace_file
|
|
332
|
+
load registered_constant_file
|
|
333
|
+
|
|
334
|
+
container = Torikago::EngineContainer.new(
|
|
335
|
+
name: :foo,
|
|
336
|
+
module_root: foo_root,
|
|
337
|
+
registered_roots: [foo_root, bar_root]
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
begin
|
|
341
|
+
container.invoke("Foo::SharedNamespaceQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
342
|
+
raise "registered descendant was unexpectedly shared"
|
|
343
|
+
rescue NameError => error
|
|
344
|
+
raise unless error.message.include?("SharedNamespace")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
puts "ok"
|
|
348
|
+
RUBY
|
|
349
|
+
foo_root,
|
|
350
|
+
bar_root,
|
|
351
|
+
root_namespace_file,
|
|
352
|
+
registered_constant_file
|
|
353
|
+
)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def test_invoke_does_not_strip_bundler_env_during_public_api_execution
|
|
358
|
+
with_module_root do |module_root|
|
|
359
|
+
File.write(
|
|
360
|
+
File.join(module_root, "app/package_api/foo/env_query.rb"),
|
|
361
|
+
<<~RUBY
|
|
362
|
+
class Foo::EnvQuery
|
|
363
|
+
def call
|
|
364
|
+
[ENV["RUBYOPT"], ENV["BUNDLER_SETUP"]]
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
RUBY
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
old_rubyopt = ENV["RUBYOPT"]
|
|
371
|
+
old_bundler_setup = ENV["BUNDLER_SETUP"]
|
|
372
|
+
ENV["RUBYOPT"] = "-rbundler/setup -w"
|
|
373
|
+
ENV["BUNDLER_SETUP"] = "true"
|
|
374
|
+
|
|
375
|
+
container = Torikago::EngineContainer.new(
|
|
376
|
+
name: :foo,
|
|
377
|
+
module_root: module_root,
|
|
378
|
+
box_factory: -> { FakeBox.new }
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
assert_equal ["-rbundler/setup -w", "true"], container.invoke("Foo::EnvQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
382
|
+
ensure
|
|
383
|
+
restore_env("RUBYOPT", old_rubyopt)
|
|
384
|
+
restore_env("BUNDLER_SETUP", old_bundler_setup)
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def test_bundler_env_stripping_is_serialized_across_containers
|
|
389
|
+
first_container = Torikago::EngineContainer.new(name: :foo, module_root: Dir.pwd)
|
|
390
|
+
second_container = Torikago::EngineContainer.new(name: :bar, module_root: Dir.pwd)
|
|
391
|
+
first_entered = Queue.new
|
|
392
|
+
release_first = Queue.new
|
|
393
|
+
second_ready = Queue.new
|
|
394
|
+
second_entered = Queue.new
|
|
395
|
+
|
|
396
|
+
old_rubyopt = ENV["RUBYOPT"]
|
|
397
|
+
old_bundler_setup = ENV["BUNDLER_SETUP"]
|
|
398
|
+
ENV["RUBYOPT"] = "-rbundler/setup"
|
|
399
|
+
ENV["BUNDLER_SETUP"] = "true"
|
|
400
|
+
|
|
401
|
+
first_thread = Thread.new do
|
|
402
|
+
first_container.send(:without_bundler_setup_env) do
|
|
403
|
+
first_entered << true
|
|
404
|
+
release_first.pop
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
first_entered.pop
|
|
408
|
+
|
|
409
|
+
second_thread = Thread.new do
|
|
410
|
+
second_ready << true
|
|
411
|
+
second_container.send(:without_bundler_setup_env) do
|
|
412
|
+
second_entered << true
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
second_ready.pop
|
|
416
|
+
|
|
417
|
+
sleep 0.05
|
|
418
|
+
assert_predicate second_entered, :empty?
|
|
419
|
+
|
|
420
|
+
release_first << true
|
|
421
|
+
first_thread.join
|
|
422
|
+
second_thread.join
|
|
423
|
+
|
|
424
|
+
refute_predicate second_entered, :empty?
|
|
425
|
+
ensure
|
|
426
|
+
first_thread&.kill if first_thread&.alive?
|
|
427
|
+
second_thread&.kill if second_thread&.alive?
|
|
428
|
+
restore_env("RUBYOPT", old_rubyopt)
|
|
429
|
+
restore_env("BUNDLER_SETUP", old_bundler_setup)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def test_invoke_uses_a_configured_entrypoint_directory_when_present
|
|
49
433
|
with_custom_entrypoint_module_root do |module_root|
|
|
50
434
|
container = Torikago::EngineContainer.new(
|
|
51
435
|
name: :foo,
|
|
@@ -53,13 +437,13 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
53
437
|
entrypoint: "components/public_api"
|
|
54
438
|
)
|
|
55
439
|
|
|
56
|
-
result = container.
|
|
440
|
+
result = container.invoke("Foo::CustomEntryPointQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
57
441
|
|
|
58
442
|
assert_equal "custom entrypoint", result
|
|
59
443
|
end
|
|
60
444
|
end
|
|
61
445
|
|
|
62
|
-
def
|
|
446
|
+
def test_invoke_does_not_load_parent_files_when_configured_entrypoint_directory_is_missing
|
|
63
447
|
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
64
448
|
FileUtils.mkdir_p(File.join(module_root, "app/controllers/foo"))
|
|
65
449
|
File.write(
|
|
@@ -76,14 +460,213 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
76
460
|
)
|
|
77
461
|
|
|
78
462
|
error = assert_raises(NameError) do
|
|
79
|
-
container.
|
|
463
|
+
container.invoke("Foo::MissingQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
80
464
|
end
|
|
81
465
|
|
|
82
466
|
assert_match(/MissingQuery/, error.message)
|
|
83
467
|
end
|
|
84
468
|
end
|
|
85
469
|
|
|
86
|
-
def
|
|
470
|
+
def test_invoke_loads_plain_lib_entrypoint_by_default
|
|
471
|
+
with_module_root do |module_root|
|
|
472
|
+
lib_dir = File.join(module_root, "lib")
|
|
473
|
+
FileUtils.mkdir_p(lib_dir)
|
|
474
|
+
File.write(
|
|
475
|
+
File.join(lib_dir, "foo.rb"),
|
|
476
|
+
<<~RUBY
|
|
477
|
+
module Foo
|
|
478
|
+
PLAIN_LIB_ENTRYPOINT = "loaded"
|
|
479
|
+
end
|
|
480
|
+
RUBY
|
|
481
|
+
)
|
|
482
|
+
File.write(
|
|
483
|
+
File.join(module_root, "app/package_api/foo/plain_lib_entrypoint_query.rb"),
|
|
484
|
+
<<~RUBY
|
|
485
|
+
class Foo::PlainLibEntrypointQuery
|
|
486
|
+
def call
|
|
487
|
+
Foo::PLAIN_LIB_ENTRYPOINT
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
RUBY
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
494
|
+
|
|
495
|
+
assert_equal "loaded", container.invoke("Foo::PlainLibEntrypointQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def test_invoke_loads_the_module_lib_entrypoint_when_rails_engine_is_enabled
|
|
500
|
+
with_module_root do |module_root|
|
|
501
|
+
lib_dir = File.join(module_root, "lib")
|
|
502
|
+
FileUtils.mkdir_p(File.join(lib_dir, "foo"))
|
|
503
|
+
File.write(
|
|
504
|
+
File.join(lib_dir, "foo.rb"),
|
|
505
|
+
<<~RUBY
|
|
506
|
+
module Foo
|
|
507
|
+
RAILS_ENGINE_ENTRYPOINT = "loaded in module runtime"
|
|
508
|
+
end
|
|
509
|
+
RUBY
|
|
510
|
+
)
|
|
511
|
+
File.write(
|
|
512
|
+
File.join(lib_dir, "foo/support.rb"),
|
|
513
|
+
<<~RUBY
|
|
514
|
+
module Foo
|
|
515
|
+
SUPPORT_VALUE = "ordinary lib file loaded"
|
|
516
|
+
end
|
|
517
|
+
RUBY
|
|
518
|
+
)
|
|
519
|
+
File.write(
|
|
520
|
+
File.join(module_root, "app/package_api/foo/rails_engine_support_query.rb"),
|
|
521
|
+
<<~RUBY
|
|
522
|
+
class Foo::RailsEngineSupportQuery
|
|
523
|
+
def call
|
|
524
|
+
[Foo::RAILS_ENGINE_ENTRYPOINT, Foo::SUPPORT_VALUE]
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
RUBY
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
container = Torikago::EngineContainer.new(
|
|
531
|
+
name: :foo,
|
|
532
|
+
module_root: module_root,
|
|
533
|
+
rails_engine: true
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
assert_equal(
|
|
537
|
+
["loaded in module runtime", "ordinary lib file loaded"],
|
|
538
|
+
container.invoke("Foo::RailsEngineSupportQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
539
|
+
)
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def test_call_loads_and_dispatches_to_a_top_level_rails_runtime_controller
|
|
544
|
+
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
545
|
+
FileUtils.mkdir_p(File.join(module_root, "lib"))
|
|
546
|
+
FileUtils.mkdir_p(File.join(module_root, "app/controllers"))
|
|
547
|
+
FileUtils.mkdir_p(File.join(module_root, "config"))
|
|
548
|
+
File.write(
|
|
549
|
+
File.join(module_root, "lib/foo.rb"),
|
|
550
|
+
<<~RUBY
|
|
551
|
+
module Foo
|
|
552
|
+
class Routes
|
|
553
|
+
def recognize_path(path, method:)
|
|
554
|
+
raise "unexpected path" unless path == "/widgets/7"
|
|
555
|
+
raise "unexpected method" unless method == :get
|
|
556
|
+
|
|
557
|
+
{ "controller" => "widgets", "action" => "show", "id" => "7" }
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
class Engine
|
|
562
|
+
def self.routes
|
|
563
|
+
@routes ||= Routes.new
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
RUBY
|
|
568
|
+
)
|
|
569
|
+
File.write(
|
|
570
|
+
File.join(module_root, "app/controllers/widgets_controller.rb"),
|
|
571
|
+
<<~RUBY
|
|
572
|
+
class WidgetsController
|
|
573
|
+
def self.action(action_name)
|
|
574
|
+
lambda do |env|
|
|
575
|
+
params = env.fetch("action_dispatch.request.path_parameters")
|
|
576
|
+
[
|
|
577
|
+
200,
|
|
578
|
+
{ "content-type" => "text/plain" },
|
|
579
|
+
["\#{action_name}:\#{params.fetch(:id)}:\#{Torikago::CurrentExecution.current_box}"]
|
|
580
|
+
]
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
RUBY
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
container = Torikago::EngineContainer.new(
|
|
588
|
+
name: :foo,
|
|
589
|
+
module_root: module_root,
|
|
590
|
+
rails_engine: true,
|
|
591
|
+
box_factory: -> { FakeBox.new }
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
response = container.call("PATH_INFO" => "/widgets/7", "REQUEST_METHOD" => "GET")
|
|
595
|
+
|
|
596
|
+
assert_equal [200, { "content-type" => "text/plain" }, ["show:7:foo"]], response
|
|
597
|
+
assert_nil Torikago::CurrentExecution.current_box
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def test_dispatch_controller_loads_a_top_level_controller_without_a_rails_engine
|
|
602
|
+
with_module_root do |module_root|
|
|
603
|
+
controller_dir = File.join(module_root, "app/controllers")
|
|
604
|
+
FileUtils.mkdir_p(controller_dir)
|
|
605
|
+
File.write(
|
|
606
|
+
File.join(controller_dir, "widgets_controller.rb"),
|
|
607
|
+
<<~RUBY
|
|
608
|
+
class WidgetsController
|
|
609
|
+
def self.action(action_name)
|
|
610
|
+
lambda do |env|
|
|
611
|
+
params = env.fetch("action_dispatch.request.path_parameters")
|
|
612
|
+
[
|
|
613
|
+
200,
|
|
614
|
+
{ "content-type" => "text/plain" },
|
|
615
|
+
["\#{action_name}:\#{params.fetch(:id)}:\#{Torikago::CurrentExecution.current_box}"]
|
|
616
|
+
]
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
RUBY
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
container = Torikago::EngineContainer.new(
|
|
624
|
+
name: :foo,
|
|
625
|
+
module_root: module_root,
|
|
626
|
+
box_factory: -> { FakeBox.new }
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
# A prior Package API invocation must not prevent the later controller
|
|
630
|
+
# runtime from loading.
|
|
631
|
+
assert_equal(
|
|
632
|
+
["coffee-beans", "drip-bag"],
|
|
633
|
+
container.invoke(
|
|
634
|
+
"Foo::ListProductsQuery",
|
|
635
|
+
:call,
|
|
636
|
+
constructor_args: [],
|
|
637
|
+
constructor_kwargs: {},
|
|
638
|
+
method_args: [],
|
|
639
|
+
method_kwargs: {}
|
|
640
|
+
)
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
response = container.dispatch_controller(
|
|
644
|
+
{
|
|
645
|
+
"PATH_INFO" => "/widgets/7",
|
|
646
|
+
"action_dispatch.request.path_parameters" => { id: "7" }
|
|
647
|
+
},
|
|
648
|
+
controller_name: "WidgetsController",
|
|
649
|
+
action_name: :show
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
assert_equal [200, { "content-type" => "text/plain" }, ["show:7:foo"]], response
|
|
653
|
+
assert_nil Torikago::CurrentExecution.current_box
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def test_call_rejects_modules_without_a_rails_engine
|
|
658
|
+
with_module_root do |module_root|
|
|
659
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
660
|
+
|
|
661
|
+
error = assert_raises(ArgumentError) do
|
|
662
|
+
container.call("PATH_INFO" => "/", "REQUEST_METHOD" => "GET")
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
assert_match(/rails_engine: true/, error.message)
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def test_invoke_prepends_explicit_gemfile_require_paths_before_loading_runtime
|
|
87
670
|
with_module_root do |module_root|
|
|
88
671
|
dependency_lib = File.join(module_root, "vendor/example-gem-1.2.3/lib")
|
|
89
672
|
FileUtils.mkdir_p(dependency_lib)
|
|
@@ -131,7 +714,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
131
714
|
end
|
|
132
715
|
)
|
|
133
716
|
|
|
134
|
-
raise "unexpected result" unless container.
|
|
717
|
+
raise "unexpected result" unless container.invoke("Foo::DependencyVersionQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {}) == "module-local"
|
|
135
718
|
|
|
136
719
|
puts "ok"
|
|
137
720
|
RUBY
|
|
@@ -141,7 +724,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
141
724
|
end
|
|
142
725
|
end
|
|
143
726
|
|
|
144
|
-
def
|
|
727
|
+
def test_invoke_resolves_path_gem_require_paths_inside_ruby_box
|
|
145
728
|
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
146
729
|
dependency_lib = File.join(module_root, "vendor/example-gem-1.0.0/lib")
|
|
147
730
|
FileUtils.mkdir_p(dependency_lib)
|
|
@@ -203,7 +786,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
203
786
|
gemfile: "Gemfile"
|
|
204
787
|
)
|
|
205
788
|
|
|
206
|
-
raise "unexpected result" unless container.
|
|
789
|
+
raise "unexpected result" unless container.invoke("Foo::DependencyVersionQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {}) == "module-local"
|
|
207
790
|
|
|
208
791
|
puts "ok"
|
|
209
792
|
RUBY
|
|
@@ -212,7 +795,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
212
795
|
end
|
|
213
796
|
end
|
|
214
797
|
|
|
215
|
-
def
|
|
798
|
+
def test_invoke_resolves_path_gem_require_paths_from_the_module_gemfile
|
|
216
799
|
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
217
800
|
dependency_root = File.join(module_root, "vendor/versioned_formatter")
|
|
218
801
|
dependency_lib = File.join(dependency_root, "lib")
|
|
@@ -275,7 +858,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
275
858
|
gemfile: "Gemfile"
|
|
276
859
|
)
|
|
277
860
|
|
|
278
|
-
raise "unexpected result" unless container.
|
|
861
|
+
raise "unexpected result" unless container.invoke("Foo::GemfileDependencyQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {}) == "1.0.0"
|
|
279
862
|
|
|
280
863
|
puts "ok"
|
|
281
864
|
RUBY
|
|
@@ -284,7 +867,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
284
867
|
end
|
|
285
868
|
end
|
|
286
869
|
|
|
287
|
-
def
|
|
870
|
+
def test_invoke_resolves_exact_installed_gem_require_paths_after_a_different_version_is_active
|
|
288
871
|
skip("requires rake 13.3.1 and 13.4.2") unless installed_gem?("rake", "= 13.3.1") && installed_gem?("rake", "= 13.4.2")
|
|
289
872
|
|
|
290
873
|
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
@@ -314,7 +897,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
314
897
|
end
|
|
315
898
|
end
|
|
316
899
|
|
|
317
|
-
def
|
|
900
|
+
def test_invoke_reports_missing_installed_gemfile_dependencies_clearly
|
|
318
901
|
with_module_root do |module_root|
|
|
319
902
|
File.write(
|
|
320
903
|
File.join(module_root, "Gemfile"),
|
|
@@ -332,7 +915,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
332
915
|
)
|
|
333
916
|
|
|
334
917
|
error = assert_raises(Torikago::GemfileOverrideError) do
|
|
335
|
-
container.
|
|
918
|
+
container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
336
919
|
end
|
|
337
920
|
|
|
338
921
|
assert_match(/example-gem/, error.message)
|
|
@@ -340,7 +923,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
340
923
|
end
|
|
341
924
|
end
|
|
342
925
|
|
|
343
|
-
def
|
|
926
|
+
def test_invoke_loads_configured_setup_before_public_api
|
|
344
927
|
with_module_root do |module_root|
|
|
345
928
|
setup_dir = File.join(module_root, "config")
|
|
346
929
|
FileUtils.mkdir_p(setup_dir)
|
|
@@ -370,11 +953,11 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
370
953
|
setup: "config/box_setup.rb"
|
|
371
954
|
)
|
|
372
955
|
|
|
373
|
-
assert_equal "patched", container.
|
|
956
|
+
assert_equal "patched", container.invoke("Foo::SetupAwareQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
374
957
|
end
|
|
375
958
|
end
|
|
376
959
|
|
|
377
|
-
def
|
|
960
|
+
def test_invoke_loads_setup_only_once
|
|
378
961
|
with_module_root do |module_root|
|
|
379
962
|
setup_dir = File.join(module_root, "config")
|
|
380
963
|
FileUtils.mkdir_p(setup_dir)
|
|
@@ -404,12 +987,12 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
404
987
|
setup: "config/box_setup.rb"
|
|
405
988
|
)
|
|
406
989
|
|
|
407
|
-
assert_equal 1, container.
|
|
408
|
-
assert_equal 1, container.
|
|
990
|
+
assert_equal 1, container.invoke("Foo::SetupCountQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
991
|
+
assert_equal 1, container.invoke("Foo::SetupCountQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
409
992
|
end
|
|
410
993
|
end
|
|
411
994
|
|
|
412
|
-
def
|
|
995
|
+
def test_invoke_raises_load_error_when_setup_is_missing
|
|
413
996
|
with_module_root do |module_root|
|
|
414
997
|
container = Torikago::EngineContainer.new(
|
|
415
998
|
name: :foo,
|
|
@@ -418,7 +1001,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
418
1001
|
)
|
|
419
1002
|
|
|
420
1003
|
error = assert_raises(LoadError) do
|
|
421
|
-
container.
|
|
1004
|
+
container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
422
1005
|
end
|
|
423
1006
|
|
|
424
1007
|
assert_match(/setup not found/, error.message)
|
|
@@ -426,7 +1009,7 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
426
1009
|
end
|
|
427
1010
|
end
|
|
428
1011
|
|
|
429
|
-
def
|
|
1012
|
+
def test_invoke_loads_plain_gateway_models_without_eager_loading_rails_models
|
|
430
1013
|
with_module_root do |module_root|
|
|
431
1014
|
model_dir = File.join(module_root, "app/models/foo")
|
|
432
1015
|
FileUtils.mkdir_p(model_dir)
|
|
@@ -462,12 +1045,218 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
462
1045
|
|
|
463
1046
|
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
464
1047
|
|
|
465
|
-
assert_equal ["order"], container.
|
|
1048
|
+
assert_equal ["order"], container.invoke("Foo::ListOrdersQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
1049
|
+
end
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
def test_invoke_keeps_constructor_and_method_arguments_separate
|
|
1053
|
+
with_module_root do |module_root|
|
|
1054
|
+
File.write(
|
|
1055
|
+
File.join(module_root, "app/package_api/foo/product_query.rb"),
|
|
1056
|
+
<<~RUBY
|
|
1057
|
+
class Foo::ProductQuery
|
|
1058
|
+
def initialize(prefix, page:)
|
|
1059
|
+
@prefix = prefix
|
|
1060
|
+
@page = page
|
|
1061
|
+
end
|
|
1062
|
+
|
|
1063
|
+
def execute!(suffix, force:)
|
|
1064
|
+
[@prefix, @page, suffix, force, Torikago::CurrentExecution.current_box]
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
RUBY
|
|
1068
|
+
)
|
|
1069
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
1070
|
+
|
|
1071
|
+
result = container.invoke(
|
|
1072
|
+
"Foo::ProductQuery",
|
|
1073
|
+
:execute!,
|
|
1074
|
+
constructor_args: ["products"],
|
|
1075
|
+
constructor_kwargs: { page: 2 },
|
|
1076
|
+
method_args: ["refresh"],
|
|
1077
|
+
method_kwargs: { force: true }
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
assert_equal ["products", 2, "refresh", true, :foo], result
|
|
1081
|
+
assert_nil Torikago::CurrentExecution.current_box
|
|
1082
|
+
end
|
|
1083
|
+
end
|
|
1084
|
+
|
|
1085
|
+
def test_invoke_rejects_private_methods
|
|
1086
|
+
with_module_root do |module_root|
|
|
1087
|
+
File.write(
|
|
1088
|
+
File.join(module_root, "app/package_api/foo/private_query.rb"),
|
|
1089
|
+
<<~RUBY
|
|
1090
|
+
class Foo::PrivateQuery
|
|
1091
|
+
private
|
|
1092
|
+
|
|
1093
|
+
def execute!
|
|
1094
|
+
"should not run"
|
|
1095
|
+
end
|
|
1096
|
+
end
|
|
1097
|
+
RUBY
|
|
1098
|
+
)
|
|
1099
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
1100
|
+
|
|
1101
|
+
assert_raises(NoMethodError) do
|
|
1102
|
+
container.invoke("Foo::PrivateQuery", :execute!, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
1103
|
+
end
|
|
1104
|
+
end
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
def test_invoke_preserves_constructor_and_method_exceptions
|
|
1108
|
+
with_module_root do |module_root|
|
|
1109
|
+
File.write(
|
|
1110
|
+
File.join(module_root, "app/package_api/foo/failing_query.rb"),
|
|
1111
|
+
<<~RUBY
|
|
1112
|
+
class Foo::FailingQuery
|
|
1113
|
+
def initialize(fail_constructor: false)
|
|
1114
|
+
raise "constructor failed" if fail_constructor
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
def execute!
|
|
1118
|
+
raise "method failed"
|
|
1119
|
+
end
|
|
1120
|
+
end
|
|
1121
|
+
RUBY
|
|
1122
|
+
)
|
|
1123
|
+
constructor_container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
1124
|
+
method_container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
1125
|
+
|
|
1126
|
+
constructor_error = assert_raises(RuntimeError) do
|
|
1127
|
+
constructor_container.invoke(
|
|
1128
|
+
"Foo::FailingQuery",
|
|
1129
|
+
:execute!,
|
|
1130
|
+
constructor_args: [],
|
|
1131
|
+
constructor_kwargs: { fail_constructor: true },
|
|
1132
|
+
method_args: [],
|
|
1133
|
+
method_kwargs: {}
|
|
1134
|
+
)
|
|
1135
|
+
end
|
|
1136
|
+
method_error = assert_raises(RuntimeError) do
|
|
1137
|
+
method_container.invoke(
|
|
1138
|
+
"Foo::FailingQuery",
|
|
1139
|
+
:execute!,
|
|
1140
|
+
constructor_args: [],
|
|
1141
|
+
constructor_kwargs: {},
|
|
1142
|
+
method_args: [],
|
|
1143
|
+
method_kwargs: {}
|
|
1144
|
+
)
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
assert_equal "constructor failed", constructor_error.message
|
|
1148
|
+
assert_equal "method failed", method_error.message
|
|
1149
|
+
end
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
def test_invoke_restores_current_execution_after_nested_execution
|
|
1153
|
+
with_module_root do |module_root|
|
|
1154
|
+
File.write(
|
|
1155
|
+
File.join(module_root, "app/package_api/foo/nested_query.rb"),
|
|
1156
|
+
<<~RUBY
|
|
1157
|
+
class Foo::NestedQuery
|
|
1158
|
+
def execute!
|
|
1159
|
+
Torikago::CurrentExecution.with_box(:bar) { :nested }
|
|
1160
|
+
Torikago::CurrentExecution.current_box
|
|
1161
|
+
end
|
|
1162
|
+
end
|
|
1163
|
+
RUBY
|
|
1164
|
+
)
|
|
1165
|
+
container = Torikago::EngineContainer.new(name: :foo, module_root: module_root)
|
|
1166
|
+
|
|
1167
|
+
result = Torikago::CurrentExecution.with_box(:caller) do
|
|
1168
|
+
current = container.invoke("Foo::NestedQuery", :execute!, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
1169
|
+
[current, Torikago::CurrentExecution.current_box]
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
assert_equal [:foo, :caller], result
|
|
1173
|
+
assert_nil Torikago::CurrentExecution.current_box
|
|
1174
|
+
end
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1177
|
+
def test_invoke_fails_closed_when_ruby_box_creation_fails
|
|
1178
|
+
with_module_root do |module_root|
|
|
1179
|
+
old_ruby_box = ENV["RUBY_BOX"]
|
|
1180
|
+
ENV["RUBY_BOX"] = "1"
|
|
1181
|
+
container = Torikago::EngineContainer.new(
|
|
1182
|
+
name: :foo,
|
|
1183
|
+
module_root: module_root,
|
|
1184
|
+
box_factory: -> { raise "box creation failed" }
|
|
1185
|
+
)
|
|
1186
|
+
|
|
1187
|
+
error = assert_raises(Torikago::BoxUnavailableError) do
|
|
1188
|
+
container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
assert_equal "Ruby::Box is unavailable for module foo: box creation failed", error.message
|
|
1192
|
+
assert_instance_of RuntimeError, error.cause
|
|
1193
|
+
refute Object.const_defined?(:Foo, false)
|
|
1194
|
+
ensure
|
|
1195
|
+
restore_env("RUBY_BOX", old_ruby_box)
|
|
1196
|
+
end
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
def test_invoke_wraps_box_runtime_preparation_failures
|
|
1200
|
+
with_module_root do |module_root|
|
|
1201
|
+
old_ruby_box = ENV["RUBY_BOX"]
|
|
1202
|
+
ENV["RUBY_BOX"] = "1"
|
|
1203
|
+
failing_box = Object.new
|
|
1204
|
+
failing_box.define_singleton_method(:load_path) { raise "load path failed" }
|
|
1205
|
+
container = Torikago::EngineContainer.new(
|
|
1206
|
+
name: :foo,
|
|
1207
|
+
module_root: module_root,
|
|
1208
|
+
box_factory: -> { failing_box }
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
error = assert_raises(Torikago::BoxUnavailableError) do
|
|
1212
|
+
container.invoke("Foo::ListProductsQuery", :call, constructor_args: [], constructor_kwargs: {}, method_args: [], method_kwargs: {})
|
|
1213
|
+
end
|
|
1214
|
+
|
|
1215
|
+
assert_match(/load path failed/, error.message)
|
|
1216
|
+
assert_instance_of RuntimeError, error.cause
|
|
1217
|
+
refute Object.const_defined?(:Foo, false)
|
|
1218
|
+
ensure
|
|
1219
|
+
restore_env("RUBY_BOX", old_ruby_box)
|
|
466
1220
|
end
|
|
467
1221
|
end
|
|
468
1222
|
|
|
469
1223
|
private
|
|
470
1224
|
|
|
1225
|
+
class FakeBox
|
|
1226
|
+
attr_reader :load_path
|
|
1227
|
+
|
|
1228
|
+
def initialize
|
|
1229
|
+
@load_path = []
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
def require(_name)
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
def load(path)
|
|
1236
|
+
Kernel.load(path)
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
def const_defined?(name, inherit = true)
|
|
1240
|
+
Object.const_defined?(name, inherit)
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
def const_set(name, value)
|
|
1244
|
+
Object.const_set(name, value)
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
def const_get(name)
|
|
1248
|
+
Object.const_get(name)
|
|
1249
|
+
end
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
def restore_env(key, value)
|
|
1253
|
+
if value
|
|
1254
|
+
ENV[key] = value
|
|
1255
|
+
else
|
|
1256
|
+
ENV.delete(key)
|
|
1257
|
+
end
|
|
1258
|
+
end
|
|
1259
|
+
|
|
471
1260
|
def with_module_root
|
|
472
1261
|
Dir.mktmpdir("torikago-engine-container") do |module_root|
|
|
473
1262
|
package_api_dir = File.join(module_root, "app/package_api/foo")
|
|
@@ -519,7 +1308,12 @@ class TorikagoEngineContainerTest < Minitest::Test
|
|
|
519
1308
|
|
|
520
1309
|
def assert_ruby_box_child_process(script, *args)
|
|
521
1310
|
stdout, stderr, status = Open3.capture3(
|
|
522
|
-
{
|
|
1311
|
+
{
|
|
1312
|
+
"RUBY_BOX" => "1",
|
|
1313
|
+
"RUBYOPT" => nil,
|
|
1314
|
+
"BUNDLER_SETUP" => nil,
|
|
1315
|
+
"BUNDLE_GEMFILE" => nil
|
|
1316
|
+
},
|
|
523
1317
|
RbConfig.ruby,
|
|
524
1318
|
"-e",
|
|
525
1319
|
script,
|