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
data/test/torikago/checker.rb
CHANGED
|
@@ -3,152 +3,178 @@ require "fileutils"
|
|
|
3
3
|
require "tmpdir"
|
|
4
4
|
|
|
5
5
|
class TorikagoCheckerTest < Minitest::Test
|
|
6
|
-
def
|
|
6
|
+
def test_check_detects_one_line_gateway_invoke
|
|
7
7
|
with_project do |root, configuration|
|
|
8
|
-
|
|
9
|
-
File.write(
|
|
10
|
-
File.join(root, "modules/foo/service.rb"),
|
|
11
|
-
<<~RUBY
|
|
12
|
-
class FooService
|
|
13
|
-
def call
|
|
14
|
-
Torikago::Gateway.call("Bar::SubmitOrderCommand")
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
RUBY
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
checker = Torikago::Checker.new(
|
|
21
|
-
configuration: configuration,
|
|
22
|
-
source_roots: [File.join(root, "modules")]
|
|
23
|
-
)
|
|
8
|
+
write_caller(root, 'Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :execute!)')
|
|
24
9
|
|
|
25
|
-
result =
|
|
10
|
+
result = check(root, configuration)
|
|
26
11
|
|
|
27
|
-
assert result.ok
|
|
28
|
-
assert_equal [], result.errors
|
|
29
|
-
assert_equal 2, result.scanned_file_count
|
|
12
|
+
assert result.ok?, result.errors.join("\n")
|
|
30
13
|
assert_equal 1, result.gateway_call_count
|
|
31
|
-
assert_equal
|
|
14
|
+
assert_equal 0, result.dynamic_gateway_call_count
|
|
32
15
|
end
|
|
33
16
|
end
|
|
34
17
|
|
|
35
|
-
def
|
|
18
|
+
def test_check_detects_multiline_build_and_invoke
|
|
36
19
|
with_project do |root, configuration|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
File.join(root, "modules/foo/service.rb"),
|
|
20
|
+
write_caller(
|
|
21
|
+
root,
|
|
40
22
|
<<~RUBY
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
end
|
|
23
|
+
Torikago::Gateway
|
|
24
|
+
.build("Bar::SubmitOrderCommand", account_id: 1)
|
|
25
|
+
.invoke(:execute!, force: true)
|
|
46
26
|
RUBY
|
|
47
27
|
)
|
|
48
28
|
|
|
49
|
-
|
|
50
|
-
File.join(root, "modules/bar/package_api.yml"),
|
|
51
|
-
<<~YAML
|
|
52
|
-
exports:
|
|
53
|
-
Bar::MissingCommand:
|
|
54
|
-
allowed_callers:
|
|
55
|
-
- foo
|
|
56
|
-
YAML
|
|
57
|
-
)
|
|
29
|
+
result = check(root, configuration)
|
|
58
30
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
31
|
+
assert result.ok?, result.errors.join("\n")
|
|
32
|
+
assert_equal 1, result.gateway_call_count
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_check_reports_a_method_not_declared_in_the_manifest
|
|
37
|
+
with_project do |root, configuration|
|
|
38
|
+
write_caller(root, 'Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :delete_all!)')
|
|
63
39
|
|
|
64
|
-
result =
|
|
40
|
+
result = check(root, configuration)
|
|
65
41
|
|
|
66
42
|
refute result.ok?
|
|
67
|
-
|
|
68
|
-
assert_match(/matching file/, result.errors.first)
|
|
43
|
+
assert(result.errors.any? { |error| error.include?("Bar::SubmitOrderCommand#delete_all! is not exported") })
|
|
69
44
|
end
|
|
70
45
|
end
|
|
71
46
|
|
|
72
|
-
def
|
|
73
|
-
with_project(
|
|
74
|
-
|
|
75
|
-
File.write(
|
|
76
|
-
File.join(root, "modules/foo/service.rb"),
|
|
77
|
-
<<~RUBY
|
|
78
|
-
class FooService
|
|
79
|
-
def call
|
|
80
|
-
Torikago::Gateway.call("Bar::SubmitOrderCommand")
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
RUBY
|
|
84
|
-
)
|
|
47
|
+
def test_check_reports_an_unauthorized_caller
|
|
48
|
+
with_project(allowed_callers: []) do |root, configuration|
|
|
49
|
+
write_caller(root, 'Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :execute!)')
|
|
85
50
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
51
|
+
result = check(root, configuration)
|
|
52
|
+
|
|
53
|
+
refute result.ok?
|
|
54
|
+
assert(result.errors.any? { |error| error.include?("foo is not allowed to call Bar::SubmitOrderCommand#execute!") })
|
|
55
|
+
end
|
|
56
|
+
end
|
|
90
57
|
|
|
91
|
-
|
|
58
|
+
def test_check_reports_manifest_entries_without_methods
|
|
59
|
+
with_project(methods: nil) do |root, configuration|
|
|
60
|
+
write_caller(root, 'Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :execute!)')
|
|
92
61
|
|
|
93
|
-
|
|
94
|
-
|
|
62
|
+
result = check(root, configuration)
|
|
63
|
+
|
|
64
|
+
refute result.ok?
|
|
65
|
+
assert(result.errors.any? { |error| error.include?("must declare a non-empty methods array") })
|
|
95
66
|
end
|
|
96
67
|
end
|
|
97
68
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
FileUtils.mkdir_p(File.join(foo_root, "app/models/foo"))
|
|
102
|
-
File.write(File.join(foo_root, "app/models/foo/widget.rb"), "")
|
|
103
|
-
File.write(
|
|
104
|
-
File.join(foo_root, "package_api.yml"),
|
|
105
|
-
<<~YAML
|
|
106
|
-
exports:
|
|
107
|
-
Models::Foo::Widget:
|
|
108
|
-
allowed_callers: []
|
|
109
|
-
YAML
|
|
110
|
-
)
|
|
69
|
+
def test_check_reports_manifest_entries_with_empty_methods
|
|
70
|
+
with_project(methods: []) do |root, configuration|
|
|
71
|
+
result = check(root, configuration)
|
|
111
72
|
|
|
112
|
-
|
|
113
|
-
|
|
73
|
+
refute result.ok?
|
|
74
|
+
assert(result.errors.any? { |error| error.include?("must declare a non-empty methods array") })
|
|
75
|
+
end
|
|
76
|
+
end
|
|
114
77
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
78
|
+
def test_check_reports_missing_public_api_files
|
|
79
|
+
with_project do |root, configuration|
|
|
80
|
+
FileUtils.rm_f(File.join(root, "modules/bar/app/package_api/bar/submit_order_command.rb"))
|
|
81
|
+
|
|
82
|
+
result = check(root, configuration)
|
|
83
|
+
|
|
84
|
+
refute result.ok?
|
|
85
|
+
assert(result.errors.any? { |error| error.include?("does not have a matching file") })
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_check_reports_exported_methods_missing_from_the_implementation
|
|
90
|
+
with_project(implementation_method: :call) do |root, configuration|
|
|
91
|
+
result = check(root, configuration)
|
|
92
|
+
|
|
93
|
+
refute result.ok?
|
|
94
|
+
assert(result.errors.any? { |error| error.include?("#execute! is exported but no public instance method definition was found") })
|
|
95
|
+
end
|
|
96
|
+
end
|
|
119
97
|
|
|
120
|
-
|
|
98
|
+
def test_check_does_not_treat_private_methods_as_public_implementations
|
|
99
|
+
with_project(implementation_visibility: :private) do |root, configuration|
|
|
100
|
+
result = check(root, configuration)
|
|
121
101
|
|
|
122
102
|
refute result.ok?
|
|
123
|
-
|
|
124
|
-
|
|
103
|
+
assert(result.errors.any? { |error| error.include?("no public instance method definition") })
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_check_skips_dynamic_gateway_arguments_and_counts_them_separately
|
|
108
|
+
with_project do |root, configuration|
|
|
109
|
+
write_caller(root, "Torikago::Gateway.invoke(class_name, method_name)")
|
|
110
|
+
|
|
111
|
+
result = check(root, configuration)
|
|
112
|
+
|
|
113
|
+
assert result.ok?, result.errors.join("\n")
|
|
114
|
+
assert_equal 0, result.gateway_call_count
|
|
115
|
+
assert_equal 1, result.dynamic_gateway_call_count
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_check_uses_a_configured_entrypoint_directory
|
|
120
|
+
with_project(entrypoint: "components/public_api") do |root, configuration|
|
|
121
|
+
write_caller(root, 'Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :execute!)')
|
|
122
|
+
|
|
123
|
+
result = check(root, configuration)
|
|
124
|
+
|
|
125
|
+
assert result.ok?, result.errors.join("\n")
|
|
125
126
|
end
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
private
|
|
129
130
|
|
|
130
|
-
def
|
|
131
|
+
def check(root, configuration)
|
|
132
|
+
checker = Torikago::Checker.new(
|
|
133
|
+
configuration: configuration,
|
|
134
|
+
source_roots: [File.join(root, "modules")]
|
|
135
|
+
)
|
|
136
|
+
Dir.chdir(root) { checker.call }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def write_caller(root, invocation)
|
|
140
|
+
FileUtils.mkdir_p(File.join(root, "modules/foo"))
|
|
141
|
+
File.write(
|
|
142
|
+
File.join(root, "modules/foo/service.rb"),
|
|
143
|
+
<<~RUBY
|
|
144
|
+
class FooService
|
|
145
|
+
def call
|
|
146
|
+
#{invocation}
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
RUBY
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def with_project(entrypoint: nil, methods: ["execute!"], allowed_callers: ["foo"], implementation_method: :execute!, implementation_visibility: :public)
|
|
131
154
|
Dir.mktmpdir("torikago-checker") do |root|
|
|
132
155
|
foo_root = File.join(root, "modules/foo")
|
|
133
156
|
bar_root = File.join(root, "modules/bar")
|
|
134
157
|
public_api_root = entrypoint ? File.join(bar_root, entrypoint, "bar") : File.join(bar_root, "app/package_api/bar")
|
|
158
|
+
FileUtils.mkdir_p(foo_root)
|
|
135
159
|
FileUtils.mkdir_p(public_api_root)
|
|
136
160
|
|
|
161
|
+
methods_yaml = methods.nil? ? "" : " methods:\n#{methods.map { |method| " - #{method}" }.join("\n")}\n"
|
|
137
162
|
File.write(
|
|
138
163
|
File.join(bar_root, "package_api.yml"),
|
|
139
164
|
<<~YAML
|
|
140
165
|
exports:
|
|
141
166
|
Bar::SubmitOrderCommand:
|
|
142
|
-
|
|
143
|
-
|
|
167
|
+
#{methods_yaml} allowed_callers:
|
|
168
|
+
#{allowed_callers.map { |caller| " - #{caller}" }.join("\n")}
|
|
144
169
|
YAML
|
|
145
170
|
)
|
|
146
171
|
|
|
172
|
+
visibility = implementation_visibility == :public ? "" : " #{implementation_visibility}\n\n"
|
|
147
173
|
File.write(
|
|
148
174
|
File.join(public_api_root, "submit_order_command.rb"),
|
|
149
175
|
<<~RUBY
|
|
150
176
|
class Bar::SubmitOrderCommand
|
|
151
|
-
|
|
177
|
+
#{visibility} def #{implementation_method}
|
|
152
178
|
end
|
|
153
179
|
end
|
|
154
180
|
RUBY
|
data/test/torikago/cli.rb
CHANGED
|
@@ -55,8 +55,8 @@ class TorikagoCliTest < Minitest::Test
|
|
|
55
55
|
bar_manifest = YAML.safe_load(File.read(File.join(root, "modules/bar/package_api.yml")))
|
|
56
56
|
initializer = File.read(File.join(root, "config/initializers/torikago.rb"))
|
|
57
57
|
|
|
58
|
-
assert_equal({ "allowed_callers" => [] }, foo_manifest.dig("exports", "Foo::ListProductsQuery"))
|
|
59
|
-
assert_equal({ "allowed_callers" => [] }, bar_manifest.dig("exports", "Bar::SubmitOrderCommand"))
|
|
58
|
+
assert_equal({ "methods" => [], "allowed_callers" => [] }, foo_manifest.dig("exports", "Foo::ListProductsQuery"))
|
|
59
|
+
assert_equal({ "methods" => [], "allowed_callers" => [] }, bar_manifest.dig("exports", "Bar::SubmitOrderCommand"))
|
|
60
60
|
assert_match(/root: Rails\.root\.join\("modules\/bar"\)/, initializer)
|
|
61
61
|
assert_match(/entrypoint: "components\/public_api"/, initializer)
|
|
62
62
|
assert_match(/root: Rails\.root\.join\("modules\/foo"\)/, initializer)
|
|
@@ -66,6 +66,19 @@ class TorikagoCliTest < Minitest::Test
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
def test_render_initializer_includes_setup_option
|
|
70
|
+
configuration = Torikago::Configuration.new
|
|
71
|
+
configuration.register(
|
|
72
|
+
:foo,
|
|
73
|
+
root: "modules/foo",
|
|
74
|
+
setup: "config/box_setup.rb"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
initializer = Torikago::CLI.new.send(:render_initializer, configuration)
|
|
78
|
+
|
|
79
|
+
assert_match(/setup: "config\/box_setup\.rb"/, initializer)
|
|
80
|
+
end
|
|
81
|
+
|
|
69
82
|
def test_check_prints_summary_information
|
|
70
83
|
with_cli_project do |root|
|
|
71
84
|
stdout = StringIO.new
|
|
@@ -77,7 +90,7 @@ class TorikagoCliTest < Minitest::Test
|
|
|
77
90
|
|
|
78
91
|
assert_equal 0, exit_code
|
|
79
92
|
assert_match(/scanned 2 Ruby files/, stdout.string)
|
|
80
|
-
assert_match(/found 1 Gateway
|
|
93
|
+
assert_match(/found 1 static Gateway invocations/, stdout.string)
|
|
81
94
|
assert_match(/validated 2 package_api manifests/, stdout.string)
|
|
82
95
|
assert_equal "", stderr.string
|
|
83
96
|
end
|
|
@@ -111,7 +124,7 @@ class TorikagoCliTest < Minitest::Test
|
|
|
111
124
|
<<~RUBY
|
|
112
125
|
class FooService
|
|
113
126
|
def call
|
|
114
|
-
Torikago::Gateway.
|
|
127
|
+
Torikago::Gateway.invoke("Bar::SubmitOrderCommand", :call)
|
|
115
128
|
end
|
|
116
129
|
end
|
|
117
130
|
RUBY
|
|
@@ -122,6 +135,8 @@ class TorikagoCliTest < Minitest::Test
|
|
|
122
135
|
<<~YAML
|
|
123
136
|
exports:
|
|
124
137
|
Bar::SubmitOrderCommand:
|
|
138
|
+
methods:
|
|
139
|
+
- call
|
|
125
140
|
allowed_callers:
|
|
126
141
|
- foo
|
|
127
142
|
YAML
|
|
@@ -10,6 +10,7 @@ class TorikagoConfigurationTest < Minitest::Test
|
|
|
10
10
|
:foo,
|
|
11
11
|
root: "/modules/foo",
|
|
12
12
|
entrypoint: "lib/foo/box_runtime.rb",
|
|
13
|
+
rails_engine: true,
|
|
13
14
|
setup: "config/box_setup.rb",
|
|
14
15
|
gemfile: "Gemfile"
|
|
15
16
|
)
|
|
@@ -19,6 +20,7 @@ class TorikagoConfigurationTest < Minitest::Test
|
|
|
19
20
|
assert_equal :foo, definition.name
|
|
20
21
|
assert_equal "/modules/foo", definition.root.to_s
|
|
21
22
|
assert_equal "lib/foo/box_runtime.rb", definition.entrypoint
|
|
23
|
+
assert_equal true, definition.rails_engine
|
|
22
24
|
assert_equal "config/box_setup.rb", definition.setup
|
|
23
25
|
assert_equal "Gemfile", definition.gemfile
|
|
24
26
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative "../test_helper"
|
|
2
|
+
|
|
3
|
+
class TorikagoControllerEndpointTest < Minitest::Test
|
|
4
|
+
def test_call_dispatches_to_a_box_owned_controller
|
|
5
|
+
received = nil
|
|
6
|
+
container = Object.new
|
|
7
|
+
container.define_singleton_method(:dispatch_controller) do |env, controller_name:, action_name:|
|
|
8
|
+
received = [env, controller_name, action_name]
|
|
9
|
+
[200, { "content-type" => "text/plain" }, ["ok"]]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
registry = Object.new
|
|
13
|
+
registry.define_singleton_method(:resolve) do |name|
|
|
14
|
+
raise "unexpected module: #{name}" unless name == :foo
|
|
15
|
+
|
|
16
|
+
container
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
endpoint = Torikago::ControllerEndpoint.new(
|
|
20
|
+
"foo",
|
|
21
|
+
"WidgetsController",
|
|
22
|
+
"show",
|
|
23
|
+
registry: registry
|
|
24
|
+
)
|
|
25
|
+
env = { "PATH_INFO" => "/widgets/7" }
|
|
26
|
+
|
|
27
|
+
assert_equal [200, { "content-type" => "text/plain" }, ["ok"]], endpoint.call(env)
|
|
28
|
+
assert_equal [env, "WidgetsController", :show], received
|
|
29
|
+
end
|
|
30
|
+
end
|