declare_schema 1.4.0.colin.7 → 1.4.0.colin.9

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.
@@ -8,19 +8,20 @@ end
8
8
  require_relative '../../../../lib/declare_schema/model/habtm_model_shim'
9
9
 
10
10
  RSpec.describe DeclareSchema::Model::HabtmModelShim do
11
- let(:join_table) { "parent_1_parent_2" }
12
- let(:foreign_keys) { ["parent_1_id", "parent_2_id"] }
13
- let(:foreign_key_classes) { [Parent1, Parent2] }
11
+ let(:join_table) { "customers_users" }
12
+ let(:foreign_keys) { ["user_id", "customer_id"] }
13
+ let(:parent_table_names) { ["users", "customers"] }
14
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class, "connection") }
14
15
 
15
16
  before do
16
17
  load File.expand_path('../prepare_testapp.rb', __dir__)
17
18
 
18
- class Parent1 < ActiveRecord::Base
19
- self.table_name = "parent_1s"
19
+ class User < ActiveRecord::Base
20
+ self.table_name = "users"
20
21
  end
21
22
 
22
- class Parent2 < ActiveRecord::Base
23
- self.table_name = "parent_2s"
23
+ class Customer < ActiveRecord::Base
24
+ self.table_name = "customers"
24
25
  end
25
26
  end
26
27
 
@@ -29,26 +30,33 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
29
30
  let(:reflection) { double("reflection", join_table: join_table,
30
31
  foreign_key: foreign_keys.first,
31
32
  association_foreign_key: foreign_keys.last,
32
- active_record: foreign_key_classes.first,
33
- class_name: 'Parent1') }
33
+ active_record: User,
34
+ class_name: 'Customer',
35
+ klass: Customer) }
34
36
  it 'returns a new object' do
37
+ expect(User).to receive(:connection).and_return(connection)
38
+
35
39
  result = described_class.from_reflection(reflection)
36
40
 
37
41
  expect(result).to be_a(described_class)
42
+ expect(result.foreign_keys).to eq(foreign_keys.reverse)
43
+ expect(result.parent_table_names).to eq(parent_table_names.reverse)
38
44
  end
39
45
  end
40
46
  end
41
47
 
42
48
  describe 'instance methods' do
43
- let(:connection) { instance_double(ActiveRecord::Base.connection.class, "connection") }
44
-
45
- subject { described_class.new(join_table, foreign_keys, foreign_key_classes, connection) }
49
+ subject { described_class.new(join_table, foreign_keys, parent_table_names, connection: connection) }
46
50
 
47
51
  describe '#initialize' do
48
52
  it 'stores initialization attributes' do
49
53
  expect(subject.join_table).to eq(join_table)
50
- expect(subject.foreign_keys).to eq(foreign_keys)
51
- expect(subject.foreign_key_classes).to be(foreign_key_classes)
54
+ expect(subject.foreign_keys).to eq(foreign_keys.reverse)
55
+ end
56
+ end
57
+
58
+ describe '#connection' do
59
+ it 'returns the connection' do
52
60
  expect(subject.connection).to be(connection)
53
61
  end
54
62
  end
@@ -67,51 +75,52 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
67
75
 
68
76
  describe '#field_specs' do
69
77
  it 'returns 2 field specs' do
70
- result = subject.field_specs
71
- expect(result.size).to eq(2), result.inspect
72
-
73
- expect(result[foreign_keys.first]).to be_a(::DeclareSchema::Model::FieldSpec)
74
- expect(result[foreign_keys.first].model).to eq(subject)
75
- expect(result[foreign_keys.first].name.to_s).to eq(foreign_keys.first)
76
- expect(result[foreign_keys.first].type).to eq(:integer)
77
- expect(result[foreign_keys.first].position).to eq(0)
78
-
79
- expect(result[foreign_keys.last]).to be_a(::DeclareSchema::Model::FieldSpec)
80
- expect(result[foreign_keys.last].model).to eq(subject)
81
- expect(result[foreign_keys.last].name.to_s).to eq(foreign_keys.last)
82
- expect(result[foreign_keys.last].type).to eq(:integer)
83
- expect(result[foreign_keys.last].position).to eq(1)
78
+ field_specs = subject.field_specs
79
+ expect(field_specs.size).to eq(2), field_specs.inspect
80
+
81
+ expect(field_specs[foreign_keys.first]).to be_a(::DeclareSchema::Model::FieldSpec)
82
+ expect(field_specs[foreign_keys.first].model).to eq(subject)
83
+ expect(field_specs[foreign_keys.first].name.to_s).to eq(foreign_keys.first)
84
+ expect(field_specs[foreign_keys.first].type).to eq(:integer)
85
+ expect(field_specs[foreign_keys.first].position).to eq(1)
86
+
87
+ expect(field_specs[foreign_keys.last]).to be_a(::DeclareSchema::Model::FieldSpec)
88
+ expect(field_specs[foreign_keys.last].model).to eq(subject)
89
+ expect(field_specs[foreign_keys.last].name.to_s).to eq(foreign_keys.last)
90
+ expect(field_specs[foreign_keys.last].type).to eq(:integer)
91
+ expect(field_specs[foreign_keys.last].position).to eq(0)
84
92
  end
85
93
  end
86
94
 
87
95
  describe '#primary_key' do
88
- it 'returns false' do
89
- expect(subject._declared_primary_key).to eq(false)
96
+ it 'returns false because there is no single-column PK for ActiveRecord to use' do
97
+ expect(subject.primary_key).to eq(false)
90
98
  end
91
99
  end
92
100
 
93
101
  describe '#_declared_primary_key' do
94
- it 'returns false' do
95
- expect(subject._declared_primary_key).to eq(false)
102
+ it 'returns the foreign key pair that are used as the primary key in the database' do
103
+ expect(subject._declared_primary_key).to eq(["customer_id", "user_id"])
96
104
  end
97
105
  end
98
106
 
99
107
  describe '#index_definitions_with_primary_key' do
100
108
  it 'returns 2 index definitions' do
101
- result = subject.index_definitions_with_primary_key
102
- expect(result.size).to eq(2), result.inspect
109
+ index_definitions = subject.index_definitions_with_primary_key
110
+ expect(index_definitions.size).to eq(2), index_definitions.inspect
103
111
 
104
- expect(result.first).to be_a(::DeclareSchema::Model::IndexDefinition)
105
- expect(result.first.name).to eq('PRIMARY')
106
- expect(result.first.fields).to eq(['parent_1_id', 'parent_2_id'])
107
- expect(result.first.unique).to be_truthy
112
+ expect(index_definitions.last).to be_a(::DeclareSchema::Model::IndexDefinition)
113
+ expect(index_definitions.last.name).to eq('PRIMARY')
114
+ expect(index_definitions.last.fields).to eq(foreign_keys.reverse)
115
+ expect(index_definitions.last.unique).to be_truthy
108
116
  end
109
117
  end
110
118
 
111
119
  context 'when table and foreign key names are long' do
112
120
  let(:join_table) { "advertiser_campaigns_tracking_pixels" }
113
- let(:foreign_keys) { ['advertiser_campaign', 'tracking_pixel'] }
114
- let(:foreign_key_classes) { [Table1, Table2] }
121
+ let(:foreign_keys_and_table_names) { [["advertiser_id", "advertisers"], ["campaign_id", "campaigns"]] }
122
+ let(:foreign_keys) { foreign_keys_and_table_names.map(&:first) }
123
+ let(:parent_table_names) { foreign_keys_and_table_names.map(&:last) }
115
124
 
116
125
  before do
117
126
  class Table1 < ActiveRecord::Base
@@ -124,19 +133,40 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
124
133
  end
125
134
 
126
135
  it 'returns two index definitions and does not raise a IndexNameTooLongError' do
127
- result = subject.index_definitions_with_primary_key
128
- expect(result.size).to eq(2), result.inspect
129
- expect(result.first).to be_a(::DeclareSchema::Model::IndexDefinition)
130
- expect(result.first.name).to eq('PRIMARY')
131
- expect(result.first.fields).to eq(['advertiser_campaign', 'tracking_pixel'])
132
- expect(result.first.unique).to be_truthy
136
+ indexes = subject.index_definitions_with_primary_key.to_a
137
+ expect(indexes.size).to eq(2), indexes.inspect
138
+ expect(indexes.last).to be_a(::DeclareSchema::Model::IndexDefinition)
139
+ expect(indexes.last.name).to eq('PRIMARY')
140
+ expect(indexes.last.fields).to eq(foreign_keys)
141
+ expect(indexes.last.unique).to be_truthy
142
+ expect(indexes.first).to be_a(::DeclareSchema::Model::IndexDefinition)
143
+ expect(indexes.first.name).to eq('index_advertiser_campaigns_tracking_pixels_on_campaign_id')
144
+ expect(indexes.first.fields).to eq([foreign_keys.last])
145
+ expect(indexes.first.unique).to be_falsey
133
146
  end
134
147
  end
135
148
 
136
149
  describe '#index_definitions' do
150
+ it 'returns index_definitions' do
151
+ indexes = subject.index_definitions
152
+ expect(indexes.size).to eq(1), indexes.inspect
153
+ expect(indexes.first.columns).to eq(["user_id"])
154
+ options = [:name, :unique, :where].map { |k| [k, indexes.first.send(k)] }.to_h
155
+ expect(options).to eq(name: "index_customers_users_on_user_id",
156
+ unique: false,
157
+ where: nil)
158
+ end
159
+ end
160
+
161
+ describe '#index_definitions_with_primary_key' do
137
162
  it 'returns index_definitions_with_primary_key' do
138
- result = subject.index_definitions
139
- expect(result.size).to eq(2), result.inspect
163
+ indexes = subject.index_definitions_with_primary_key
164
+ expect(indexes.size).to eq(2), indexes.inspect
165
+ expect(indexes.last.columns).to eq(["customer_id", "user_id"])
166
+ options = [:name, :unique, :where].map { |k| [k, indexes.last.send(k)] }.to_h
167
+ expect(options).to eq(name: "PRIMARY",
168
+ unique: true,
169
+ where: nil)
140
170
  end
141
171
  end
142
172
 
@@ -146,21 +176,13 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
146
176
  end
147
177
  end
148
178
 
149
- describe '#constraint_specs' do
179
+ describe '#constraint_definitions' do
150
180
  it 'returns 2 foreign keys' do
151
- result = subject.constraint_specs
152
- expect(result.size).to eq(2), result.inspect
153
-
154
- expect(result.first).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
155
- expect(result.first.foreign_key).to eq(foreign_keys.first)
156
- expect(result.first.parent_table_name).to be(Parent1.table_name)
157
- expect(result.first.on_delete_cascade).to be_truthy
158
-
159
- sample = result.to_a.last
160
- expect(sample).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
161
- expect(sample.foreign_key).to eq(foreign_keys.last)
162
- expect(sample.parent_table_name).to be(Parent2.table_name)
163
- expect(sample.on_delete_cascade).to be_truthy
181
+ constraints = subject.constraint_definitions
182
+ expect(constraints.map(&:key)).to eq([
183
+ ["customers_users", "customer_id", :delete],
184
+ ["customers_users", "user_id", :delete]
185
+ ])
164
186
  end
165
187
  end
166
188
  end
@@ -11,6 +11,7 @@ require_relative '../../../../lib/declare_schema/model/index_definition'
11
11
 
12
12
  RSpec.describe DeclareSchema::Model::IndexDefinition do
13
13
  let(:model_class) { IndexDefinitionTestModel }
14
+ let(:table_name) { model_class.table_name }
14
15
 
15
16
  context 'Using declare_schema' do
16
17
  before do
@@ -34,23 +35,19 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
34
35
  end
35
36
  end
36
37
 
38
+ # TODO: create model_spec.rb and move the Model specs below into it. -Colin
37
39
  describe 'instance methods' do
38
40
  let(:model) { model_class.new }
41
+ let(:table_name) { "index_definition_test_models" }
39
42
  let(:fields) { ['last_name', 'first_name'] }
40
- let(:options) { {} }
41
- subject(:instance) { described_class.new(model_class, fields, **options) }
43
+ let(:options) { { table_name: table_name } }
44
+ subject(:instance) { described_class.new(fields, **options) }
42
45
 
43
46
  describe 'attr_readers' do
44
- describe '#table' do
45
- subject { instance.table }
47
+ describe '#table_name' do
48
+ subject { instance.table_name }
46
49
 
47
- it { is_expected.to eq(model_class.table_name) }
48
-
49
- context 'with table_name option' do
50
- let(:options) { { table_name: 'auth_users' } }
51
-
52
- it { is_expected.to eq('auth_users') }
53
- end
50
+ it { is_expected.to eq(table_name) }
54
51
  end
55
52
 
56
53
  describe '#fields' do
@@ -62,27 +59,32 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
62
59
  describe '#explicit_name' do
63
60
  subject { instance.explicit_name }
64
61
 
65
- it { is_expected.to eq(nil) }
62
+ context 'with allow_equivalent' do
63
+ let(:options) { { table_name: table_name, allow_equivalent: true } }
64
+
65
+ it { is_expected.to eq(nil) }
66
+ end
66
67
 
67
68
  context 'with name option' do
68
- let(:options) { { name: 'index_auth_users_on_last_name_and_first_name' } }
69
+ let(:options) { { table_name: table_name, name: 'index_auth_users_on_names' } }
69
70
 
70
- it { is_expected.to eq('index_auth_users_on_last_name_and_first_name') }
71
+ it { is_expected.to eq('index_auth_users_on_names') }
71
72
  end
72
73
  end
73
74
 
74
75
  describe '#length' do
75
76
  subject { instance.length }
76
- let(:options) { { length: length } }
77
+ let(:options) { { table_name: table_name, length: length } }
77
78
 
78
79
  context 'with integer length' do
80
+ let(:fields) { ['last_name'] }
79
81
  let(:length) { 2 }
80
82
 
81
- it { is_expected.to eq(length) }
83
+ it { is_expected.to eq(last_name: 2) }
82
84
  end
83
85
 
84
86
  context 'with Hash length' do
85
- let(:length) { { name: 2 } }
87
+ let(:length) { { first_name: 2 } }
86
88
 
87
89
  it { is_expected.to eq(length) }
88
90
  end
@@ -90,9 +92,9 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
90
92
 
91
93
  describe '#options' do
92
94
  subject { instance.options }
93
- let(:options) { { name: 'my_index', unique: false, where: "(name like 'a%')", length: 10 } }
95
+ let(:options) { { name: 'my_index', table_name: table_name, unique: false, where: "(last_name like 'a%')", length: { last_name: 10, first_name: 5 } } }
94
96
 
95
- it { is_expected.to eq(options) }
97
+ it { is_expected.to eq(options.except(:table_name)) }
96
98
  end
97
99
 
98
100
  describe '#with_name' do
@@ -105,33 +107,25 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
105
107
  end
106
108
  end
107
109
 
108
- describe 'class methods' do
109
- let(:model) { model_class.new }
110
-
111
- describe 'index_definitions' do
112
- it do
113
- expect(model_class.index_definitions.size).to eq(1)
110
+ describe 'Model class methods' do
111
+ describe '.has index_definitions' do
112
+ subject { model_class.index_definitions }
114
113
 
115
- sample = model_class.index_definitions.to_a.first
116
- expect(sample.name).to eq('index_index_definition_test_models_on_name')
117
- expect(sample.fields).to eq(['name'])
118
- expect(sample.unique).to eq(false)
114
+ it 'returns indexes without primary key' do
115
+ expect(subject.map(&:to_key)).to eq([
116
+ ['index_index_definition_test_models_on_name', ['name'], { length: nil, unique: false, where: nil }],
117
+ ])
119
118
  end
120
119
  end
121
120
 
122
- describe 'has index_definitions_with_primary_key' do
123
- it do
124
- expect(model_class.index_definitions_with_primary_key).to be_kind_of(Set)
125
- result = model_class.index_definitions_with_primary_key.sort_by(&:name)
126
- expect(result.size).to eq(2)
127
-
128
- expect(result[0].name).to eq('PRIMARY')
129
- expect(result[0].fields).to eq(['id'])
130
- expect(result[0].unique).to eq(true)
121
+ describe '.has index_definitions_with_primary_key' do
122
+ subject { model_class.index_definitions_with_primary_key }
131
123
 
132
- expect(result[1].name).to eq('index_index_definition_test_models_on_name')
133
- expect(result[1].fields).to eq(['name'])
134
- expect(result[1].unique).to eq(false)
124
+ it 'returns indexes with primary key' do
125
+ expect(subject.map(&:to_key)).to eq([
126
+ ['index_index_definition_test_models_on_name', ['name'], { length: nil, unique: false, where: nil }],
127
+ ['PRIMARY', ['id'], { length: nil, unique: true, where: nil }],
128
+ ])
135
129
  end
136
130
  end
137
131
  end
@@ -147,6 +141,11 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
147
141
  ActiveRecord::Base.connection.execute <<~EOS
148
142
  CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
149
143
  EOS
144
+ if defined?(Mysql2)
145
+ ActiveRecord::Base.connection.execute <<~EOS
146
+ CREATE INDEX index_definition_test_models_on_name_partial ON index_definition_test_models(name(10))
147
+ EOS
148
+ end
150
149
  ActiveRecord::Base.connection.execute <<~EOS
151
150
  CREATE TABLE index_definition_compound_index_models (
152
151
  fk1_id INTEGER NOT NULL,
@@ -157,29 +156,38 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
157
156
  ActiveRecord::Base.connection.schema_cache.clear!
158
157
  end
159
158
 
160
- describe 'for_model' do
161
- subject { described_class.for_model(model_class) }
159
+ describe 'for_table' do
160
+ let(:ignore_indexes) { model_class.ignore_indexes }
161
+ subject { described_class.for_table(model_class.table_name, ignore_indexes, model_class.connection) }
162
162
 
163
163
  context 'with single-column PK' do
164
164
  it 'returns the indexes for the model' do
165
- expect(subject.size).to eq(2), subject.inspect
166
- expect(subject[0].name).to eq('index_definition_test_models_on_name')
167
- expect(subject[0].columns).to eq(['name'])
168
- expect(subject[0].unique).to eq(true)
169
- expect(subject[1].name).to eq('PRIMARY')
170
- expect(subject[1].columns).to eq(['id'])
171
- expect(subject[1].unique).to eq(true)
165
+ expect(subject.map(&:to_key)).to eq([
166
+ ["index_definition_test_models_on_name", ["name"], { unique: true, where: nil, length: nil }],
167
+ (["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: { name: 10 } }] if defined?(Mysql2)),
168
+ ["PRIMARY", ["id"], { unique: true, where: nil, length: nil }]
169
+ ].compact)
172
170
  end
173
171
  end
174
172
 
175
- context 'with compound-column PK' do
173
+ context 'with composite (multi-column) PK' do
176
174
  let(:model_class) { IndexDefinitionCompoundIndexModel }
177
175
 
178
176
  it 'returns the indexes for the model' do
179
- expect(subject.size).to eq(1), subject.inspect
180
- expect(subject[0].name).to eq('PRIMARY')
181
- expect(subject[0].columns).to eq(['fk1_id', 'fk2_id'])
182
- expect(subject[0].unique).to eq(true)
177
+ expect(subject.map(&:to_key)).to eq([
178
+ ["PRIMARY", ["fk1_id", "fk2_id"], { length: nil, unique: true, where: nil }]
179
+ ])
180
+ end
181
+ end
182
+
183
+ context 'with ignored_indexes' do
184
+ let(:ignore_indexes) { ['index_definition_test_models_on_name'] }
185
+
186
+ it 'skips the ignored index' do
187
+ expect(subject.map(&:to_key)).to eq([
188
+ (["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: { name: 10 } }] if defined?(Mysql2)),
189
+ ["PRIMARY", ["id"], { length: nil, unique: true, where: nil }]
190
+ ].compact)
183
191
  end
184
192
  end
185
193
  end
@@ -252,6 +260,69 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
252
260
  end
253
261
  end
254
262
  end
263
+
264
+ describe '.normalize_index_length' do
265
+ let(:columns) { [:last_name] }
266
+ subject { described_class.normalize_index_length(length, columns: columns) }
267
+
268
+ context 'with nil length' do
269
+ let(:length) { nil }
270
+
271
+ it { is_expected.to eq(nil) }
272
+ end
273
+
274
+ context 'when Integer' do
275
+ let(:length) { 10 }
276
+
277
+ it { is_expected.to eq(last_name: length) }
278
+
279
+ context 'with multiple columns' do
280
+ let(:columns) { ["last_name", "first_name"] }
281
+
282
+ it { expect { subject }.to raise_exception(ArgumentError, /Index length of Integer only allowed when exactly one column; got 10 for \["last_name", "first_name"]/i) }
283
+ end
284
+ end
285
+
286
+ context 'when empty Hash' do
287
+ let(:length) { {} }
288
+
289
+ it { is_expected.to eq(nil) }
290
+ end
291
+
292
+ context 'when Hash' do
293
+ let(:length) { { last_name: 10 } }
294
+
295
+ it { is_expected.to eq(length) }
296
+ end
297
+
298
+ context 'when Hash with String key' do
299
+ let(:length) { { "last_name" => 10 } }
300
+
301
+ it { is_expected.to eq(last_name: 10) }
302
+ end
303
+
304
+ context 'with multiple columns' do
305
+ let(:columns) { [:last_name, :first_name] }
306
+
307
+ context 'when Hash with String keys' do
308
+ let(:length) { { "last_name" => 10, "first_name" => 5 } }
309
+
310
+ it { is_expected.to eq(last_name: 10, first_name: 5) }
311
+ end
312
+ end
313
+
314
+ context 'with nil length' do
315
+ let(:length) { nil }
316
+
317
+ it { is_expected.to eq(nil) }
318
+ end
319
+
320
+ context 'with an invalid length' do
321
+ let(:length) { 10.5 }
322
+
323
+ it { expect { subject }.to raise_exception(ArgumentError, /length must be nil or Integer or a Hash of column names to lengths; got 10\.5 for \[:last_name]/i) }
324
+ end
325
+ end
255
326
  end
256
327
  # TODO: fill out remaining tests
257
328
  end
@@ -9,6 +9,8 @@ require_relative '../../../../lib/declare_schema/model/table_options_definition'
9
9
 
10
10
  RSpec.describe DeclareSchema::Model::TableOptionsDefinition do
11
11
  let(:model_class) { TableOptionsDefinitionTestModel }
12
+ let(:charset) { DeclareSchema.normalize_charset('utf8') }
13
+ let(:collation) { DeclareSchema.normalize_collation('utf8_general') } # adapt so that tests will pass on MySQL 5.7 or 8+
12
14
 
13
15
  context 'Using declare_schema' do
14
16
  before do
@@ -27,27 +29,45 @@ RSpec.describe DeclareSchema::Model::TableOptionsDefinition do
27
29
 
28
30
  describe '#to_key' do
29
31
  subject { model.to_key }
30
- it { should eq(['table_options_definition_test_models', '{:charset=>"utf8", :collation=>"utf8_general"}']) }
32
+ it { is_expected.to eq(['table_options_definition_test_models', "{:charset=>#{charset.inspect}, :collation=>#{collation.inspect}}"]) }
31
33
  end
32
34
 
33
35
  describe '#settings' do
34
36
  subject { model.settings }
35
- it { should eq("CHARACTER SET utf8 COLLATE utf8_general") }
37
+ it { is_expected.to eq("CHARACTER SET #{charset} COLLATE #{collation}") }
38
+
39
+ if defined?(Mysql2)
40
+ context 'when running in MySQL 8' do
41
+ around do |spec|
42
+ DeclareSchema.mysql_version = Gem::Version.new('8.0.21')
43
+ spec.run
44
+ ensure
45
+ DeclareSchema.remove_instance_variable('@mysql_version') rescue nil
46
+ end
47
+
48
+ it { is_expected.to eq("CHARACTER SET utf8mb3 COLLATE utf8mb3_general") }
49
+
50
+ context 'when _ci collation' do
51
+ let(:table_options) { { charset: "utf8", collation: "utf8_general_ci"} }
52
+ it { is_expected.to eq("CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci") }
53
+ end
54
+ end
55
+ end
36
56
  end
37
57
 
38
58
  describe '#hash' do
39
59
  subject { model.hash }
40
- it { should eq(['table_options_definition_test_models', '{:charset=>"utf8", :collation=>"utf8_general"}'].hash) }
60
+ it { is_expected.to eq(['table_options_definition_test_models', "{:charset=>#{charset.inspect}, :collation=>#{collation.inspect}}"].hash) }
41
61
  end
42
62
 
43
63
  describe '#to_s' do
44
64
  subject { model.to_s }
45
- it { should eq("CHARACTER SET utf8 COLLATE utf8_general") }
65
+ it { is_expected.to eq("CHARACTER SET #{charset} COLLATE #{collation}") }
46
66
  end
47
67
 
48
68
  describe '#alter_table_statement' do
49
69
  subject { model.alter_table_statement }
50
- it { should match(/execute "ALTER TABLE .*table_options_definition_test_models.* CHARACTER SET utf8 COLLATE utf8_general"/) }
70
+ it { is_expected.to match(/execute "ALTER TABLE .*table_options_definition_test_models.* CHARACTER SET #{charset} COLLATE #{collation}"/) }
51
71
  end
52
72
  end
53
73
 
@@ -67,7 +87,7 @@ RSpec.describe DeclareSchema::Model::TableOptionsDefinition do
67
87
  generate_migrations '-n', '-m'
68
88
  end
69
89
 
70
- it { should eq(described_class.new(model_class.table_name, **options)) }
90
+ it { is_expected.to eq(described_class.new(model_class.table_name, **options)) }
71
91
  end
72
92
  end
73
93
  end
@@ -8,10 +8,34 @@ RSpec.describe DeclareSchema do
8
8
  it { is_expected.to eq("utf8mb4") }
9
9
  end
10
10
 
11
- context 'when explicitly set' do
12
- before { described_class.default_charset = "utf8" }
13
- after { described_class.default_charset = "utf8mb4" }
14
- it { is_expected.to eq("utf8") }
11
+ context 'when running on MySQL 5.7' do
12
+ around do |spec|
13
+ described_class.mysql_version = Gem::Version.new('5.7.48')
14
+ spec.run
15
+ ensure
16
+ described_class.remove_instance_variable('@mysql_version') rescue nil
17
+ end
18
+
19
+ context 'when explicitly set' do
20
+ before { described_class.default_charset = "utf8" }
21
+ after { described_class.default_charset = "utf8mb4" }
22
+ it { is_expected.to eq("utf8") }
23
+ end
24
+ end
25
+
26
+ context 'when running on MySQL 8.0' do
27
+ around do |spec|
28
+ described_class.mysql_version = Gem::Version.new('8.0.21')
29
+ spec.run
30
+ ensure
31
+ described_class.remove_instance_variable('@mysql_version') rescue nil
32
+ end
33
+
34
+ context 'when explicitly set' do
35
+ before { described_class.default_charset = "utf8" }
36
+ after { described_class.default_charset = "utf8mb4" }
37
+ it { is_expected.to eq("utf8mb3") }
38
+ end
15
39
  end
16
40
  end
17
41
 
@@ -22,10 +46,40 @@ RSpec.describe DeclareSchema do
22
46
  it { is_expected.to eq("utf8mb4_bin") }
23
47
  end
24
48
 
25
- context 'when explicitly set' do
26
- before { described_class.default_collation = "utf8mb4_general_ci" }
27
- after { described_class.default_collation = "utf8mb4_bin" }
28
- it { is_expected.to eq("utf8mb4_general_ci") }
49
+ context 'when running on MySQL 5.7' do
50
+ around do |spec|
51
+ described_class.mysql_version = Gem::Version.new('5.7.48')
52
+ spec.run
53
+ ensure
54
+ described_class.remove_instance_variable('@mysql_version')
55
+ end
56
+
57
+ context 'when explicitly set' do
58
+ before { described_class.default_collation = "utf8_general_ci" }
59
+ after { described_class.default_collation = "utf8mb4_bin" }
60
+ it { is_expected.to eq("utf8_general_ci") }
61
+ end
62
+ end
63
+
64
+ context 'when running on MySQL 8.0' do
65
+ around do |spec|
66
+ described_class.mysql_version = Gem::Version.new('8.0.21')
67
+ spec.run
68
+ ensure
69
+ described_class.remove_instance_variable('@mysql_version')
70
+ end
71
+
72
+ context 'when explicitly set without _ci' do
73
+ before { described_class.default_collation = "utf8_general" }
74
+ after { described_class.default_collation = "utf8mb4_bin" }
75
+ it { is_expected.to eq("utf8mb3_general") }
76
+ end
77
+
78
+ context 'when explicitly set with _ci' do
79
+ before { described_class.default_collation = "utf8_general_ci" }
80
+ after { described_class.default_collation = "utf8mb4_bin" }
81
+ it { is_expected.to eq("utf8mb3_general_ci") }
82
+ end
29
83
  end
30
84
  end
31
85
 
@@ -12,6 +12,8 @@ module Generators
12
12
  module Migration
13
13
  RSpec.describe Migrator do
14
14
  subject { described_class.new }
15
+ let(:charset) { ::DeclareSchema.normalize_charset('utf8') }
16
+ let(:collation) { ::DeclareSchema.normalize_collation('utf8_general') } # adapt so that tests will pass on MySQL 5.7 or 8+
15
17
 
16
18
  describe '#before_generating_migration' do
17
19
  it 'requires a block be passed' do
@@ -29,7 +31,7 @@ module Generators
29
31
  context 'when explicitly set' do
30
32
  before { described_class.default_charset = "utf8" }
31
33
  after { described_class.default_charset = "utf8mb4" }
32
- it { should eq("utf8") }
34
+ it { should eq(charset) }
33
35
  end
34
36
 
35
37
  it 'should output deprecation warning' do