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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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