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.
@@ -3,152 +3,178 @@ require "fileutils"
3
3
  require "tmpdir"
4
4
 
5
5
  class TorikagoCheckerTest < Minitest::Test
6
- def test_check_returns_no_errors_for_declared_calls
6
+ def test_check_detects_one_line_gateway_invoke
7
7
  with_project do |root, configuration|
8
- FileUtils.mkdir_p(File.join(root, "modules/foo"))
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 = Dir.chdir(root) { checker.call }
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 2, result.manifest_count
14
+ assert_equal 0, result.dynamic_gateway_call_count
32
15
  end
33
16
  end
34
17
 
35
- def test_check_reports_undeclared_calls_and_missing_public_api_files
18
+ def test_check_detects_multiline_build_and_invoke
36
19
  with_project do |root, configuration|
37
- FileUtils.mkdir_p(File.join(root, "modules/foo"))
38
- File.write(
39
- File.join(root, "modules/foo/service.rb"),
20
+ write_caller(
21
+ root,
40
22
  <<~RUBY
41
- class FooService
42
- def call
43
- Torikago::Gateway.call("Bar::MissingCommand")
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
- File.write(
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
- checker = Torikago::Checker.new(
60
- configuration: configuration,
61
- source_roots: [File.join(root, "modules")]
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 = Dir.chdir(root) { checker.call }
40
+ result = check(root, configuration)
65
41
 
66
42
  refute result.ok?
67
- assert_equal 1, result.errors.size
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 test_check_uses_a_configured_entrypoint_directory_when_matching_manifest_files
73
- with_project(entrypoint: "components/public_api") do |root, configuration|
74
- FileUtils.mkdir_p(File.join(root, "modules/foo"))
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
- checker = Torikago::Checker.new(
87
- configuration: configuration,
88
- source_roots: [File.join(root, "modules")]
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
- result = Dir.chdir(root) { checker.call }
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
- assert result.ok?
94
- assert_equal [], result.errors
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 test_check_does_not_match_manifest_entries_against_parent_when_configured_entrypoint_directory_is_missing
99
- Dir.mktmpdir("torikago-checker") do |root|
100
- foo_root = File.join(root, "modules/foo")
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
- configuration = Torikago::Configuration.new
113
- configuration.register(:foo, root: foo_root, entrypoint: "app/package_api")
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
- checker = Torikago::Checker.new(
116
- configuration: configuration,
117
- source_roots: [File.join(root, "modules")]
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
- result = Dir.chdir(root) { checker.call }
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
- assert_equal 1, result.errors.size
124
- assert_match(/matching file/, result.errors.first)
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 with_project(entrypoint: nil)
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
- allowed_callers:
143
- - foo
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
- def call
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\.call usages/, stdout.string)
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.call("Bar::SubmitOrderCommand")
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