pg_trunk 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 +7 -0
- data/.github/workflows/ci.yml +87 -0
- data/.gitignore +9 -0
- data/.rspec +4 -0
- data/.rubocop.yml +92 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +31 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +141 -0
- data/Rakefile +16 -0
- data/bin/console +8 -0
- data/bin/rake +19 -0
- data/bin/rspec +19 -0
- data/bin/setup +8 -0
- data/bin/yard +19 -0
- data/lib/pg_trunk/core/adapters/postgres.rb +80 -0
- data/lib/pg_trunk/core/dependencies_resolver.rb +101 -0
- data/lib/pg_trunk/core/generators.rb +140 -0
- data/lib/pg_trunk/core/operation/attributes.rb +78 -0
- data/lib/pg_trunk/core/operation/callbacks.rb +40 -0
- data/lib/pg_trunk/core/operation/generators.rb +51 -0
- data/lib/pg_trunk/core/operation/inversion.rb +70 -0
- data/lib/pg_trunk/core/operation/registration.rb +55 -0
- data/lib/pg_trunk/core/operation/ruby_builder.rb +112 -0
- data/lib/pg_trunk/core/operation/ruby_helpers.rb +99 -0
- data/lib/pg_trunk/core/operation/sql_helpers.rb +44 -0
- data/lib/pg_trunk/core/operation/validations.rb +21 -0
- data/lib/pg_trunk/core/operation.rb +78 -0
- data/lib/pg_trunk/core/qualified_name.rb +165 -0
- data/lib/pg_trunk/core/railtie/command_recorder.rb +30 -0
- data/lib/pg_trunk/core/railtie/custom_types.rb +37 -0
- data/lib/pg_trunk/core/railtie/migration.rb +50 -0
- data/lib/pg_trunk/core/railtie/migrator.rb +22 -0
- data/lib/pg_trunk/core/railtie/schema_dumper.rb +75 -0
- data/lib/pg_trunk/core/railtie/schema_migration.rb +22 -0
- data/lib/pg_trunk/core/railtie/statements.rb +21 -0
- data/lib/pg_trunk/core/railtie.rb +35 -0
- data/lib/pg_trunk/core/registry.rb +159 -0
- data/lib/pg_trunk/core/serializers/array_of_hashes_serializer.rb +28 -0
- data/lib/pg_trunk/core/serializers/array_of_strings_serializer.rb +29 -0
- data/lib/pg_trunk/core/serializers/array_of_symbols_serializer.rb +28 -0
- data/lib/pg_trunk/core/serializers/array_serializer.rb +22 -0
- data/lib/pg_trunk/core/serializers/lowercase_string_serializer.rb +21 -0
- data/lib/pg_trunk/core/serializers/multiline_text_serializer.rb +21 -0
- data/lib/pg_trunk/core/serializers/qualified_name_serializer.rb +27 -0
- data/lib/pg_trunk/core/serializers/symbol_serializer.rb +22 -0
- data/lib/pg_trunk/core/serializers.rb +16 -0
- data/lib/pg_trunk/core/validators/all_items_valid_validator.rb +15 -0
- data/lib/pg_trunk/core/validators/difference_validator.rb +19 -0
- data/lib/pg_trunk/core/validators.rb +10 -0
- data/lib/pg_trunk/core.rb +21 -0
- data/lib/pg_trunk/generators.rb +7 -0
- data/lib/pg_trunk/operations/check_constraints/add_check_constraint.rb +109 -0
- data/lib/pg_trunk/operations/check_constraints/base.rb +69 -0
- data/lib/pg_trunk/operations/check_constraints/drop_check_constraint.rb +60 -0
- data/lib/pg_trunk/operations/check_constraints/rename_check_constraint.rb +54 -0
- data/lib/pg_trunk/operations/check_constraints/validate_check_constraint.rb +39 -0
- data/lib/pg_trunk/operations/check_constraints.rb +14 -0
- data/lib/pg_trunk/operations/composite_types/base.rb +61 -0
- data/lib/pg_trunk/operations/composite_types/change_composite_type.rb +136 -0
- data/lib/pg_trunk/operations/composite_types/column.rb +118 -0
- data/lib/pg_trunk/operations/composite_types/create_composite_type.rb +99 -0
- data/lib/pg_trunk/operations/composite_types/drop_composite_type.rb +67 -0
- data/lib/pg_trunk/operations/composite_types/rename_composite_type.rb +44 -0
- data/lib/pg_trunk/operations/composite_types.rb +15 -0
- data/lib/pg_trunk/operations/domains/base.rb +46 -0
- data/lib/pg_trunk/operations/domains/change_domain.rb +140 -0
- data/lib/pg_trunk/operations/domains/constraint.rb +93 -0
- data/lib/pg_trunk/operations/domains/create_domain.rb +124 -0
- data/lib/pg_trunk/operations/domains/drop_domain.rb +65 -0
- data/lib/pg_trunk/operations/domains/rename_domain.rb +44 -0
- data/lib/pg_trunk/operations/domains.rb +15 -0
- data/lib/pg_trunk/operations/enums/base.rb +47 -0
- data/lib/pg_trunk/operations/enums/change.rb +55 -0
- data/lib/pg_trunk/operations/enums/change_enum.rb +119 -0
- data/lib/pg_trunk/operations/enums/create_enum.rb +83 -0
- data/lib/pg_trunk/operations/enums/drop_enum.rb +63 -0
- data/lib/pg_trunk/operations/enums/rename_enum.rb +44 -0
- data/lib/pg_trunk/operations/enums.rb +15 -0
- data/lib/pg_trunk/operations/foreign_keys/add_foreign_key.rb +174 -0
- data/lib/pg_trunk/operations/foreign_keys/base.rb +155 -0
- data/lib/pg_trunk/operations/foreign_keys/drop_foreign_key.rb +76 -0
- data/lib/pg_trunk/operations/foreign_keys/rename_foreign_key.rb +63 -0
- data/lib/pg_trunk/operations/foreign_keys.rb +16 -0
- data/lib/pg_trunk/operations/functions/base.rb +54 -0
- data/lib/pg_trunk/operations/functions/change_function.rb +108 -0
- data/lib/pg_trunk/operations/functions/create_function.rb +198 -0
- data/lib/pg_trunk/operations/functions/drop_function.rb +88 -0
- data/lib/pg_trunk/operations/functions/rename_function.rb +57 -0
- data/lib/pg_trunk/operations/functions.rb +14 -0
- data/lib/pg_trunk/operations/indexes/add_index.rb +68 -0
- data/lib/pg_trunk/operations/indexes.rb +10 -0
- data/lib/pg_trunk/operations/materialized_views/base.rb +79 -0
- data/lib/pg_trunk/operations/materialized_views/change_materialized_view.rb +139 -0
- data/lib/pg_trunk/operations/materialized_views/column.rb +94 -0
- data/lib/pg_trunk/operations/materialized_views/create_materialized_view.rb +170 -0
- data/lib/pg_trunk/operations/materialized_views/drop_materialized_view.rb +70 -0
- data/lib/pg_trunk/operations/materialized_views/refresh_materialized_view.rb +48 -0
- data/lib/pg_trunk/operations/materialized_views/rename_materialized_view.rb +61 -0
- data/lib/pg_trunk/operations/materialized_views.rb +17 -0
- data/lib/pg_trunk/operations/procedures/base.rb +42 -0
- data/lib/pg_trunk/operations/procedures/change_procedure.rb +107 -0
- data/lib/pg_trunk/operations/procedures/create_procedure.rb +146 -0
- data/lib/pg_trunk/operations/procedures/drop_procedure.rb +66 -0
- data/lib/pg_trunk/operations/procedures/rename_procedure.rb +57 -0
- data/lib/pg_trunk/operations/procedures.rb +14 -0
- data/lib/pg_trunk/operations/statistics/base.rb +94 -0
- data/lib/pg_trunk/operations/statistics/create_statistics.rb +181 -0
- data/lib/pg_trunk/operations/statistics/drop_statistics.rb +75 -0
- data/lib/pg_trunk/operations/statistics/rename_statistics.rb +48 -0
- data/lib/pg_trunk/operations/statistics.rb +13 -0
- data/lib/pg_trunk/operations/tables/create_table.rb +75 -0
- data/lib/pg_trunk/operations/tables.rb +10 -0
- data/lib/pg_trunk/operations/triggers/base.rb +119 -0
- data/lib/pg_trunk/operations/triggers/change_trigger.rb +82 -0
- data/lib/pg_trunk/operations/triggers/create_trigger.rb +208 -0
- data/lib/pg_trunk/operations/triggers/drop_trigger.rb +66 -0
- data/lib/pg_trunk/operations/triggers/rename_trigger.rb +71 -0
- data/lib/pg_trunk/operations/triggers.rb +14 -0
- data/lib/pg_trunk/operations/views/base.rb +38 -0
- data/lib/pg_trunk/operations/views/change_view.rb +90 -0
- data/lib/pg_trunk/operations/views/create_view.rb +115 -0
- data/lib/pg_trunk/operations/views/drop_view.rb +69 -0
- data/lib/pg_trunk/operations/views/rename_view.rb +58 -0
- data/lib/pg_trunk/operations/views.rb +14 -0
- data/lib/pg_trunk/operations.rb +23 -0
- data/lib/pg_trunk/version.rb +6 -0
- data/lib/pg_trunk.rb +27 -0
- data/pg_trunk.gemspec +34 -0
- data/spec/dummy/.gitignore +16 -0
- data/spec/dummy/Rakefile +15 -0
- data/spec/dummy/bin/bundle +6 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/config/application.rb +18 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/database.yml +14 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/db/materialized_views/admin_users_v01.sql +1 -0
- data/spec/dummy/db/migrate/.keep +0 -0
- data/spec/dummy/db/schema.rb +18 -0
- data/spec/dummy/db/views/admin_users_v01.sql +1 -0
- data/spec/dummy/db/views/admin_users_v02.sql +1 -0
- data/spec/operations/check_constraints/add_check_constraint_spec.rb +85 -0
- data/spec/operations/check_constraints/drop_check_constraint_spec.rb +111 -0
- data/spec/operations/check_constraints/rename_check_constraint_spec.rb +90 -0
- data/spec/operations/composite_types/change_composite_type_spec.rb +257 -0
- data/spec/operations/composite_types/create_composite_type_spec.rb +55 -0
- data/spec/operations/composite_types/drop_composite_type_spec.rb +109 -0
- data/spec/operations/composite_types/rename_composite_type_spec.rb +74 -0
- data/spec/operations/dependency_resolver_spec.rb +177 -0
- data/spec/operations/domains/change_domain_spec.rb +287 -0
- data/spec/operations/domains/create_domain_spec.rb +69 -0
- data/spec/operations/domains/drop_domain_spec.rb +119 -0
- data/spec/operations/domains/rename_domain_spec.rb +70 -0
- data/spec/operations/enums/change_enum_spec.rb +157 -0
- data/spec/operations/enums/create_enum_spec.rb +40 -0
- data/spec/operations/enums/drop_enum_spec.rb +120 -0
- data/spec/operations/enums/rename_enum_spec.rb +72 -0
- data/spec/operations/foreign_keys/add_foreign_key_spec.rb +208 -0
- data/spec/operations/foreign_keys/drop_foreign_key_spec.rb +167 -0
- data/spec/operations/foreign_keys/rename_foreign_key_spec.rb +101 -0
- data/spec/operations/functions/change_function_spec.rb +166 -0
- data/spec/operations/functions/create_function_spec.rb +192 -0
- data/spec/operations/functions/drop_function_spec.rb +182 -0
- data/spec/operations/functions/rename_function_spec.rb +101 -0
- data/spec/operations/indexes/add_index_spec.rb +94 -0
- data/spec/operations/materialized_views/change_materialized_view_spec.rb +190 -0
- data/spec/operations/materialized_views/create_materialized_view_spec.rb +144 -0
- data/spec/operations/materialized_views/drop_materialized_view_spec.rb +145 -0
- data/spec/operations/materialized_views/refresh_materialized_view_spec.rb +79 -0
- data/spec/operations/materialized_views/rename_materialized_view_spec.rb +88 -0
- data/spec/operations/procedures/change_procedure_spec.rb +175 -0
- data/spec/operations/procedures/create_procedure_spec.rb +151 -0
- data/spec/operations/procedures/drop_procedure_spec.rb +159 -0
- data/spec/operations/procedures/rename_procedure_spec.rb +107 -0
- data/spec/operations/statistics/create_statistics_spec.rb +230 -0
- data/spec/operations/statistics/drop_statistics_spec.rb +106 -0
- data/spec/operations/statistics/rename_statistics_spec.rb +129 -0
- data/spec/operations/tables/create_table_spec.rb +53 -0
- data/spec/operations/tables/rename_table_spec.rb +37 -0
- data/spec/operations/triggers/change_trigger_spec.rb +195 -0
- data/spec/operations/triggers/create_trigger_spec.rb +104 -0
- data/spec/operations/triggers/drop_trigger_spec.rb +124 -0
- data/spec/operations/triggers/rename_trigger_spec.rb +160 -0
- data/spec/operations/views/change_view_spec.rb +144 -0
- data/spec/operations/views/create_view_spec.rb +134 -0
- data/spec/operations/views/drop_view_spec.rb +146 -0
- data/spec/operations/views/rename_view_spec.rb +85 -0
- data/spec/pg_trunk/dependencies_resolver_spec.rb +43 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/migrations_helper.rb +376 -0
- metadata +348 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Helpers for migration end-to-end specs
|
|
4
|
+
module MigrationsHelper
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
# Wrap the migration containing ruby code for a `change` method
|
|
8
|
+
# into the corresponding migrator.
|
|
9
|
+
#
|
|
10
|
+
# Methods `execution` and `inversion` return blocks
|
|
11
|
+
# running the migration up or up'n'down correspondingly.
|
|
12
|
+
# As a result the instance of the class can be used as a subject
|
|
13
|
+
# of the test:
|
|
14
|
+
#
|
|
15
|
+
# subject { TestMigration.new(migration) }
|
|
16
|
+
# let(:migration) { "puts 'FOOBAR'" }
|
|
17
|
+
# its(:execution) { is_expected.not_to raise_error }
|
|
18
|
+
#
|
|
19
|
+
class TestMigration
|
|
20
|
+
private def initialize(change, args = {})
|
|
21
|
+
@change = change.to_s
|
|
22
|
+
@verbose = args[:verbose]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def execution
|
|
26
|
+
proc { run_migration(:up) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inversion
|
|
30
|
+
proc { run_migration(:up, :down) }
|
|
31
|
+
end
|
|
32
|
+
alias reversion inversion
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
MIGRATION =
|
|
37
|
+
if Rails::VERSION::MAJOR >= 5
|
|
38
|
+
::ActiveRecord::Migration[ActiveRecord::Migration.current_version]
|
|
39
|
+
else
|
|
40
|
+
::ActiveRecord::Migration
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def version
|
|
44
|
+
@version ||= (Time.now.to_f * 1e6).to_i
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def migration_klass
|
|
48
|
+
@migration_klass ||= Class.new(MIGRATION).tap do |m|
|
|
49
|
+
m.class_eval("def change;#{@change};end", __FILE__, __LINE__)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def migration
|
|
54
|
+
@migration ||= migration_klass.new("migration", version)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def run_migration(*directions)
|
|
58
|
+
return run_and_report(*directions) if @verbose
|
|
59
|
+
|
|
60
|
+
silence_stream($stdout) { run_and_report(*directions) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def run_and_report(*directions)
|
|
64
|
+
Array.wrap(directions).each do |direction|
|
|
65
|
+
ActiveRecord::Migrator
|
|
66
|
+
.new(direction, [migration], ActiveRecord::SchemaMigration, version)
|
|
67
|
+
.run
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def silence_stream(stream)
|
|
72
|
+
old_stream = stream.dup
|
|
73
|
+
stream.reopen(IO::NULL)
|
|
74
|
+
stream.sync = true
|
|
75
|
+
yield
|
|
76
|
+
ensure
|
|
77
|
+
stream.reopen(old_stream)
|
|
78
|
+
old_stream.close
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def run_migration(snippet)
|
|
83
|
+
data = self.class.metadata.slice(:verbose)
|
|
84
|
+
TestMigration.new(snippet, data).execution.call
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Default subject for migration tests
|
|
88
|
+
# It expect the migration to be specified (in a `let` clause)
|
|
89
|
+
# @return [TestMigration]
|
|
90
|
+
def subject
|
|
91
|
+
@subject ||= begin
|
|
92
|
+
raise NoMethodError, <<~MSG.squish unless respond_to?(:migration)
|
|
93
|
+
Use `let(:migration) { ... }` with a Ruby code for migration
|
|
94
|
+
MSG
|
|
95
|
+
|
|
96
|
+
data = self.class.metadata.slice(:verbose)
|
|
97
|
+
TestMigration.new(migration, data)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Read the database schema
|
|
102
|
+
# @return [String]
|
|
103
|
+
def read_schema(skip_header: false)
|
|
104
|
+
stream = StringIO.new
|
|
105
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
|
106
|
+
stream = stream.string.lines.map(&:rstrip).join("\n")
|
|
107
|
+
return stream unless skip_header
|
|
108
|
+
|
|
109
|
+
stream.lines.reject { |line| line["ActiveRecord::Schema.define"] }.join
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# expect { ... }.not_to change_schema
|
|
114
|
+
RSpec::Matchers.define :change_schema do
|
|
115
|
+
supports_block_expectations
|
|
116
|
+
|
|
117
|
+
match_when_negated do |block|
|
|
118
|
+
@expected = MigrationsHelper.read_schema(skip_header: true)
|
|
119
|
+
block.call
|
|
120
|
+
@actual = MigrationsHelper.read_schema(skip_header: true)
|
|
121
|
+
expect(@actual).to eq(@expected)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
failure_message_when_negated do
|
|
125
|
+
differ = RSpec::Support::Differ.new(color: true)
|
|
126
|
+
<<~MSG
|
|
127
|
+
It is expected the schema to remain the same, but it has been changed:
|
|
128
|
+
|
|
129
|
+
#{differ.diff_as_string(@actual, @expected).indent(2)}
|
|
130
|
+
MSG
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# expect { ... }.to insert(snippet).into_schema
|
|
135
|
+
# expect { ... }.to insert(snippet).into_schema
|
|
136
|
+
RSpec::Matchers.define :insert do |snippet|
|
|
137
|
+
supports_block_expectations
|
|
138
|
+
|
|
139
|
+
description do
|
|
140
|
+
"insert given snippet into the database schema"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
attr_reader :strict
|
|
144
|
+
|
|
145
|
+
chain(:into_schema) {} # does nothing; added for readability
|
|
146
|
+
|
|
147
|
+
match do |block|
|
|
148
|
+
@expected = snippet.lines.map(&:rstrip).join("\n") << "\n"
|
|
149
|
+
@expected = @expected.indent(2) unless @expected[/\A /]
|
|
150
|
+
|
|
151
|
+
@final = false
|
|
152
|
+
@actual = MigrationsHelper.read_schema
|
|
153
|
+
expect(@actual).not_to include(@expected)
|
|
154
|
+
|
|
155
|
+
block.call
|
|
156
|
+
|
|
157
|
+
@final = true
|
|
158
|
+
@actual = MigrationsHelper.read_schema
|
|
159
|
+
expect(@actual).to include(@expected)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
failure_message do
|
|
163
|
+
header = <<~MSG
|
|
164
|
+
It is expected the following snippet to be added to the schema:
|
|
165
|
+
|
|
166
|
+
#{@expected}
|
|
167
|
+
MSG
|
|
168
|
+
|
|
169
|
+
return <<~MSG unless @final
|
|
170
|
+
#{header.strip}
|
|
171
|
+
|
|
172
|
+
But it was present from the very beginning:
|
|
173
|
+
|
|
174
|
+
#{@actual}
|
|
175
|
+
MSG
|
|
176
|
+
|
|
177
|
+
differ = RSpec::Support::Differ.new(color: true)
|
|
178
|
+
<<~MSG
|
|
179
|
+
#{header.strip}
|
|
180
|
+
|
|
181
|
+
But the final snippet is different:
|
|
182
|
+
|
|
183
|
+
#{differ.diff_as_string(@actual, @expected).indent(2)}
|
|
184
|
+
MSG
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# expect { ... }.to remove(snippet).from_schema
|
|
189
|
+
# expect { ... }.to remove(snippet).from_schema
|
|
190
|
+
RSpec::Matchers.define :remove do |snippet|
|
|
191
|
+
supports_block_expectations
|
|
192
|
+
|
|
193
|
+
description do
|
|
194
|
+
"insert given snippet into the database schema"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
attr_reader :strict
|
|
198
|
+
|
|
199
|
+
chain(:from_schema) {} # does nothing; added for readability
|
|
200
|
+
|
|
201
|
+
match do |block|
|
|
202
|
+
@expected = snippet.lines.map(&:rstrip).join("\n") << "\n"
|
|
203
|
+
@expected = @expected.indent(2) unless @expected[/\A /]
|
|
204
|
+
|
|
205
|
+
@final = false
|
|
206
|
+
@actual = MigrationsHelper.read_schema
|
|
207
|
+
expect(@actual).to include(@expected)
|
|
208
|
+
|
|
209
|
+
block.call
|
|
210
|
+
|
|
211
|
+
@final = true
|
|
212
|
+
@actual = MigrationsHelper.read_schema
|
|
213
|
+
expect(@actual).not_to include(@expected)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
failure_message do
|
|
217
|
+
header = <<~MSG
|
|
218
|
+
It is expected the following snippet to be removed from the schema:
|
|
219
|
+
|
|
220
|
+
#{@expected}
|
|
221
|
+
MSG
|
|
222
|
+
|
|
223
|
+
return <<~MSG if @final
|
|
224
|
+
#{header}
|
|
225
|
+
|
|
226
|
+
But it is still present in the schema:
|
|
227
|
+
|
|
228
|
+
#{@actual}
|
|
229
|
+
MSG
|
|
230
|
+
|
|
231
|
+
differ = RSpec::Support::Differ.new(color: true)
|
|
232
|
+
<<~MSG
|
|
233
|
+
#{header.strip}
|
|
234
|
+
|
|
235
|
+
But the initial schema was different:
|
|
236
|
+
|
|
237
|
+
#{differ.diff_as_string(@actual, @expected).indent(2)}
|
|
238
|
+
MSG
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# expect { ... }.to enable_sql_request(query)
|
|
243
|
+
#
|
|
244
|
+
# Because we use a `transactional` strategy of the database cleaner,
|
|
245
|
+
# the whole migration is wrapped into the transaction which won't
|
|
246
|
+
# be fixed, but rolled out at the end of each spec.
|
|
247
|
+
#
|
|
248
|
+
# In this case some queries provide false negatives
|
|
249
|
+
# because they expect a DDL transaction to be fixed
|
|
250
|
+
# before running a select. For this reason we enable
|
|
251
|
+
# to ignore particular errors, treating them as positive outcomes.
|
|
252
|
+
#
|
|
253
|
+
# expect { ... }.to enable_sql_request(query).ignoring(/values/i)
|
|
254
|
+
RSpec::Matchers.define :enable_sql_request do |query|
|
|
255
|
+
supports_block_expectations
|
|
256
|
+
|
|
257
|
+
description do
|
|
258
|
+
"enable valid SQL request"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
chain(:ignoring, :pattern)
|
|
262
|
+
|
|
263
|
+
match do |block|
|
|
264
|
+
block.call
|
|
265
|
+
check = proc { ActiveRecord::Base.connection.execute(query) }
|
|
266
|
+
|
|
267
|
+
if pattern
|
|
268
|
+
expect(&check).to raise_error(pattern)
|
|
269
|
+
else
|
|
270
|
+
expect(&check).not_to raise_error
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
RSpec::Matchers.alias_matcher :reenable_sql_request, :enable_sql_request do
|
|
276
|
+
"enable SQL request as valid again"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# expect { ... }.to disable_sql_request(query)
|
|
280
|
+
RSpec::Matchers.define :disable_sql_request do |query|
|
|
281
|
+
supports_block_expectations
|
|
282
|
+
|
|
283
|
+
description do
|
|
284
|
+
"disable SQL request as invalid"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
match do |block|
|
|
288
|
+
block.call
|
|
289
|
+
expect { ActiveRecord::Base.connection.execute(query) }.to raise_error(StandardError)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# expect { ... }.to fail_validation.because_of(/name/i)
|
|
294
|
+
RSpec::Matchers.define :fail_validation do
|
|
295
|
+
chain(:because_of) { |regexp| @pattern = regexp }
|
|
296
|
+
chain(:because) { |regexp| @pattern = regexp }
|
|
297
|
+
|
|
298
|
+
attr_reader :pattern
|
|
299
|
+
|
|
300
|
+
match do |test_migration|
|
|
301
|
+
@actual = nil
|
|
302
|
+
expect { test_migration.execution.call }.to raise_error do |ex|
|
|
303
|
+
@actual = ex
|
|
304
|
+
expect(ex).to be_a(StandardError)
|
|
305
|
+
expect(ex.cause).to be_a(ActiveModel::ValidationError)
|
|
306
|
+
expect(ex.message).to match(pattern) if pattern
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
failure_message do
|
|
311
|
+
message = ["It is expected the migration to fail validation"]
|
|
312
|
+
message << "for the following reason: #{pattern}" if pattern
|
|
313
|
+
if @actual.nil?
|
|
314
|
+
message << "but it proves valid"
|
|
315
|
+
elsif @actual.cause.is_a?(ActiveModel::ValidationError)
|
|
316
|
+
message << "but the actual reason is different:"
|
|
317
|
+
message << @actual.cause.message
|
|
318
|
+
else
|
|
319
|
+
message << "but it has risen the following exception:"
|
|
320
|
+
message << @actual.inspect
|
|
321
|
+
message << @actual.backtrace.join("\n")
|
|
322
|
+
end
|
|
323
|
+
message.join("\n")
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# expect { ... }.to be_irreversible.because_of(/added values/i)
|
|
328
|
+
RSpec::Matchers.define :be_irreversible do
|
|
329
|
+
description do
|
|
330
|
+
"be irreversible migration"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
chain(:because_of) { |regexp| @pattern = regexp }
|
|
334
|
+
chain(:because) { |regexp| @pattern = regexp }
|
|
335
|
+
|
|
336
|
+
attr_reader :pattern
|
|
337
|
+
|
|
338
|
+
match do |test_migration|
|
|
339
|
+
@actual = nil
|
|
340
|
+
expect { test_migration.inversion.call }.to raise_error do |ex|
|
|
341
|
+
@actual = ex
|
|
342
|
+
expect(ex).to be_a(StandardError)
|
|
343
|
+
expect(ex.cause).to be_a(ActiveRecord::IrreversibleMigration)
|
|
344
|
+
next unless pattern
|
|
345
|
+
|
|
346
|
+
expect(ex.message).to match(pattern)
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
failure_message do
|
|
351
|
+
message = ["It is expected the migration to be irreversible"]
|
|
352
|
+
message << "for the following reason: #{pattern}" if pattern
|
|
353
|
+
|
|
354
|
+
if @actual.nil?
|
|
355
|
+
message << "but it proves reversible"
|
|
356
|
+
elsif @actual.cause.is_a?(ActiveRecord::IrreversibleMigration)
|
|
357
|
+
message << "but the actual reason is different:"
|
|
358
|
+
message << @actual.message
|
|
359
|
+
else
|
|
360
|
+
message << "but it has risen the following exception:"
|
|
361
|
+
message << @actual.inspect
|
|
362
|
+
end
|
|
363
|
+
message.join("\n")
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
RSpec.configure do |config|
|
|
368
|
+
migration = { described_class: ActiveRecord::Migration }
|
|
369
|
+
config.include MigrationsHelper, **migration
|
|
370
|
+
config.around(:each, **migration) do |example|
|
|
371
|
+
DatabaseCleaner.start
|
|
372
|
+
ActiveRecord::SchemaMigration.create_table
|
|
373
|
+
example.run
|
|
374
|
+
DatabaseCleaner.clean
|
|
375
|
+
end
|
|
376
|
+
end
|