crspec 0.1.1 → 0.1.3
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/CHANGELOG.md +86 -0
- data/README.md +1 -1
- data/lib/crspec/cli.rb +86 -5
- data/lib/crspec/configuration.rb +62 -9
- data/lib/crspec/dsl.rb +66 -2
- data/lib/crspec/example.rb +60 -6
- data/lib/crspec/example_group.rb +363 -63
- data/lib/crspec/execution_context.rb +10 -6
- data/lib/crspec/expectations.rb +62 -2
- data/lib/crspec/file_fixtures.rb +18 -0
- data/lib/crspec/formatters/progress_formatter.rb +37 -14
- data/lib/crspec/matchers.rb +642 -0
- data/lib/crspec/mock/argument_matchers.rb +157 -0
- data/lib/crspec/mock/double.rb +246 -13
- data/lib/crspec/mock/interceptor.rb +32 -9
- data/lib/crspec/mock/space.rb +14 -0
- data/lib/crspec/process_runner.rb +332 -0
- data/lib/crspec/rails/assets_shim.rb +33 -0
- data/lib/crspec/rails/database_isolation.rb +179 -8
- data/lib/crspec/rails/parallel.rb +29 -29
- data/lib/crspec/rails/request_helpers.rb +70 -14
- data/lib/crspec/rails/system_server.rb +7 -0
- data/lib/crspec/rails/warden_shim.rb +18 -0
- data/lib/crspec/runner.rb +143 -29
- data/lib/crspec/shared_examples.rb +70 -0
- data/lib/crspec/status_persistence.rb +46 -0
- data/lib/crspec/transpiler/cli.rb +106 -15
- data/lib/crspec/transpiler/rewriter.rb +179 -25
- data/lib/crspec/version.rb +1 -1
- data/lib/crspec.rb +6 -0
- data/lib/rspec/core.rb +2 -0
- data/lib/rspec/expectations.rb +2 -0
- data/lib/rspec/mocks.rb +2 -0
- data/lib/rspec/rails.rb +2 -0
- data/lib/rspec.rb +2 -0
- metadata +27 -156
- data/benchmark/README.md +0 -56
- data/benchmark/run.rb +0 -72
- data/benchmark/spec/user_service_crspec_spec.rb +0 -15
- data/benchmark/spec/user_service_rspec_spec.rb +0 -16
- data/benchmark/target_service.rb +0 -12
- data/benchmark/test/user_service_test.rb +0 -18
- data/demo.rb +0 -129
- data/samples/book_store/.dockerignore +0 -51
- data/samples/book_store/.gitattributes +0 -9
- data/samples/book_store/.github/dependabot.yml +0 -12
- data/samples/book_store/.github/workflows/ci.yml +0 -130
- data/samples/book_store/.gitignore +0 -35
- data/samples/book_store/.kamal/hooks/docker-setup.sample +0 -3
- data/samples/book_store/.kamal/hooks/post-app-boot.sample +0 -3
- data/samples/book_store/.kamal/hooks/post-deploy.sample +0 -14
- data/samples/book_store/.kamal/hooks/post-proxy-reboot.sample +0 -3
- data/samples/book_store/.kamal/hooks/pre-app-boot.sample +0 -3
- data/samples/book_store/.kamal/hooks/pre-build.sample +0 -51
- data/samples/book_store/.kamal/hooks/pre-connect.sample +0 -47
- data/samples/book_store/.kamal/hooks/pre-deploy.sample +0 -122
- data/samples/book_store/.kamal/hooks/pre-proxy-reboot.sample +0 -3
- data/samples/book_store/.kamal/secrets +0 -20
- data/samples/book_store/.rubocop.yml +0 -8
- data/samples/book_store/.ruby-version +0 -1
- data/samples/book_store/Dockerfile +0 -77
- data/samples/book_store/Gemfile +0 -67
- data/samples/book_store/Gemfile.lock +0 -572
- data/samples/book_store/README.md +0 -24
- data/samples/book_store/Rakefile +0 -6
- data/samples/book_store/app/assets/images/.keep +0 -0
- data/samples/book_store/app/assets/stylesheets/application.css +0 -10
- data/samples/book_store/app/controllers/application_controller.rb +0 -2
- data/samples/book_store/app/controllers/books_controller.rb +0 -30
- data/samples/book_store/app/controllers/comments_controller.rb +0 -21
- data/samples/book_store/app/controllers/concerns/.keep +0 -0
- data/samples/book_store/app/controllers/users_controller.rb +0 -21
- data/samples/book_store/app/helpers/application_helper.rb +0 -2
- data/samples/book_store/app/helpers/books_helper.rb +0 -2
- data/samples/book_store/app/helpers/comments_helper.rb +0 -2
- data/samples/book_store/app/helpers/users_helper.rb +0 -2
- data/samples/book_store/app/javascript/application.js +0 -3
- data/samples/book_store/app/javascript/controllers/application.js +0 -9
- data/samples/book_store/app/javascript/controllers/hello_controller.js +0 -7
- data/samples/book_store/app/javascript/controllers/index.js +0 -4
- data/samples/book_store/app/jobs/application_job.rb +0 -7
- data/samples/book_store/app/mailers/application_mailer.rb +0 -4
- data/samples/book_store/app/models/application_record.rb +0 -3
- data/samples/book_store/app/models/book.rb +0 -10
- data/samples/book_store/app/models/comment.rb +0 -6
- data/samples/book_store/app/models/concerns/.keep +0 -0
- data/samples/book_store/app/models/user.rb +0 -4
- data/samples/book_store/app/views/layouts/application.html.erb +0 -29
- data/samples/book_store/app/views/layouts/mailer.html.erb +0 -13
- data/samples/book_store/app/views/layouts/mailer.text.erb +0 -1
- data/samples/book_store/app/views/pwa/manifest.json.erb +0 -22
- data/samples/book_store/app/views/pwa/service-worker.js +0 -26
- data/samples/book_store/bin/brakeman +0 -7
- data/samples/book_store/bin/bundler-audit +0 -6
- data/samples/book_store/bin/ci +0 -6
- data/samples/book_store/bin/dev +0 -2
- data/samples/book_store/bin/docker-entrypoint +0 -8
- data/samples/book_store/bin/importmap +0 -4
- data/samples/book_store/bin/jobs +0 -6
- data/samples/book_store/bin/kamal +0 -16
- data/samples/book_store/bin/rails +0 -4
- data/samples/book_store/bin/rake +0 -4
- data/samples/book_store/bin/rubocop +0 -8
- data/samples/book_store/bin/setup +0 -35
- data/samples/book_store/bin/thrust +0 -5
- data/samples/book_store/config/application.rb +0 -27
- data/samples/book_store/config/boot.rb +0 -4
- data/samples/book_store/config/bundler-audit.yml +0 -5
- data/samples/book_store/config/cable.yml +0 -17
- data/samples/book_store/config/cache.yml +0 -16
- data/samples/book_store/config/ci.rb +0 -24
- data/samples/book_store/config/credentials.yml.enc +0 -1
- data/samples/book_store/config/database.yml +0 -45
- data/samples/book_store/config/deploy.yml +0 -119
- data/samples/book_store/config/environment.rb +0 -5
- data/samples/book_store/config/environments/development.rb +0 -78
- data/samples/book_store/config/environments/production.rb +0 -90
- data/samples/book_store/config/environments/test.rb +0 -53
- data/samples/book_store/config/importmap.rb +0 -7
- data/samples/book_store/config/initializers/assets.rb +0 -7
- data/samples/book_store/config/initializers/content_security_policy.rb +0 -29
- data/samples/book_store/config/initializers/filter_parameter_logging.rb +0 -8
- data/samples/book_store/config/initializers/inflections.rb +0 -16
- data/samples/book_store/config/locales/en.yml +0 -31
- data/samples/book_store/config/puma.rb +0 -42
- data/samples/book_store/config/queue.yml +0 -18
- data/samples/book_store/config/recurring.yml +0 -15
- data/samples/book_store/config/routes.rb +0 -17
- data/samples/book_store/config/storage.yml +0 -27
- data/samples/book_store/config.ru +0 -6
- data/samples/book_store/db/cable_schema.rb +0 -11
- data/samples/book_store/db/cache_schema.rb +0 -12
- data/samples/book_store/db/migrate/20260731035441_create_users.rb +0 -10
- data/samples/book_store/db/migrate/20260731035506_create_books.rb +0 -11
- data/samples/book_store/db/migrate/20260731035528_create_comments.rb +0 -11
- data/samples/book_store/db/queue_schema.rb +0 -129
- data/samples/book_store/db/schema.rb +0 -39
- data/samples/book_store/db/seeds.rb +0 -9
- data/samples/book_store/lib/tasks/.keep +0 -0
- data/samples/book_store/log/.keep +0 -0
- data/samples/book_store/public/400.html +0 -135
- data/samples/book_store/public/404.html +0 -135
- data/samples/book_store/public/406-unsupported-browser.html +0 -135
- data/samples/book_store/public/422.html +0 -135
- data/samples/book_store/public/500.html +0 -135
- data/samples/book_store/public/icon.png +0 -0
- data/samples/book_store/public/icon.svg +0 -3
- data/samples/book_store/public/robots.txt +0 -1
- data/samples/book_store/script/.keep +0 -0
- data/samples/book_store/spec/models/book_spec.rb +0 -42
- data/samples/book_store/spec/models/comment_spec.rb +0 -22
- data/samples/book_store/spec/models/user_advanced_spec.rb +0 -27
- data/samples/book_store/spec/models/user_spec.rb +0 -30
- data/samples/book_store/spec/rails_helper.rb +0 -36
- data/samples/book_store/spec/rails_parallel_spec.rb +0 -23
- data/samples/book_store/spec/requests/books_spec.rb +0 -21
- data/samples/book_store/spec/requests/users_spec.rb +0 -14
- data/samples/book_store/spec/services/inventory_service_spec.rb +0 -28
- data/samples/book_store/spec/services/time_dependent_service_spec.rb +0 -26
- data/samples/book_store/spec/spec_helper.rb +0 -16
- data/samples/book_store/spec/system/book_store_system_spec.rb +0 -18
- data/samples/book_store/storage/.keep +0 -0
- data/samples/book_store/test/controllers/.keep +0 -0
- data/samples/book_store/test/controllers/books_controller_test.rb +0 -7
- data/samples/book_store/test/controllers/comments_controller_test.rb +0 -7
- data/samples/book_store/test/controllers/users_controller_test.rb +0 -7
- data/samples/book_store/test/fixtures/books.yml +0 -11
- data/samples/book_store/test/fixtures/comments.yml +0 -11
- data/samples/book_store/test/fixtures/files/.keep +0 -0
- data/samples/book_store/test/fixtures/users.yml +0 -9
- data/samples/book_store/test/helpers/.keep +0 -0
- data/samples/book_store/test/integration/.keep +0 -0
- data/samples/book_store/test/mailers/.keep +0 -0
- data/samples/book_store/test/models/.keep +0 -0
- data/samples/book_store/test/models/book_test.rb +0 -7
- data/samples/book_store/test/models/comment_test.rb +0 -7
- data/samples/book_store/test/models/user_test.rb +0 -7
- data/samples/book_store/test/test_helper.rb +0 -15
- data/samples/book_store/tmp/.keep +0 -0
- data/samples/book_store/tmp/pids/.keep +0 -0
- data/samples/book_store/tmp/storage/.keep +0 -0
- data/samples/book_store/vendor/.keep +0 -0
- data/samples/book_store/vendor/javascript/.keep +0 -0
- data/samples/user_spec.rb +0 -31
- data/test/crspec/configuration_test.rb +0 -76
- data/test/crspec/execution_context_test.rb +0 -61
- data/test/crspec/mock_test.rb +0 -55
- data/test/crspec/rails_test.rb +0 -62
- data/test/crspec/runner_test.rb +0 -68
- data/test/crspec/transpiler_test.rb +0 -41
- data/test/test_helper.rb +0 -6
|
@@ -4,14 +4,26 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
module Crspec
|
|
6
6
|
module Transpiler
|
|
7
|
+
# Lint severities:
|
|
8
|
+
# :error - construct is rejected by Crspec at load/run time
|
|
9
|
+
# :warning - construct is concurrency-unsafe and needs manual review
|
|
10
|
+
# :info - construct was auto-rewritten or is worth double-checking
|
|
11
|
+
Lint = Struct.new(:line, :severity, :code, :message)
|
|
12
|
+
|
|
7
13
|
class Rewriter < Prism::Visitor
|
|
8
|
-
attr_reader :transformed_code, :warnings
|
|
14
|
+
attr_reader :transformed_code, :warnings, :lints
|
|
9
15
|
|
|
10
16
|
def initialize(source_code)
|
|
11
17
|
super()
|
|
12
18
|
@source_code = source_code
|
|
13
19
|
@replacements = []
|
|
14
|
-
@
|
|
20
|
+
@lints = []
|
|
21
|
+
@inside_example_depth = 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Backwards-compatible string warnings.
|
|
25
|
+
def warnings
|
|
26
|
+
@lints.map { |l| "Line #{l.line}: [#{l.severity}] #{l.message}" }
|
|
15
27
|
end
|
|
16
28
|
|
|
17
29
|
def transpile
|
|
@@ -20,46 +32,188 @@ module Crspec
|
|
|
20
32
|
apply_replacements
|
|
21
33
|
end
|
|
22
34
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
if node.receiver.is_a?(Prism::ConstantReadNode) &&
|
|
26
|
-
node.receiver.name == :RSpec &&
|
|
27
|
-
node.name == :describe
|
|
28
|
-
@replacements << {
|
|
29
|
-
start_offset: node.receiver.location.start_offset,
|
|
30
|
-
end_offset: node.receiver.location.end_offset,
|
|
31
|
-
text: "Crspec"
|
|
32
|
-
}
|
|
33
|
-
end
|
|
35
|
+
def safety_score
|
|
36
|
+
return 100 if @lints.empty?
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
deductions = @lints.sum do |l|
|
|
39
|
+
case l.severity
|
|
40
|
+
when :error then 25
|
|
41
|
+
when :warning then 10
|
|
42
|
+
else 2
|
|
40
43
|
end
|
|
41
44
|
end
|
|
45
|
+
[100 - deductions, 0].max
|
|
46
|
+
end
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
def visit_call_node(node)
|
|
49
|
+
rewrite_rspec_receiver(node)
|
|
50
|
+
rewrite_matcher_define(node)
|
|
51
|
+
rewrite_before_all(node)
|
|
52
|
+
rewrite_focus_aliases(node)
|
|
53
|
+
lint_any_instance(node)
|
|
54
|
+
lint_env_mutation(node)
|
|
55
|
+
lint_time_mutation(node)
|
|
56
|
+
|
|
57
|
+
example_call = %i[it specify example].include?(node.name)
|
|
58
|
+
@inside_example_depth += 1 if example_call
|
|
59
|
+
begin
|
|
60
|
+
super
|
|
61
|
+
ensure
|
|
62
|
+
@inside_example_depth -= 1 if example_call
|
|
63
|
+
end
|
|
44
64
|
end
|
|
45
65
|
|
|
46
66
|
def visit_constant_read_node(node)
|
|
47
67
|
if node.name == :RSpec
|
|
48
|
-
|
|
49
|
-
start_offset: node.location.start_offset,
|
|
50
|
-
end_offset: node.location.end_offset,
|
|
51
|
-
text: "Crspec"
|
|
52
|
-
}
|
|
68
|
+
replace(node.location, "Crspec")
|
|
53
69
|
end
|
|
54
70
|
super
|
|
55
71
|
end
|
|
56
72
|
|
|
73
|
+
def visit_class_variable_write_node(node)
|
|
74
|
+
add_lint(node, :warning, :class_variable,
|
|
75
|
+
"class variable mutation (#{node.name}) is shared across all threads/fibers")
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def visit_class_variable_read_node(node)
|
|
80
|
+
add_lint(node, :warning, :class_variable,
|
|
81
|
+
"class variable read (#{node.name}) implies shared mutable state")
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def visit_constant_write_node(node)
|
|
86
|
+
if @inside_example_depth.positive?
|
|
87
|
+
add_lint(node, :warning, :constant_mutation,
|
|
88
|
+
"constant assignment (#{node.name}) inside an example mutates global state")
|
|
89
|
+
end
|
|
90
|
+
super
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def visit_index_operator_write_node(node)
|
|
94
|
+
lint_env_index(node)
|
|
95
|
+
super
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def visit_index_and_write_node(node)
|
|
99
|
+
lint_env_index(node)
|
|
100
|
+
super
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def visit_index_or_write_node(node)
|
|
104
|
+
lint_env_index(node)
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
|
|
57
108
|
private
|
|
58
109
|
|
|
110
|
+
def rewrite_rspec_receiver(node)
|
|
111
|
+
return unless node.receiver.is_a?(Prism::ConstantReadNode) && node.receiver.name == :RSpec
|
|
112
|
+
|
|
113
|
+
replace(node.receiver.location, "Crspec")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# RSpec::Matchers.define -> Crspec::Matchers.define
|
|
117
|
+
def rewrite_matcher_define(node)
|
|
118
|
+
return unless node.name == :define
|
|
119
|
+
return unless node.receiver.is_a?(Prism::ConstantPathNode)
|
|
120
|
+
|
|
121
|
+
path = node.receiver
|
|
122
|
+
parent = path.parent
|
|
123
|
+
return unless parent.is_a?(Prism::ConstantReadNode) && parent.name == :RSpec && path.name == :Matchers
|
|
124
|
+
|
|
125
|
+
replace(parent.location, "Crspec")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# before(:all)/before(:context) -> before(:each) with annotation.
|
|
129
|
+
def rewrite_before_all(node)
|
|
130
|
+
return unless %i[before after].include?(node.name) && node.arguments
|
|
131
|
+
|
|
132
|
+
first_arg = node.arguments.arguments.first
|
|
133
|
+
return unless first_arg.is_a?(Prism::SymbolNode) && %w[all context suite].include?(first_arg.value)
|
|
134
|
+
|
|
135
|
+
add_lint(node, :info, :before_all_rewritten,
|
|
136
|
+
"#{node.name}(:#{first_arg.value}) auto-rewritten to #{node.name}(:each); " \
|
|
137
|
+
"review for per-example cost (consider let/let!)")
|
|
138
|
+
replace(first_arg.location, ":each")
|
|
139
|
+
annotate_line(node, "# CRSPEC-MIGRATION: was #{node.name}(:#{first_arg.value}) — now runs per example; consider let/let! for expensive setup")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# fit/fdescribe/fcontext are focus aliases; xit etc are already
|
|
143
|
+
# supported natively. Normalize focus to plain forms with a note.
|
|
144
|
+
def rewrite_focus_aliases(node)
|
|
145
|
+
mapping = { fit: "it", fdescribe: "describe", fcontext: "context" }
|
|
146
|
+
replacement = mapping[node.name]
|
|
147
|
+
return unless replacement
|
|
148
|
+
return if node.receiver
|
|
149
|
+
|
|
150
|
+
add_lint(node, :info, :focus_removed,
|
|
151
|
+
"#{node.name} (focus) normalized to #{replacement}")
|
|
152
|
+
replace(node.message_loc, replacement)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def lint_any_instance(node)
|
|
156
|
+
if %i[any_instance allow_any_instance_of expect_any_instance_of].include?(node.name)
|
|
157
|
+
add_lint(node, :error, :any_instance,
|
|
158
|
+
"#{node.name} mutates shared class hierarchies at runtime and is rejected by Crspec; " \
|
|
159
|
+
"inject a double or stub the specific instance instead")
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def lint_env_mutation(node)
|
|
164
|
+
return unless node.name == :[]= && node.receiver.is_a?(Prism::ConstantReadNode) && node.receiver.name == :ENV
|
|
165
|
+
|
|
166
|
+
add_lint(node, :warning, :env_mutation,
|
|
167
|
+
"ENV mutation inside specs is process-global and races across threads")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def lint_env_index(node)
|
|
171
|
+
return unless node.receiver.is_a?(Prism::ConstantReadNode) && node.receiver.name == :ENV
|
|
172
|
+
|
|
173
|
+
add_lint(node, :warning, :env_mutation,
|
|
174
|
+
"ENV mutation inside specs is process-global and races across threads")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
TIME_MUTATORS = %i[travel travel_to travel_back freeze return].freeze
|
|
178
|
+
|
|
179
|
+
def lint_time_mutation(node)
|
|
180
|
+
receiver = node.receiver
|
|
181
|
+
if receiver.is_a?(Prism::ConstantReadNode) && receiver.name == :Timecop
|
|
182
|
+
add_lint(node, :warning, :time_mutation,
|
|
183
|
+
"Timecop.#{node.name} mutates global time state; use ActiveSupport::Testing::TimeHelpers " \
|
|
184
|
+
"scoped per example, or inject a clock")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def add_lint(node, severity, code, message)
|
|
189
|
+
@lints << Lint.new(node.location.start_line, severity, code, message)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def replace(location, text)
|
|
193
|
+
@replacements << {
|
|
194
|
+
start_offset: location.start_offset,
|
|
195
|
+
end_offset: location.end_offset,
|
|
196
|
+
text: text
|
|
197
|
+
}
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Insert a comment line above the node's line.
|
|
201
|
+
def annotate_line(node, comment)
|
|
202
|
+
line_start = @source_code.rindex("\n", node.location.start_offset)
|
|
203
|
+
line_start = line_start.nil? ? 0 : line_start + 1
|
|
204
|
+
indent = @source_code[line_start..node.location.start_offset][/\A[ \t]*/]
|
|
205
|
+
@replacements << {
|
|
206
|
+
start_offset: line_start,
|
|
207
|
+
end_offset: line_start,
|
|
208
|
+
text: "#{indent}#{comment}\n"
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
59
212
|
def apply_replacements
|
|
60
213
|
buffer = @source_code.dup
|
|
61
214
|
# Sort replacements backwards so offsets remain valid during splicing
|
|
62
|
-
sorted = @replacements.uniq { |r| [r[:start_offset], r[:end_offset]
|
|
215
|
+
sorted = @replacements.uniq { |r| [r[:start_offset], r[:end_offset], r[:text]] }
|
|
216
|
+
.sort_by { |r| [-r[:start_offset], -r[:end_offset]] }
|
|
63
217
|
sorted.each do |r|
|
|
64
218
|
buffer[r[:start_offset]...r[:end_offset]] = r[:text]
|
|
65
219
|
end
|
data/lib/crspec/version.rb
CHANGED
data/lib/crspec.rb
CHANGED
|
@@ -5,13 +5,17 @@ require_relative "crspec/configuration"
|
|
|
5
5
|
require_relative "crspec/execution_context"
|
|
6
6
|
require_relative "crspec/matchers"
|
|
7
7
|
require_relative "crspec/expectations"
|
|
8
|
+
require_relative "crspec/file_fixtures"
|
|
8
9
|
require_relative "crspec/example"
|
|
10
|
+
require_relative "crspec/shared_examples"
|
|
9
11
|
require_relative "crspec/example_group"
|
|
10
12
|
require_relative "crspec/mock/interceptor"
|
|
11
13
|
require_relative "crspec/mock/space"
|
|
12
14
|
require_relative "crspec/mock/double"
|
|
13
15
|
require_relative "crspec/formatters/progress_formatter"
|
|
16
|
+
require_relative "crspec/status_persistence"
|
|
14
17
|
require_relative "crspec/runner"
|
|
18
|
+
require_relative "crspec/process_runner"
|
|
15
19
|
require_relative "crspec/dsl"
|
|
16
20
|
require_relative "crspec/rails/database_isolation"
|
|
17
21
|
require_relative "crspec/rails/system_server"
|
|
@@ -24,3 +28,5 @@ require_relative "crspec/generators/init"
|
|
|
24
28
|
module Crspec
|
|
25
29
|
class Error < StandardError; end
|
|
26
30
|
end
|
|
31
|
+
|
|
32
|
+
RSpec = Crspec unless defined?(RSpec)
|
data/lib/rspec/core.rb
ADDED
data/lib/rspec/mocks.rb
ADDED
data/lib/rspec/rails.rb
ADDED
data/lib/rspec.rb
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: crspec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aboobacker MK
|
|
@@ -10,6 +10,20 @@ bindir: exe
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2026-07-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: async
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: prism
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,13 +52,6 @@ files:
|
|
|
38
52
|
- LICENSE.txt
|
|
39
53
|
- README.md
|
|
40
54
|
- Rakefile
|
|
41
|
-
- benchmark/README.md
|
|
42
|
-
- benchmark/run.rb
|
|
43
|
-
- benchmark/spec/user_service_crspec_spec.rb
|
|
44
|
-
- benchmark/spec/user_service_rspec_spec.rb
|
|
45
|
-
- benchmark/target_service.rb
|
|
46
|
-
- benchmark/test/user_service_test.rb
|
|
47
|
-
- demo.rb
|
|
48
55
|
- exe/crspec
|
|
49
56
|
- exe/crspec-transpile
|
|
50
57
|
- lib/crspec.rb
|
|
@@ -55,169 +62,33 @@ files:
|
|
|
55
62
|
- lib/crspec/example_group.rb
|
|
56
63
|
- lib/crspec/execution_context.rb
|
|
57
64
|
- lib/crspec/expectations.rb
|
|
65
|
+
- lib/crspec/file_fixtures.rb
|
|
58
66
|
- lib/crspec/formatters/progress_formatter.rb
|
|
59
67
|
- lib/crspec/generators/init.rb
|
|
60
68
|
- lib/crspec/matchers.rb
|
|
69
|
+
- lib/crspec/mock/argument_matchers.rb
|
|
61
70
|
- lib/crspec/mock/double.rb
|
|
62
71
|
- lib/crspec/mock/interceptor.rb
|
|
63
72
|
- lib/crspec/mock/space.rb
|
|
73
|
+
- lib/crspec/process_runner.rb
|
|
74
|
+
- lib/crspec/rails/assets_shim.rb
|
|
64
75
|
- lib/crspec/rails/database_isolation.rb
|
|
65
76
|
- lib/crspec/rails/parallel.rb
|
|
66
77
|
- lib/crspec/rails/request_helpers.rb
|
|
67
78
|
- lib/crspec/rails/system_server.rb
|
|
79
|
+
- lib/crspec/rails/warden_shim.rb
|
|
68
80
|
- lib/crspec/runner.rb
|
|
81
|
+
- lib/crspec/shared_examples.rb
|
|
82
|
+
- lib/crspec/status_persistence.rb
|
|
69
83
|
- lib/crspec/transpiler/cli.rb
|
|
70
84
|
- lib/crspec/transpiler/rewriter.rb
|
|
71
85
|
- lib/crspec/version.rb
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
- samples/book_store/.kamal/hooks/docker-setup.sample
|
|
78
|
-
- samples/book_store/.kamal/hooks/post-app-boot.sample
|
|
79
|
-
- samples/book_store/.kamal/hooks/post-deploy.sample
|
|
80
|
-
- samples/book_store/.kamal/hooks/post-proxy-reboot.sample
|
|
81
|
-
- samples/book_store/.kamal/hooks/pre-app-boot.sample
|
|
82
|
-
- samples/book_store/.kamal/hooks/pre-build.sample
|
|
83
|
-
- samples/book_store/.kamal/hooks/pre-connect.sample
|
|
84
|
-
- samples/book_store/.kamal/hooks/pre-deploy.sample
|
|
85
|
-
- samples/book_store/.kamal/hooks/pre-proxy-reboot.sample
|
|
86
|
-
- samples/book_store/.kamal/secrets
|
|
87
|
-
- samples/book_store/.rubocop.yml
|
|
88
|
-
- samples/book_store/.ruby-version
|
|
89
|
-
- samples/book_store/Dockerfile
|
|
90
|
-
- samples/book_store/Gemfile
|
|
91
|
-
- samples/book_store/Gemfile.lock
|
|
92
|
-
- samples/book_store/README.md
|
|
93
|
-
- samples/book_store/Rakefile
|
|
94
|
-
- samples/book_store/app/assets/images/.keep
|
|
95
|
-
- samples/book_store/app/assets/stylesheets/application.css
|
|
96
|
-
- samples/book_store/app/controllers/application_controller.rb
|
|
97
|
-
- samples/book_store/app/controllers/books_controller.rb
|
|
98
|
-
- samples/book_store/app/controllers/comments_controller.rb
|
|
99
|
-
- samples/book_store/app/controllers/concerns/.keep
|
|
100
|
-
- samples/book_store/app/controllers/users_controller.rb
|
|
101
|
-
- samples/book_store/app/helpers/application_helper.rb
|
|
102
|
-
- samples/book_store/app/helpers/books_helper.rb
|
|
103
|
-
- samples/book_store/app/helpers/comments_helper.rb
|
|
104
|
-
- samples/book_store/app/helpers/users_helper.rb
|
|
105
|
-
- samples/book_store/app/javascript/application.js
|
|
106
|
-
- samples/book_store/app/javascript/controllers/application.js
|
|
107
|
-
- samples/book_store/app/javascript/controllers/hello_controller.js
|
|
108
|
-
- samples/book_store/app/javascript/controllers/index.js
|
|
109
|
-
- samples/book_store/app/jobs/application_job.rb
|
|
110
|
-
- samples/book_store/app/mailers/application_mailer.rb
|
|
111
|
-
- samples/book_store/app/models/application_record.rb
|
|
112
|
-
- samples/book_store/app/models/book.rb
|
|
113
|
-
- samples/book_store/app/models/comment.rb
|
|
114
|
-
- samples/book_store/app/models/concerns/.keep
|
|
115
|
-
- samples/book_store/app/models/user.rb
|
|
116
|
-
- samples/book_store/app/views/layouts/application.html.erb
|
|
117
|
-
- samples/book_store/app/views/layouts/mailer.html.erb
|
|
118
|
-
- samples/book_store/app/views/layouts/mailer.text.erb
|
|
119
|
-
- samples/book_store/app/views/pwa/manifest.json.erb
|
|
120
|
-
- samples/book_store/app/views/pwa/service-worker.js
|
|
121
|
-
- samples/book_store/bin/brakeman
|
|
122
|
-
- samples/book_store/bin/bundler-audit
|
|
123
|
-
- samples/book_store/bin/ci
|
|
124
|
-
- samples/book_store/bin/dev
|
|
125
|
-
- samples/book_store/bin/docker-entrypoint
|
|
126
|
-
- samples/book_store/bin/importmap
|
|
127
|
-
- samples/book_store/bin/jobs
|
|
128
|
-
- samples/book_store/bin/kamal
|
|
129
|
-
- samples/book_store/bin/rails
|
|
130
|
-
- samples/book_store/bin/rake
|
|
131
|
-
- samples/book_store/bin/rubocop
|
|
132
|
-
- samples/book_store/bin/setup
|
|
133
|
-
- samples/book_store/bin/thrust
|
|
134
|
-
- samples/book_store/config.ru
|
|
135
|
-
- samples/book_store/config/application.rb
|
|
136
|
-
- samples/book_store/config/boot.rb
|
|
137
|
-
- samples/book_store/config/bundler-audit.yml
|
|
138
|
-
- samples/book_store/config/cable.yml
|
|
139
|
-
- samples/book_store/config/cache.yml
|
|
140
|
-
- samples/book_store/config/ci.rb
|
|
141
|
-
- samples/book_store/config/credentials.yml.enc
|
|
142
|
-
- samples/book_store/config/database.yml
|
|
143
|
-
- samples/book_store/config/deploy.yml
|
|
144
|
-
- samples/book_store/config/environment.rb
|
|
145
|
-
- samples/book_store/config/environments/development.rb
|
|
146
|
-
- samples/book_store/config/environments/production.rb
|
|
147
|
-
- samples/book_store/config/environments/test.rb
|
|
148
|
-
- samples/book_store/config/importmap.rb
|
|
149
|
-
- samples/book_store/config/initializers/assets.rb
|
|
150
|
-
- samples/book_store/config/initializers/content_security_policy.rb
|
|
151
|
-
- samples/book_store/config/initializers/filter_parameter_logging.rb
|
|
152
|
-
- samples/book_store/config/initializers/inflections.rb
|
|
153
|
-
- samples/book_store/config/locales/en.yml
|
|
154
|
-
- samples/book_store/config/puma.rb
|
|
155
|
-
- samples/book_store/config/queue.yml
|
|
156
|
-
- samples/book_store/config/recurring.yml
|
|
157
|
-
- samples/book_store/config/routes.rb
|
|
158
|
-
- samples/book_store/config/storage.yml
|
|
159
|
-
- samples/book_store/db/cable_schema.rb
|
|
160
|
-
- samples/book_store/db/cache_schema.rb
|
|
161
|
-
- samples/book_store/db/migrate/20260731035441_create_users.rb
|
|
162
|
-
- samples/book_store/db/migrate/20260731035506_create_books.rb
|
|
163
|
-
- samples/book_store/db/migrate/20260731035528_create_comments.rb
|
|
164
|
-
- samples/book_store/db/queue_schema.rb
|
|
165
|
-
- samples/book_store/db/schema.rb
|
|
166
|
-
- samples/book_store/db/seeds.rb
|
|
167
|
-
- samples/book_store/lib/tasks/.keep
|
|
168
|
-
- samples/book_store/log/.keep
|
|
169
|
-
- samples/book_store/public/400.html
|
|
170
|
-
- samples/book_store/public/404.html
|
|
171
|
-
- samples/book_store/public/406-unsupported-browser.html
|
|
172
|
-
- samples/book_store/public/422.html
|
|
173
|
-
- samples/book_store/public/500.html
|
|
174
|
-
- samples/book_store/public/icon.png
|
|
175
|
-
- samples/book_store/public/icon.svg
|
|
176
|
-
- samples/book_store/public/robots.txt
|
|
177
|
-
- samples/book_store/script/.keep
|
|
178
|
-
- samples/book_store/spec/models/book_spec.rb
|
|
179
|
-
- samples/book_store/spec/models/comment_spec.rb
|
|
180
|
-
- samples/book_store/spec/models/user_advanced_spec.rb
|
|
181
|
-
- samples/book_store/spec/models/user_spec.rb
|
|
182
|
-
- samples/book_store/spec/rails_helper.rb
|
|
183
|
-
- samples/book_store/spec/rails_parallel_spec.rb
|
|
184
|
-
- samples/book_store/spec/requests/books_spec.rb
|
|
185
|
-
- samples/book_store/spec/requests/users_spec.rb
|
|
186
|
-
- samples/book_store/spec/services/inventory_service_spec.rb
|
|
187
|
-
- samples/book_store/spec/services/time_dependent_service_spec.rb
|
|
188
|
-
- samples/book_store/spec/spec_helper.rb
|
|
189
|
-
- samples/book_store/spec/system/book_store_system_spec.rb
|
|
190
|
-
- samples/book_store/storage/.keep
|
|
191
|
-
- samples/book_store/test/controllers/.keep
|
|
192
|
-
- samples/book_store/test/controllers/books_controller_test.rb
|
|
193
|
-
- samples/book_store/test/controllers/comments_controller_test.rb
|
|
194
|
-
- samples/book_store/test/controllers/users_controller_test.rb
|
|
195
|
-
- samples/book_store/test/fixtures/books.yml
|
|
196
|
-
- samples/book_store/test/fixtures/comments.yml
|
|
197
|
-
- samples/book_store/test/fixtures/files/.keep
|
|
198
|
-
- samples/book_store/test/fixtures/users.yml
|
|
199
|
-
- samples/book_store/test/helpers/.keep
|
|
200
|
-
- samples/book_store/test/integration/.keep
|
|
201
|
-
- samples/book_store/test/mailers/.keep
|
|
202
|
-
- samples/book_store/test/models/.keep
|
|
203
|
-
- samples/book_store/test/models/book_test.rb
|
|
204
|
-
- samples/book_store/test/models/comment_test.rb
|
|
205
|
-
- samples/book_store/test/models/user_test.rb
|
|
206
|
-
- samples/book_store/test/test_helper.rb
|
|
207
|
-
- samples/book_store/tmp/.keep
|
|
208
|
-
- samples/book_store/tmp/pids/.keep
|
|
209
|
-
- samples/book_store/tmp/storage/.keep
|
|
210
|
-
- samples/book_store/vendor/.keep
|
|
211
|
-
- samples/book_store/vendor/javascript/.keep
|
|
212
|
-
- samples/user_spec.rb
|
|
86
|
+
- lib/rspec.rb
|
|
87
|
+
- lib/rspec/core.rb
|
|
88
|
+
- lib/rspec/expectations.rb
|
|
89
|
+
- lib/rspec/mocks.rb
|
|
90
|
+
- lib/rspec/rails.rb
|
|
213
91
|
- sig/crspec.rbs
|
|
214
|
-
- test/crspec/configuration_test.rb
|
|
215
|
-
- test/crspec/execution_context_test.rb
|
|
216
|
-
- test/crspec/mock_test.rb
|
|
217
|
-
- test/crspec/rails_test.rb
|
|
218
|
-
- test/crspec/runner_test.rb
|
|
219
|
-
- test/crspec/transpiler_test.rb
|
|
220
|
-
- test/test_helper.rb
|
|
221
92
|
homepage: https://github.com/crspec/crspec
|
|
222
93
|
licenses:
|
|
223
94
|
- MIT
|
data/benchmark/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# Crspec Native Benchmark Suite
|
|
2
|
-
|
|
3
|
-
This directory contains benchmarking utilities comparing default native framework runner performance across **crspec**, **RSpec**, and **Minitest** without any manual thread-slicing or concurrency controls in the benchmark script.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Directory Structure
|
|
8
|
-
|
|
9
|
-
```text
|
|
10
|
-
benchmark/
|
|
11
|
-
├── target_service.rb # Domain target class under test (UserService)
|
|
12
|
-
├── spec/
|
|
13
|
-
│ ├── user_service_crspec_spec.rb # Idiomatic Crspec spec (Crspec.describe)
|
|
14
|
-
│ └── user_service_rspec_spec.rb # Idiomatic RSpec spec (RSpec.describe)
|
|
15
|
-
├── test/
|
|
16
|
-
│ └── user_service_test.rb # Idiomatic Minitest test (class UserServiceTest < Minitest::Test)
|
|
17
|
-
├── run.rb # Clean benchmark runner script
|
|
18
|
-
└── README.md
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Evaluated Configurations
|
|
24
|
-
|
|
25
|
-
Each framework is executed using its native default runner invocation:
|
|
26
|
-
- **Crspec (Native Default Runner)**: Uses `Crspec::Runner.new` default multi-threaded / fiber execution kernel.
|
|
27
|
-
- **RSpec (Native Default Runner)**: Native standard RSpec runner.
|
|
28
|
-
- **Minitest (Native Default Runner)**: Native Minitest runner.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## How to Run
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
mise exec -- ruby benchmark/run.rb
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Benchmark Results (macOS arm64 / Ruby 4.0.2)
|
|
41
|
-
|
|
42
|
-
```text
|
|
43
|
-
==========================================================================
|
|
44
|
-
Crspec vs RSpec vs Minitest Benchmark Suite
|
|
45
|
-
Test Workload: 100 examples per framework
|
|
46
|
-
Evaluating Default Native Framework Runners (No Manual Concurrency Controls)
|
|
47
|
-
==========================================================================
|
|
48
|
-
|
|
49
|
-
Results Summary:
|
|
50
|
-
Framework Engine | Duration (s) | Throughput (ops/s)
|
|
51
|
-
------------------------------------------------------------------------------
|
|
52
|
-
Crspec (Native Default Runner) | 0.0211 | 4749.47
|
|
53
|
-
RSpec (Native Default Runner) | 0.1375 | 727.15
|
|
54
|
-
Minitest (Native Default Runner) | 0.1259 | 794.45
|
|
55
|
-
==============================================================================
|
|
56
|
-
```
|
data/benchmark/run.rb
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
4
|
-
$LOAD_PATH.unshift File.expand_path(__dir__)
|
|
5
|
-
|
|
6
|
-
require "crspec"
|
|
7
|
-
require "rspec/core"
|
|
8
|
-
require "minitest"
|
|
9
|
-
require "stringio"
|
|
10
|
-
|
|
11
|
-
NUMBER_OF_TESTS = 100
|
|
12
|
-
|
|
13
|
-
Rails.logger.debug "=========================================================================="
|
|
14
|
-
Rails.logger.debug " Crspec vs RSpec vs Minitest Benchmark Suite"
|
|
15
|
-
Rails.logger.debug { " Test Workload: #{NUMBER_OF_TESTS} examples per framework" }
|
|
16
|
-
Rails.logger.debug " Evaluating Default Native Framework Runners (No Manual Concurrency Controls)"
|
|
17
|
-
Rails.logger.debug "=========================================================================="
|
|
18
|
-
|
|
19
|
-
results = {}
|
|
20
|
-
null_formatter = Crspec::Formatters::NullFormatter.new
|
|
21
|
-
|
|
22
|
-
# -----------------------------------------------------------------------------
|
|
23
|
-
# 1. Crspec Native Execution (Default Concurrency Kernel)
|
|
24
|
-
# -----------------------------------------------------------------------------
|
|
25
|
-
Crspec.reset!
|
|
26
|
-
load File.expand_path("spec/user_service_crspec_spec.rb", __dir__)
|
|
27
|
-
|
|
28
|
-
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
29
|
-
Crspec::Runner.new(formatter: null_formatter).run(Crspec.world.example_groups)
|
|
30
|
-
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
31
|
-
results["Crspec (Native Default Runner)"] = t1 - t0
|
|
32
|
-
|
|
33
|
-
# -----------------------------------------------------------------------------
|
|
34
|
-
# 2. RSpec Native Execution
|
|
35
|
-
# -----------------------------------------------------------------------------
|
|
36
|
-
config = RSpec::Core::Configuration.new
|
|
37
|
-
config.output_stream = StringIO.new
|
|
38
|
-
world = RSpec::Core::World.new(config)
|
|
39
|
-
RSpec.instance_variable_set(:@world, world)
|
|
40
|
-
load File.expand_path("spec/user_service_rspec_spec.rb", __dir__)
|
|
41
|
-
|
|
42
|
-
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
43
|
-
world.example_groups.first.run(config.reporter)
|
|
44
|
-
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
45
|
-
results["RSpec (Native Default Runner)"] = t1 - t0
|
|
46
|
-
|
|
47
|
-
# -----------------------------------------------------------------------------
|
|
48
|
-
# 3. Minitest Native Execution
|
|
49
|
-
# -----------------------------------------------------------------------------
|
|
50
|
-
load File.expand_path("test/user_service_test.rb", __dir__)
|
|
51
|
-
|
|
52
|
-
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
53
|
-
Minitest::CompositeReporter.new
|
|
54
|
-
UserServiceTest.runnable_methods.each do |method_name|
|
|
55
|
-
UserServiceTest.new(method_name).run
|
|
56
|
-
end
|
|
57
|
-
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
58
|
-
results["Minitest (Native Default Runner)"] = t1 - t0
|
|
59
|
-
|
|
60
|
-
# -----------------------------------------------------------------------------
|
|
61
|
-
# Summary Output
|
|
62
|
-
# -----------------------------------------------------------------------------
|
|
63
|
-
Rails.logger.debug "\nResults Summary:"
|
|
64
|
-
Rails.logger.debug format("%-45s | %-12s | %-15s", "Framework Engine", "Duration (s)", "Throughput (ops/s)")
|
|
65
|
-
Rails.logger.debug "-" * 78
|
|
66
|
-
|
|
67
|
-
results.each do |engine, duration|
|
|
68
|
-
throughput = (NUMBER_OF_TESTS / duration).round(2)
|
|
69
|
-
Rails.logger.debug format("%-45s | %-12.4f | %-15.2f", engine, duration, throughput)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
Rails.logger.debug "=" * 78
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../target_service"
|
|
4
|
-
|
|
5
|
-
Crspec.describe UserService do
|
|
6
|
-
100.times do |i|
|
|
7
|
-
let(:"user_#{i}") { UserService.process_user(i, "User #{i}", "user#{i}@example.com") }
|
|
8
|
-
|
|
9
|
-
it "processes user #{i}" do
|
|
10
|
-
u = send(:"user_#{i}")
|
|
11
|
-
expect(u[:valid]).to be(true)
|
|
12
|
-
expect(u[:token]).not_to be_nil
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rspec/core"
|
|
4
|
-
require_relative "../target_service"
|
|
5
|
-
|
|
6
|
-
RSpec.describe UserService do
|
|
7
|
-
100.times do |i|
|
|
8
|
-
let(:"user_#{i}") { UserService.process_user(i, "User #{i}", "user#{i}@example.com") }
|
|
9
|
-
|
|
10
|
-
it "processes user #{i}" do
|
|
11
|
-
u = send(:"user_#{i}")
|
|
12
|
-
expect(u[:valid]).to be(true)
|
|
13
|
-
expect(u[:token]).not_to be_nil
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|