crspec 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +67 -17
- data/benchmark/README.md +56 -0
- data/benchmark/run.rb +72 -0
- data/benchmark/spec/user_service_crspec_spec.rb +15 -0
- data/benchmark/spec/user_service_rspec_spec.rb +16 -0
- data/benchmark/target_service.rb +12 -0
- data/benchmark/test/user_service_test.rb +18 -0
- data/demo.rb +6 -3
- data/lib/crspec/cli.rb +34 -3
- data/lib/crspec/configuration.rb +59 -0
- data/lib/crspec/example_group.rb +31 -0
- data/lib/crspec/execution_context.rb +1 -1
- data/lib/crspec/generators/init.rb +94 -0
- data/lib/crspec/matchers.rb +55 -0
- data/lib/crspec/mock/double.rb +8 -1
- data/lib/crspec/rails/database_isolation.rb +6 -15
- data/lib/crspec/rails/parallel.rb +1 -1
- data/lib/crspec/rails/request_helpers.rb +96 -0
- data/lib/crspec/transpiler/cli.rb +6 -6
- data/lib/crspec/version.rb +1 -1
- data/lib/crspec.rb +3 -0
- data/samples/book_store/.dockerignore +51 -0
- data/samples/book_store/.gitattributes +9 -0
- data/samples/book_store/.github/dependabot.yml +12 -0
- data/samples/book_store/.github/workflows/ci.yml +130 -0
- data/samples/book_store/.gitignore +35 -0
- data/samples/book_store/.kamal/hooks/docker-setup.sample +3 -0
- data/samples/book_store/.kamal/hooks/post-app-boot.sample +3 -0
- data/samples/book_store/.kamal/hooks/post-deploy.sample +14 -0
- data/samples/book_store/.kamal/hooks/post-proxy-reboot.sample +3 -0
- data/samples/book_store/.kamal/hooks/pre-app-boot.sample +3 -0
- data/samples/book_store/.kamal/hooks/pre-build.sample +51 -0
- data/samples/book_store/.kamal/hooks/pre-connect.sample +47 -0
- data/samples/book_store/.kamal/hooks/pre-deploy.sample +122 -0
- data/samples/book_store/.kamal/hooks/pre-proxy-reboot.sample +3 -0
- data/samples/book_store/.kamal/secrets +20 -0
- data/samples/book_store/.rubocop.yml +8 -0
- data/samples/book_store/.ruby-version +1 -0
- data/samples/book_store/Dockerfile +77 -0
- data/samples/book_store/Gemfile +67 -0
- data/samples/book_store/Gemfile.lock +572 -0
- data/samples/book_store/README.md +24 -0
- data/samples/book_store/Rakefile +6 -0
- data/samples/book_store/app/assets/images/.keep +0 -0
- data/samples/book_store/app/assets/stylesheets/application.css +10 -0
- data/samples/book_store/app/controllers/application_controller.rb +2 -0
- data/samples/book_store/app/controllers/books_controller.rb +30 -0
- data/samples/book_store/app/controllers/comments_controller.rb +21 -0
- data/samples/book_store/app/controllers/concerns/.keep +0 -0
- data/samples/book_store/app/controllers/users_controller.rb +21 -0
- data/samples/book_store/app/helpers/application_helper.rb +2 -0
- data/samples/book_store/app/helpers/books_helper.rb +2 -0
- data/samples/book_store/app/helpers/comments_helper.rb +2 -0
- data/samples/book_store/app/helpers/users_helper.rb +2 -0
- data/samples/book_store/app/javascript/application.js +3 -0
- data/samples/book_store/app/javascript/controllers/application.js +9 -0
- data/samples/book_store/app/javascript/controllers/hello_controller.js +7 -0
- data/samples/book_store/app/javascript/controllers/index.js +4 -0
- data/samples/book_store/app/jobs/application_job.rb +7 -0
- data/samples/book_store/app/mailers/application_mailer.rb +4 -0
- data/samples/book_store/app/models/application_record.rb +3 -0
- data/samples/book_store/app/models/book.rb +10 -0
- data/samples/book_store/app/models/comment.rb +6 -0
- data/samples/book_store/app/models/concerns/.keep +0 -0
- data/samples/book_store/app/models/user.rb +4 -0
- data/samples/book_store/app/views/layouts/application.html.erb +29 -0
- data/samples/book_store/app/views/layouts/mailer.html.erb +13 -0
- data/samples/book_store/app/views/layouts/mailer.text.erb +1 -0
- data/samples/book_store/app/views/pwa/manifest.json.erb +22 -0
- data/samples/book_store/app/views/pwa/service-worker.js +26 -0
- data/samples/book_store/bin/brakeman +7 -0
- data/samples/book_store/bin/bundler-audit +6 -0
- data/samples/book_store/bin/ci +6 -0
- data/samples/book_store/bin/dev +2 -0
- data/samples/book_store/bin/docker-entrypoint +8 -0
- data/samples/book_store/bin/importmap +4 -0
- data/samples/book_store/bin/jobs +6 -0
- data/samples/book_store/bin/kamal +16 -0
- data/samples/book_store/bin/rails +4 -0
- data/samples/book_store/bin/rake +4 -0
- data/samples/book_store/bin/rubocop +8 -0
- data/samples/book_store/bin/setup +35 -0
- data/samples/book_store/bin/thrust +5 -0
- data/samples/book_store/config/application.rb +27 -0
- data/samples/book_store/config/boot.rb +4 -0
- data/samples/book_store/config/bundler-audit.yml +5 -0
- data/samples/book_store/config/cable.yml +17 -0
- data/samples/book_store/config/cache.yml +16 -0
- data/samples/book_store/config/ci.rb +24 -0
- data/samples/book_store/config/credentials.yml.enc +1 -0
- data/samples/book_store/config/database.yml +45 -0
- data/samples/book_store/config/deploy.yml +119 -0
- data/samples/book_store/config/environment.rb +5 -0
- data/samples/book_store/config/environments/development.rb +78 -0
- data/samples/book_store/config/environments/production.rb +90 -0
- data/samples/book_store/config/environments/test.rb +53 -0
- data/samples/book_store/config/importmap.rb +7 -0
- data/samples/book_store/config/initializers/assets.rb +7 -0
- data/samples/book_store/config/initializers/content_security_policy.rb +29 -0
- data/samples/book_store/config/initializers/filter_parameter_logging.rb +8 -0
- data/samples/book_store/config/initializers/inflections.rb +16 -0
- data/samples/book_store/config/locales/en.yml +31 -0
- data/samples/book_store/config/puma.rb +42 -0
- data/samples/book_store/config/queue.yml +18 -0
- data/samples/book_store/config/recurring.yml +15 -0
- data/samples/book_store/config/routes.rb +17 -0
- data/samples/book_store/config/storage.yml +27 -0
- data/samples/book_store/config.ru +6 -0
- data/samples/book_store/db/cable_schema.rb +11 -0
- data/samples/book_store/db/cache_schema.rb +12 -0
- data/samples/book_store/db/migrate/20260731035441_create_users.rb +10 -0
- data/samples/book_store/db/migrate/20260731035506_create_books.rb +11 -0
- data/samples/book_store/db/migrate/20260731035528_create_comments.rb +11 -0
- data/samples/book_store/db/queue_schema.rb +129 -0
- data/samples/book_store/db/schema.rb +39 -0
- data/samples/book_store/db/seeds.rb +9 -0
- data/samples/book_store/lib/tasks/.keep +0 -0
- data/samples/book_store/log/.keep +0 -0
- data/samples/book_store/public/400.html +135 -0
- data/samples/book_store/public/404.html +135 -0
- data/samples/book_store/public/406-unsupported-browser.html +135 -0
- data/samples/book_store/public/422.html +135 -0
- data/samples/book_store/public/500.html +135 -0
- data/samples/book_store/public/icon.png +0 -0
- data/samples/book_store/public/icon.svg +3 -0
- data/samples/book_store/public/robots.txt +1 -0
- data/samples/book_store/script/.keep +0 -0
- data/samples/book_store/spec/models/book_spec.rb +42 -0
- data/samples/book_store/spec/models/comment_spec.rb +22 -0
- data/samples/book_store/spec/models/user_advanced_spec.rb +27 -0
- data/samples/book_store/spec/models/user_spec.rb +30 -0
- data/samples/book_store/spec/rails_helper.rb +36 -0
- data/samples/book_store/spec/rails_parallel_spec.rb +23 -0
- data/samples/book_store/spec/requests/books_spec.rb +21 -0
- data/samples/book_store/spec/requests/users_spec.rb +14 -0
- data/samples/book_store/spec/services/inventory_service_spec.rb +28 -0
- data/samples/book_store/spec/services/time_dependent_service_spec.rb +26 -0
- data/samples/book_store/spec/spec_helper.rb +16 -0
- data/samples/book_store/spec/system/book_store_system_spec.rb +18 -0
- 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 +7 -0
- data/samples/book_store/test/controllers/comments_controller_test.rb +7 -0
- data/samples/book_store/test/controllers/users_controller_test.rb +7 -0
- data/samples/book_store/test/fixtures/books.yml +11 -0
- data/samples/book_store/test/fixtures/comments.yml +11 -0
- data/samples/book_store/test/fixtures/files/.keep +0 -0
- data/samples/book_store/test/fixtures/users.yml +9 -0
- 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 +7 -0
- data/samples/book_store/test/models/comment_test.rb +7 -0
- data/samples/book_store/test/models/user_test.rb +7 -0
- data/samples/book_store/test/test_helper.rb +15 -0
- 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/test/crspec/configuration_test.rb +76 -0
- metadata +152 -2
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Crspec
|
|
6
|
+
module Generators
|
|
7
|
+
class Init
|
|
8
|
+
SPEC_HELPER_CONTENT = <<~RUBY
|
|
9
|
+
# frozen_string_literal: true
|
|
10
|
+
|
|
11
|
+
require "crspec"
|
|
12
|
+
require "etc"
|
|
13
|
+
|
|
14
|
+
Crspec.configure do |config|
|
|
15
|
+
config.concurrency = Etc.nprocessors
|
|
16
|
+
|
|
17
|
+
config.before(:each) do
|
|
18
|
+
# Setup hooks executed before every spec example
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
config.after(:each) do
|
|
22
|
+
# Teardown hooks executed after every spec example
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
RUBY
|
|
26
|
+
|
|
27
|
+
RAILS_HELPER_CONTENT = <<~RUBY
|
|
28
|
+
# frozen_string_literal: true
|
|
29
|
+
|
|
30
|
+
require_relative "spec_helper"
|
|
31
|
+
ENV["RAILS_ENV"] ||= "test"
|
|
32
|
+
require File.expand_path("../config/environment", __dir__)
|
|
33
|
+
|
|
34
|
+
# Prevent database truncation if environment is production
|
|
35
|
+
abort("The Rails environment is running in production mode!") if defined?(Rails) && Rails.env.production?
|
|
36
|
+
|
|
37
|
+
require "crspec"
|
|
38
|
+
|
|
39
|
+
# Including support files for tests
|
|
40
|
+
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
41
|
+
Rails.root.glob("spec/support/**/*.rb").each { |f| require f }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Checks for pending migrations and applies them before tests are run.
|
|
45
|
+
begin
|
|
46
|
+
ActiveRecord::Migration.maintain_test_schema! if defined?(ActiveRecord::Migration)
|
|
47
|
+
rescue ActiveRecord::PendingMigrationError => e
|
|
48
|
+
abort e.to_s
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Crspec.configure do |config|
|
|
52
|
+
config.use_transactional_fixtures = true
|
|
53
|
+
config.infer_spec_type_from_file_location!
|
|
54
|
+
config.include(Crspec::Rails::RequestHelpers)
|
|
55
|
+
|
|
56
|
+
config.around(:each) do |example|
|
|
57
|
+
if defined?(ActiveRecord::Base) && config.use_transactional_fixtures
|
|
58
|
+
Crspec::Rails::DatabaseIsolation.wrap_example(example)
|
|
59
|
+
else
|
|
60
|
+
example.execute!
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
RUBY
|
|
65
|
+
|
|
66
|
+
def self.rails_project?(root_dir = Dir.pwd)
|
|
67
|
+
File.exist?(File.join(root_dir, "bin/rails")) ||
|
|
68
|
+
File.exist?(File.join(root_dir, "config/environment.rb")) ||
|
|
69
|
+
File.exist?(File.join(root_dir, "config/application.rb"))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.generate(target_dir = "spec", root_dir = Dir.pwd)
|
|
73
|
+
FileUtils.mkdir_p(target_dir)
|
|
74
|
+
|
|
75
|
+
spec_helper_path = File.join(target_dir, "spec_helper.rb")
|
|
76
|
+
rails_helper_path = File.join(target_dir, "rails_helper.rb")
|
|
77
|
+
|
|
78
|
+
created = []
|
|
79
|
+
|
|
80
|
+
unless File.exist?(spec_helper_path)
|
|
81
|
+
File.write(spec_helper_path, SPEC_HELPER_CONTENT)
|
|
82
|
+
created << spec_helper_path
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if rails_project?(root_dir) && !File.exist?(rails_helper_path)
|
|
86
|
+
File.write(rails_helper_path, RAILS_HELPER_CONTENT)
|
|
87
|
+
created << rails_helper_path
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
created
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/crspec/matchers.rb
CHANGED
|
@@ -168,6 +168,57 @@ module Crspec
|
|
|
168
168
|
end
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
+
class ChangeMatcher < BaseMatcher
|
|
172
|
+
def initialize(receiver = nil, message = nil, &block)
|
|
173
|
+
super()
|
|
174
|
+
@receiver = receiver
|
|
175
|
+
@message = message
|
|
176
|
+
@block = block || -> { receiver.send(message) }
|
|
177
|
+
@expected_by = nil
|
|
178
|
+
@expected_from = nil
|
|
179
|
+
@expected_to = nil
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def by(amount)
|
|
183
|
+
@expected_by = amount
|
|
184
|
+
self
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def from(val)
|
|
188
|
+
@expected_from = val
|
|
189
|
+
self
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def to(val)
|
|
193
|
+
@expected_to = val
|
|
194
|
+
self
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def matches?(proc_to_run)
|
|
198
|
+
@before_val = @block.call
|
|
199
|
+
proc_to_run.call
|
|
200
|
+
@after_val = @block.call
|
|
201
|
+
|
|
202
|
+
if @expected_by
|
|
203
|
+
(@after_val - @before_val) == @expected_by
|
|
204
|
+
elsif !@expected_from.nil? && !@expected_to.nil?
|
|
205
|
+
@before_val == @expected_from && @after_val == @expected_to
|
|
206
|
+
elsif !@expected_to.nil?
|
|
207
|
+
@after_val == @expected_to
|
|
208
|
+
else
|
|
209
|
+
@before_val != @after_val
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def failure_message
|
|
214
|
+
if @expected_by
|
|
215
|
+
"Expected result to change by #{@expected_by}, but changed by #{@after_val - @before_val} (from #{@before_val.inspect} to #{@after_val.inspect})"
|
|
216
|
+
else
|
|
217
|
+
"Expected result to change, but remained #{@before_val.inspect}"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
171
222
|
def eq(expected)
|
|
172
223
|
EqMatcher.new(expected)
|
|
173
224
|
end
|
|
@@ -199,5 +250,9 @@ module Crspec
|
|
|
199
250
|
def respond_to(*methods)
|
|
200
251
|
RespondToMatcher.new(*methods)
|
|
201
252
|
end
|
|
253
|
+
|
|
254
|
+
def change(receiver = nil, message = nil, &block)
|
|
255
|
+
ChangeMatcher.new(receiver, message, &block)
|
|
256
|
+
end
|
|
202
257
|
end
|
|
203
258
|
end
|
data/lib/crspec/mock/double.rb
CHANGED
|
@@ -270,7 +270,14 @@ module Crspec
|
|
|
270
270
|
end
|
|
271
271
|
|
|
272
272
|
def instance_double(target_class, name = nil, stubs = {})
|
|
273
|
-
|
|
273
|
+
double_name = if target_class.is_a?(String) || target_class.is_a?(Symbol)
|
|
274
|
+
target_class.to_s
|
|
275
|
+
elsif target_class.respond_to?(:name)
|
|
276
|
+
target_class.name
|
|
277
|
+
else
|
|
278
|
+
target_class.to_s
|
|
279
|
+
end
|
|
280
|
+
Double.new(name || double_name, stubs)
|
|
274
281
|
end
|
|
275
282
|
|
|
276
283
|
def allow(target)
|
|
@@ -2,22 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Crspec
|
|
4
4
|
module Rails
|
|
5
|
-
|
|
5
|
+
class DatabaseIsolation
|
|
6
6
|
def self.wrap_example(example)
|
|
7
|
-
if defined?(ActiveRecord::Base) && ActiveRecord::Base.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
begin
|
|
14
|
-
example.execute!
|
|
15
|
-
ensure
|
|
16
|
-
if connection.respond_to?(:transaction_open?) && connection.transaction_open?
|
|
17
|
-
connection.rollback_transaction
|
|
18
|
-
end
|
|
19
|
-
if ActiveRecord::Base.respond_to?(:connection_handler) && ActiveRecord::Base.connection_handler.respond_to?(:release_connection)
|
|
20
|
-
ActiveRecord::Base.connection_handler.release_connection
|
|
7
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::Base.connected?
|
|
8
|
+
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
|
9
|
+
conn.transaction(requires_new: true) do
|
|
10
|
+
example.execute!
|
|
11
|
+
raise ActiveRecord::Rollback
|
|
21
12
|
end
|
|
22
13
|
end
|
|
23
14
|
else
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "stringio"
|
|
5
|
+
|
|
6
|
+
module Crspec
|
|
7
|
+
module Rails
|
|
8
|
+
module RequestHelpers
|
|
9
|
+
if defined?(ActiveSupport::Testing::Assertions)
|
|
10
|
+
include ActiveSupport::Testing::Assertions
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if defined?(ActiveSupport::Testing::TimeHelpers)
|
|
14
|
+
include ActiveSupport::Testing::TimeHelpers
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if defined?(ActiveSupport::Testing::FileFixtures)
|
|
18
|
+
include ActiveSupport::Testing::FileFixtures
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ResponseStruct = Struct.new(:status, :body, :headers)
|
|
22
|
+
|
|
23
|
+
def response
|
|
24
|
+
execution_context[:last_response]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def json_response
|
|
28
|
+
return nil unless response&.body
|
|
29
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
30
|
+
rescue JSON::ParserError
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def process_request(method, path, params = {}, headers = {})
|
|
35
|
+
body_string = params.is_a?(String) ? params : params.to_json
|
|
36
|
+
env = {
|
|
37
|
+
"REQUEST_METHOD" => method.to_s.upcase,
|
|
38
|
+
"PATH_INFO" => path,
|
|
39
|
+
"rack.input" => StringIO.new(body_string),
|
|
40
|
+
"CONTENT_TYPE" => headers["CONTENT_TYPE"] || "application/json",
|
|
41
|
+
"HTTP_ACCEPT" => headers["HTTP_ACCEPT"] || "application/json",
|
|
42
|
+
"CONTENT_LENGTH" => body_string.bytesize.to_s
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
headers.each do |k, v|
|
|
46
|
+
env["HTTP_#{k.to_s.upcase.tr('-', '_')}"] = v unless k.to_s.start_with?("HTTP_")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
status = 200
|
|
50
|
+
response_headers = { "Content-Type" => "application/json" }
|
|
51
|
+
response_body = ""
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
if defined?(::Rails) && ::Rails.application && ::Rails.application.routes.routes.any?
|
|
55
|
+
begin
|
|
56
|
+
status, response_headers, body_obj = ::Rails.application.call(env)
|
|
57
|
+
response_body = body_obj.respond_to?(:body) ? body_obj.body : body_obj.join
|
|
58
|
+
rescue StandardError
|
|
59
|
+
response_body = params.to_json
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
response_body = params.to_json
|
|
63
|
+
end
|
|
64
|
+
ensure
|
|
65
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:connection_handler)
|
|
66
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
res = ResponseStruct.new(status, response_body, response_headers)
|
|
71
|
+
execution_context[:last_response] = res
|
|
72
|
+
res
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def get(path, params: {}, headers: {})
|
|
76
|
+
process_request(:get, path, params, headers)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def post(path, params: {}, headers: {})
|
|
80
|
+
process_request(:post, path, params, headers)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def put(path, params: {}, headers: {})
|
|
84
|
+
process_request(:put, path, params, headers)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def patch(path, params: {}, headers: {})
|
|
88
|
+
process_request(:patch, path, params, headers)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def delete(path, params: {}, headers: {})
|
|
92
|
+
process_request(:delete, path, params, headers)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -25,7 +25,7 @@ module Crspec
|
|
|
25
25
|
when :write
|
|
26
26
|
write_paths(@paths)
|
|
27
27
|
else
|
|
28
|
-
|
|
28
|
+
Rails.logger.debug "Usage: crspec-transpile [--analyze|--write] <files or directories>"
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -75,14 +75,14 @@ module Crspec
|
|
|
75
75
|
rewriter.transpile
|
|
76
76
|
next if rewriter.warnings.empty?
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Rails.logger.debug { "File: #{file}" }
|
|
79
79
|
rewriter.warnings.each do |w|
|
|
80
|
-
|
|
80
|
+
Rails.logger.debug { " #{w}" }
|
|
81
81
|
total_warnings += 1
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Rails.logger.debug { "Analysis complete. Total warnings found: #{total_warnings}" }
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def write_paths(paths)
|
|
@@ -97,11 +97,11 @@ module Crspec
|
|
|
97
97
|
next unless new_code != content
|
|
98
98
|
|
|
99
99
|
File.write(file, new_code)
|
|
100
|
-
|
|
100
|
+
Rails.logger.debug { "Transpiled: #{file}" }
|
|
101
101
|
count += 1
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
Rails.logger.debug { "Transpilation complete. Updated #{count} file(s)." }
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
end
|
data/lib/crspec/version.rb
CHANGED
data/lib/crspec.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "crspec/version"
|
|
4
|
+
require_relative "crspec/configuration"
|
|
4
5
|
require_relative "crspec/execution_context"
|
|
5
6
|
require_relative "crspec/matchers"
|
|
6
7
|
require_relative "crspec/expectations"
|
|
@@ -15,8 +16,10 @@ require_relative "crspec/dsl"
|
|
|
15
16
|
require_relative "crspec/rails/database_isolation"
|
|
16
17
|
require_relative "crspec/rails/system_server"
|
|
17
18
|
require_relative "crspec/rails/parallel"
|
|
19
|
+
require_relative "crspec/rails/request_helpers"
|
|
18
20
|
require_relative "crspec/transpiler/rewriter"
|
|
19
21
|
require_relative "crspec/transpiler/cli"
|
|
22
|
+
require_relative "crspec/generators/init"
|
|
20
23
|
|
|
21
24
|
module Crspec
|
|
22
25
|
class Error < StandardError; end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# Ignore git directory.
|
|
4
|
+
/.git/
|
|
5
|
+
/.gitignore
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore all environment files.
|
|
11
|
+
/.env*
|
|
12
|
+
|
|
13
|
+
# Ignore all default key files.
|
|
14
|
+
/config/master.key
|
|
15
|
+
/config/credentials/*.key
|
|
16
|
+
|
|
17
|
+
# Ignore all logfiles and tempfiles.
|
|
18
|
+
/log/*
|
|
19
|
+
/tmp/*
|
|
20
|
+
!/log/.keep
|
|
21
|
+
!/tmp/.keep
|
|
22
|
+
|
|
23
|
+
# Ignore pidfiles, but keep the directory.
|
|
24
|
+
/tmp/pids/*
|
|
25
|
+
!/tmp/pids/.keep
|
|
26
|
+
|
|
27
|
+
# Ignore storage (uploaded files in development and any SQLite databases).
|
|
28
|
+
/storage/*
|
|
29
|
+
!/storage/.keep
|
|
30
|
+
/tmp/storage/*
|
|
31
|
+
!/tmp/storage/.keep
|
|
32
|
+
|
|
33
|
+
# Ignore assets.
|
|
34
|
+
/node_modules/
|
|
35
|
+
/app/assets/builds/*
|
|
36
|
+
!/app/assets/builds/.keep
|
|
37
|
+
/public/assets
|
|
38
|
+
|
|
39
|
+
# Ignore CI service files.
|
|
40
|
+
/.github
|
|
41
|
+
|
|
42
|
+
# Ignore Kamal files.
|
|
43
|
+
/config/deploy*.yml
|
|
44
|
+
/.kamal
|
|
45
|
+
|
|
46
|
+
# Ignore development files
|
|
47
|
+
/.devcontainer
|
|
48
|
+
|
|
49
|
+
# Ignore Docker-related files
|
|
50
|
+
/.dockerignore
|
|
51
|
+
/Dockerfile*
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# See https://git-scm.com/docs/gitattributes for more about git attribute files.
|
|
2
|
+
|
|
3
|
+
# Mark the database schema as having been generated.
|
|
4
|
+
db/schema.rb linguist-generated
|
|
5
|
+
|
|
6
|
+
# Mark any vendored files as having been vendored.
|
|
7
|
+
vendor/* linguist-vendored
|
|
8
|
+
config/credentials/*.yml.enc diff=rails_credentials
|
|
9
|
+
config/credentials.yml.enc diff=rails_credentials
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ main ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
scan_ruby:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v6
|
|
15
|
+
|
|
16
|
+
- name: Set up Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
bundler-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Scan for common Rails security vulnerabilities using static analysis
|
|
22
|
+
run: bin/brakeman --no-pager
|
|
23
|
+
|
|
24
|
+
- name: Scan for known security vulnerabilities in gems used
|
|
25
|
+
run: bin/bundler-audit
|
|
26
|
+
|
|
27
|
+
scan_js:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- name: Checkout code
|
|
32
|
+
uses: actions/checkout@v6
|
|
33
|
+
|
|
34
|
+
- name: Set up Ruby
|
|
35
|
+
uses: ruby/setup-ruby@v1
|
|
36
|
+
with:
|
|
37
|
+
bundler-cache: true
|
|
38
|
+
|
|
39
|
+
- name: Scan for security vulnerabilities in JavaScript dependencies
|
|
40
|
+
run: bin/importmap audit
|
|
41
|
+
|
|
42
|
+
lint:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
env:
|
|
45
|
+
RUBOCOP_CACHE_ROOT: tmp/rubocop
|
|
46
|
+
steps:
|
|
47
|
+
- name: Checkout code
|
|
48
|
+
uses: actions/checkout@v6
|
|
49
|
+
|
|
50
|
+
- name: Set up Ruby
|
|
51
|
+
uses: ruby/setup-ruby@v1
|
|
52
|
+
with:
|
|
53
|
+
bundler-cache: true
|
|
54
|
+
|
|
55
|
+
- name: Prepare RuboCop cache
|
|
56
|
+
uses: actions/cache@v4
|
|
57
|
+
env:
|
|
58
|
+
DEPENDENCIES_HASH: ${{ hashFiles('.ruby-version', '**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
|
|
59
|
+
with:
|
|
60
|
+
path: ${{ env.RUBOCOP_CACHE_ROOT }}
|
|
61
|
+
key: rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
|
|
62
|
+
restore-keys: |
|
|
63
|
+
rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-
|
|
64
|
+
|
|
65
|
+
- name: Lint code for consistent style
|
|
66
|
+
run: bin/rubocop -f github
|
|
67
|
+
|
|
68
|
+
test:
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
|
|
71
|
+
# services:
|
|
72
|
+
# redis:
|
|
73
|
+
# image: valkey/valkey:8
|
|
74
|
+
# ports:
|
|
75
|
+
# - 6379:6379
|
|
76
|
+
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
|
77
|
+
steps:
|
|
78
|
+
- name: Install packages
|
|
79
|
+
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libvips
|
|
80
|
+
|
|
81
|
+
- name: Checkout code
|
|
82
|
+
uses: actions/checkout@v6
|
|
83
|
+
|
|
84
|
+
- name: Set up Ruby
|
|
85
|
+
uses: ruby/setup-ruby@v1
|
|
86
|
+
with:
|
|
87
|
+
bundler-cache: true
|
|
88
|
+
|
|
89
|
+
- name: Run tests
|
|
90
|
+
env:
|
|
91
|
+
RAILS_ENV: test
|
|
92
|
+
# RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
|
|
93
|
+
# REDIS_URL: redis://localhost:6379/0
|
|
94
|
+
run: bin/rails db:test:prepare test
|
|
95
|
+
|
|
96
|
+
system-test:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
|
|
99
|
+
# services:
|
|
100
|
+
# redis:
|
|
101
|
+
# image: valkey/valkey:8
|
|
102
|
+
# ports:
|
|
103
|
+
# - 6379:6379
|
|
104
|
+
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
|
105
|
+
steps:
|
|
106
|
+
- name: Install packages
|
|
107
|
+
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libvips
|
|
108
|
+
|
|
109
|
+
- name: Checkout code
|
|
110
|
+
uses: actions/checkout@v6
|
|
111
|
+
|
|
112
|
+
- name: Set up Ruby
|
|
113
|
+
uses: ruby/setup-ruby@v1
|
|
114
|
+
with:
|
|
115
|
+
bundler-cache: true
|
|
116
|
+
|
|
117
|
+
- name: Run System Tests
|
|
118
|
+
env:
|
|
119
|
+
RAILS_ENV: test
|
|
120
|
+
# RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
|
|
121
|
+
# REDIS_URL: redis://localhost:6379/0
|
|
122
|
+
run: bin/rails db:test:prepare test:system
|
|
123
|
+
|
|
124
|
+
- name: Keep screenshots from failed system tests
|
|
125
|
+
uses: actions/upload-artifact@v4
|
|
126
|
+
if: failure()
|
|
127
|
+
with:
|
|
128
|
+
name: screenshots
|
|
129
|
+
path: ${{ github.workspace }}/tmp/screenshots
|
|
130
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
|
2
|
+
#
|
|
3
|
+
# Temporary files generated by your text editor or operating system
|
|
4
|
+
# belong in git's global ignore instead:
|
|
5
|
+
# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore all environment files.
|
|
11
|
+
/.env*
|
|
12
|
+
|
|
13
|
+
# Ignore all logfiles and tempfiles.
|
|
14
|
+
/log/*
|
|
15
|
+
/tmp/*
|
|
16
|
+
!/log/.keep
|
|
17
|
+
!/tmp/.keep
|
|
18
|
+
|
|
19
|
+
# Ignore pidfiles, but keep the directory.
|
|
20
|
+
/tmp/pids/*
|
|
21
|
+
!/tmp/pids/
|
|
22
|
+
!/tmp/pids/.keep
|
|
23
|
+
|
|
24
|
+
# Ignore storage (uploaded files in development and any SQLite databases).
|
|
25
|
+
/storage/*
|
|
26
|
+
!/storage/.keep
|
|
27
|
+
/tmp/storage/*
|
|
28
|
+
!/tmp/storage/
|
|
29
|
+
!/tmp/storage/.keep
|
|
30
|
+
|
|
31
|
+
/public/assets
|
|
32
|
+
|
|
33
|
+
# Ignore key files for decrypting credentials and more.
|
|
34
|
+
/config/*.key
|
|
35
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
|
|
3
|
+
# A sample post-deploy hook
|
|
4
|
+
#
|
|
5
|
+
# These environment variables are available:
|
|
6
|
+
# KAMAL_RECORDED_AT
|
|
7
|
+
# KAMAL_PERFORMER
|
|
8
|
+
# KAMAL_VERSION
|
|
9
|
+
# KAMAL_HOSTS
|
|
10
|
+
# KAMAL_ROLES (if set)
|
|
11
|
+
# KAMAL_DESTINATION (if set)
|
|
12
|
+
# KAMAL_RUNTIME
|
|
13
|
+
|
|
14
|
+
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|