schema_expectations 0.0.1 → 0.2.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 +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +19 -5
- data/Appraisals +33 -0
- data/CHANGELOG.md +17 -1
- data/README.md +4 -0
- data/Rakefile +12 -0
- data/gemfiles/activerecord_3.1.gemfile +7 -0
- data/gemfiles/activerecord_3.2.gemfile +7 -0
- data/gemfiles/activerecord_4.0.gemfile +7 -0
- data/gemfiles/activerecord_4.1.gemfile +7 -0
- data/gemfiles/activerecord_4.2.gemfile +7 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/rspec_3.0.gemfile +7 -0
- data/gemfiles/rspec_3.1.gemfile +7 -0
- data/gemfiles/rspec_3.2.gemfile +7 -0
- data/lib/schema_expectations/active_record/column_reflector.rb +92 -0
- data/lib/schema_expectations/active_record/validation_reflector.rb +59 -0
- data/lib/schema_expectations/config.rb +25 -0
- data/lib/schema_expectations/rspec_matchers/validate_schema_nullable.rb +49 -58
- data/lib/schema_expectations/util.rb +9 -0
- data/lib/schema_expectations/version.rb +1 -1
- data/lib/schema_expectations.rb +1 -0
- data/schema_expectations.gemspec +12 -3
- data/spec/db/database.yml +13 -0
- data/spec/lib/schema_expectations/active_record/column_reflector_spec.rb +150 -0
- data/spec/lib/schema_expectations/active_record/validation_reflector_spec.rb +62 -0
- data/spec/lib/schema_expectations/config_spec.rb +22 -0
- data/spec/lib/schema_expectations/rspec_matchers/validate_schema_nullable_spec.rb +260 -77
- data/spec/lib/schema_expectations/util_spec.rb +16 -0
- data/spec/meta_spec.rb +44 -0
- data/spec/spec_helper.rb +11 -2
- data/spec/support/active_record.rb +34 -12
- data/spec/support/active_record_helpers.rb +51 -0
- data/spec/support/gem_filters.rb +6 -0
- metadata +162 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18218491d35e976cf13367e3d30d60e952886de6
|
4
|
+
data.tar.gz: fa58281615fdda22fefb031a987f85001c8fe672
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1eb5dc8161b1b91c08d40225bbd03d58e4153a6ea51af88420afbb91549837ca861849e486bb8e41f703097e5566ff6a344785829ac772a24c5a36995aa3992d
|
7
|
+
data.tar.gz: 8cc5ba8420797a7d5b10e8c25ca2ca309b2b8ecd7204f804296960988d7b835fc99ced2ff607f8b32c384ab5f407de6cf1d37cd3b103cc53fc1c23e0ba2052a9
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,7 +1,21 @@
|
|
1
|
+
---
|
1
2
|
language: ruby
|
2
3
|
rvm:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.5
|
6
|
+
- 2.2.0
|
7
|
+
env:
|
8
|
+
- DB=sqlite3
|
9
|
+
- DB=postgresql
|
10
|
+
- DB=mysql2
|
11
|
+
script: bundle exec rake
|
12
|
+
gemfile:
|
13
|
+
- gemfiles/activerecord_3.1.gemfile
|
14
|
+
- gemfiles/activerecord_3.2.gemfile
|
15
|
+
- gemfiles/activerecord_4.0.gemfile
|
16
|
+
- gemfiles/activerecord_4.1.gemfile
|
17
|
+
- gemfiles/activerecord_4.2.gemfile
|
18
|
+
- gemfiles/default.gemfile
|
19
|
+
- gemfiles/rspec_3.0.gemfile
|
20
|
+
- gemfiles/rspec_3.1.gemfile
|
21
|
+
- gemfiles/rspec_3.2.gemfile
|
data/Appraisals
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
appraise 'activerecord-3.1' do
|
2
|
+
gem 'activerecord', '~> 3.1'
|
3
|
+
end
|
4
|
+
|
5
|
+
appraise 'activerecord-3.2' do
|
6
|
+
gem 'activerecord', '~> 3.2'
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise 'activerecord-4.0' do
|
10
|
+
gem 'activerecord', '~> 4.0'
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise 'activerecord-4.1' do
|
14
|
+
gem 'activerecord', '~> 4.1'
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise 'activerecord-4.2' do
|
18
|
+
gem 'activerecord', '~> 4.2'
|
19
|
+
end
|
20
|
+
|
21
|
+
appraise 'rspec-3.0' do
|
22
|
+
gem 'rspec', '~> 3.0'
|
23
|
+
end
|
24
|
+
|
25
|
+
appraise 'rspec-3.1' do
|
26
|
+
gem 'rspec', '~> 3.1'
|
27
|
+
end
|
28
|
+
|
29
|
+
appraise 'rspec-3.2' do
|
30
|
+
gem 'rspec', '~> 3.2'
|
31
|
+
end
|
32
|
+
|
33
|
+
appraise('default') {}
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Schema Expectations Changelog
|
2
2
|
|
3
|
-
|
3
|
+
### git master
|
4
|
+
|
5
|
+
### 0.2.0 (Febuary 18, 2015)
|
6
|
+
|
7
|
+
- support activerecord 3.1 to 4.2
|
8
|
+
- support rspec 3.0 to 3.2
|
9
|
+
- `validate_schema_nullable` #failure_message_when_negated works
|
10
|
+
- `validate_schema_nullable` #description works
|
11
|
+
- `validate_schema_nullable` supports belongs_to association validators
|
12
|
+
- `validate_schema_nullable` supports polymorphic belongs_to association validators
|
13
|
+
- `validate_schema_nullable` skips primary key and timestamps
|
14
|
+
- `validate_schema_nullable` is aware of columns with default values
|
15
|
+
- `validate_schema_nullable` is aware of columns with default functions
|
16
|
+
- postgres support
|
17
|
+
- mysql support
|
18
|
+
|
19
|
+
### 0.0.1 (February 12, 2015)
|
4
20
|
|
5
21
|
- `validate_schema_nullable` supports being called on AR instances
|
6
22
|
- documented `validate_schema_nullable`
|
data/README.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
[](https://rubygems.org/gems/schema_expectations)
|
1
2
|
[](https://travis-ci.org/emma-borhanian/schema_expectations)
|
3
|
+
[](https://codeclimate.com/github/emma-borhanian/schema_expectations)
|
4
|
+
[](https://codeclimate.com/github/emma-borhanian/schema_expectations)
|
5
|
+
[](https://gemnasium.com/emma-borhanian/schema_expectations)
|
2
6
|
|
3
7
|
# Work in progress
|
4
8
|
|
data/Rakefile
CHANGED
@@ -10,3 +10,15 @@ begin
|
|
10
10
|
rescue LoadError
|
11
11
|
# no rspec available
|
12
12
|
end
|
13
|
+
|
14
|
+
task :refresh do
|
15
|
+
require 'yaml'
|
16
|
+
|
17
|
+
`appraisal install`
|
18
|
+
|
19
|
+
travis_file = File.expand_path('../.travis.yml', __FILE__)
|
20
|
+
travis_config = YAML.load(File.read(travis_file))
|
21
|
+
travis_config['gemfile'] = Dir.glob("#{File.dirname(__FILE__)}/gemfiles/*.gemfile").
|
22
|
+
map { |path| "gemfiles/#{File.basename(path)}" }
|
23
|
+
File.open(travis_file, 'w') { |f| f.write YAML.dump(travis_config) }
|
24
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
3
|
+
module SchemaExpectations
|
4
|
+
module ActiveRecord
|
5
|
+
class ColumnReflector # :nodoc:
|
6
|
+
def initialize(model, columns = nil)
|
7
|
+
@model = model
|
8
|
+
@columns = columns || model.columns
|
9
|
+
end
|
10
|
+
|
11
|
+
def column_names
|
12
|
+
@columns.map { |column| column.name.to_sym }
|
13
|
+
end
|
14
|
+
|
15
|
+
def not_null
|
16
|
+
new_with_columns @columns.reject(&:null)
|
17
|
+
end
|
18
|
+
|
19
|
+
def for_attributes(*attributes)
|
20
|
+
new_with_columns attributes_to_columns(*attributes)
|
21
|
+
end
|
22
|
+
|
23
|
+
def without_present_default
|
24
|
+
new_with_columns @columns.reject(&method(:present_default_column?))
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def new_with_columns(columns)
|
30
|
+
ColumnReflector.new(@model, columns)
|
31
|
+
end
|
32
|
+
|
33
|
+
def attributes_to_columns(*attributes)
|
34
|
+
column_names = attributes.flat_map(&method(:attribute_to_column_names))
|
35
|
+
@columns.select do |column|
|
36
|
+
column_names.include? column.name.to_sym
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def attribute_to_column_names(attribute)
|
41
|
+
association = @model.reflect_on_association(attribute)
|
42
|
+
|
43
|
+
if association && association.belongs_to? && association.options.key?(:polymorphic)
|
44
|
+
[association.foreign_key.to_sym, association.foreign_type.to_sym]
|
45
|
+
elsif association && association.belongs_to?
|
46
|
+
[association.foreign_key.to_sym]
|
47
|
+
else
|
48
|
+
[attribute]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def present_default_column?(column)
|
53
|
+
column.default.present? ||
|
54
|
+
primary_key?(column) ||
|
55
|
+
default_timestamp?(column) ||
|
56
|
+
default_function?(column)
|
57
|
+
end
|
58
|
+
|
59
|
+
def primary_key?(column)
|
60
|
+
column.name.to_s == @model.primary_key.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_timestamp?(column)
|
64
|
+
@model.record_timestamps && all_timestamp_attributes.include?(column.name.to_sym)
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_function?(column)
|
68
|
+
column.respond_to?(:default_function) &&
|
69
|
+
column.default_function &&
|
70
|
+
function_produces_present_value?(column.default_function)
|
71
|
+
end
|
72
|
+
|
73
|
+
def function_produces_present_value?(function)
|
74
|
+
query = "SELECT #{function}"
|
75
|
+
begin
|
76
|
+
result = @model.connection.execute query
|
77
|
+
result.first.values.first.present?
|
78
|
+
rescue => e
|
79
|
+
message = "SchemaExpectations: encountered error running #{query}"
|
80
|
+
message << "\n" << e.message
|
81
|
+
message << "\n" << e.backtrace.join('\n')
|
82
|
+
SchemaExpectations.error_logger.error message
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def all_timestamp_attributes
|
88
|
+
@all_timestamp_attributes ||= Record.new.send(:all_timestamp_attributes).map(&:to_sym)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'schema_expectations/util'
|
2
|
+
|
3
|
+
module SchemaExpectations
|
4
|
+
module ActiveRecord
|
5
|
+
class ValidationReflector # :nodoc:
|
6
|
+
CONDITIONAL_OPTIONS = %i(on if unless allow_nil allow_blank)
|
7
|
+
|
8
|
+
def initialize(model, validators = nil)
|
9
|
+
@model = model
|
10
|
+
@validators = validators || model.validators
|
11
|
+
end
|
12
|
+
|
13
|
+
def attributes
|
14
|
+
@validators.flat_map(&:attributes).uniq
|
15
|
+
end
|
16
|
+
|
17
|
+
def conditions_for_attribute(attribute)
|
18
|
+
validators = validators_for_attribute(attribute)
|
19
|
+
validators -= validators_without_options CONDITIONAL_OPTIONS
|
20
|
+
Util.slice_hash(validators.first.options, *CONDITIONAL_OPTIONS) if validators.first
|
21
|
+
end
|
22
|
+
|
23
|
+
def presence
|
24
|
+
new_with_validators validators_with_kind :presence
|
25
|
+
end
|
26
|
+
|
27
|
+
def unconditional
|
28
|
+
new_with_validators validators_without_options CONDITIONAL_OPTIONS
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def new_with_validators(validators)
|
34
|
+
ValidationReflector.new(@model, validators)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validators_with_kind(kind)
|
38
|
+
@validators.select do |validator|
|
39
|
+
validator.kind == kind
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def validators_without_options(options)
|
44
|
+
@validators.select do |validator|
|
45
|
+
options.all? do |option_key|
|
46
|
+
!validator.options[option_key] ||
|
47
|
+
Array(validator.options[option_key]).empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def validators_for_attribute(attribute)
|
53
|
+
@validators.select do |validator|
|
54
|
+
validator.attributes.include? attribute
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module SchemaExpectations
|
4
|
+
def self.configure(&block)
|
5
|
+
@config.instance_eval(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.error_logger
|
9
|
+
@config.error_logger
|
10
|
+
end
|
11
|
+
|
12
|
+
class Config
|
13
|
+
attr_accessor :error_logger
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
reset!
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset!
|
20
|
+
@error_logger = Logger.new($stderr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@config = Config.new
|
25
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'rspec/expectations'
|
2
|
+
require 'schema_expectations/active_record/validation_reflector'
|
3
|
+
require 'schema_expectations/active_record/column_reflector'
|
2
4
|
|
3
5
|
module SchemaExpectations
|
4
6
|
module RSpecMatchers
|
5
|
-
# The `validate_schema_nullable` matcher
|
7
|
+
# The `validate_schema_nullable` matcher tests that an ActiveRecord model
|
6
8
|
# has unconditional presence validation on columns with `NOT NULL` constraints,
|
7
9
|
# and vice versa.
|
8
10
|
#
|
@@ -30,7 +32,7 @@ module SchemaExpectations
|
|
30
32
|
# it { should validate_schema_nullable.except(:name) }
|
31
33
|
# end
|
32
34
|
#
|
33
|
-
# The
|
35
|
+
# The primary key and timestamp columns are automatically skipped.
|
34
36
|
#
|
35
37
|
# @return [ValidateSchemaNullableMatcher]
|
36
38
|
def validate_schema_nullable
|
@@ -39,36 +41,40 @@ module SchemaExpectations
|
|
39
41
|
|
40
42
|
class ValidateSchemaNullableMatcher
|
41
43
|
def matches?(model)
|
42
|
-
model =
|
43
|
-
|
44
|
-
|
45
|
-
@
|
46
|
-
@
|
47
|
-
@
|
48
|
-
@not_null_columns == @present_attributes
|
44
|
+
@model = cast_model model
|
45
|
+
@validation_reflector = ActiveRecord::ValidationReflector.new(@model)
|
46
|
+
@column_reflector = ActiveRecord::ColumnReflector.new(@model)
|
47
|
+
@not_null_column_names = filter_column_names(not_null_column_names).sort
|
48
|
+
@present_column_names = filter_column_names(present_column_names).sort
|
49
|
+
@not_null_column_names == @present_column_names
|
49
50
|
end
|
50
51
|
|
51
52
|
def failure_message
|
52
|
-
@not_null_columns.sort!
|
53
|
-
@present_attributes.sort!
|
54
|
-
|
55
53
|
errors = []
|
56
54
|
|
57
|
-
(@
|
58
|
-
errors << "#{
|
55
|
+
(@present_column_names - @not_null_column_names).each do |column_name|
|
56
|
+
errors << "#{column_name} has unconditional presence validation but is missing NOT NULL"
|
59
57
|
end
|
60
58
|
|
61
|
-
(@
|
62
|
-
if
|
63
|
-
errors << "#{
|
59
|
+
(@not_null_column_names - @present_column_names).each do |column_name|
|
60
|
+
if conditions = validator_conditions_for_column_name(column_name)
|
61
|
+
errors << "#{column_name} is NOT NULL but its presence validator was conditional: #{conditions.inspect}"
|
64
62
|
else
|
65
|
-
errors << "#{
|
63
|
+
errors << "#{column_name} is NOT NULL but has no presence validation"
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
69
67
|
errors.join(', ')
|
70
68
|
end
|
71
69
|
|
70
|
+
def failure_message_when_negated
|
71
|
+
'should not match NOT NULL with its presence validation but does'
|
72
|
+
end
|
73
|
+
|
74
|
+
def description
|
75
|
+
'validate NOT NULL columns are present'
|
76
|
+
end
|
77
|
+
|
72
78
|
# Specifies a list of columns to restrict matcher
|
73
79
|
#
|
74
80
|
# @return [ValidateSchemaNullableMatcher] self
|
@@ -91,58 +97,43 @@ module SchemaExpectations
|
|
91
97
|
|
92
98
|
private
|
93
99
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
100
|
+
def cast_model(model)
|
101
|
+
model = model.class if model.is_a?(::ActiveRecord::Base)
|
102
|
+
unless model.is_a?(Class) && model.ancestors.include?(::ActiveRecord::Base)
|
103
|
+
fail "#{model.inspect} does not inherit from ActiveRecord::Base"
|
97
104
|
end
|
105
|
+
model
|
98
106
|
end
|
99
107
|
|
100
|
-
def
|
101
|
-
|
102
|
-
keep = %i(on if unless).all? do |option_key|
|
103
|
-
Array(validator.options[option_key]).empty?
|
104
|
-
end
|
105
|
-
|
106
|
-
keep && !validator.options[:allow_nil] && !validator.options[:allow_blank]
|
107
|
-
end
|
108
|
+
def present_attributes
|
109
|
+
@validation_reflector.presence.unconditional.attributes
|
108
110
|
end
|
109
111
|
|
110
|
-
def
|
111
|
-
present_attributes
|
112
|
-
|
113
|
-
present_attributes &= model.columns.map(&:name).map(&:to_sym)
|
114
|
-
present_attributes
|
112
|
+
def present_column_names
|
113
|
+
@column_reflector.for_attributes(*present_attributes).
|
114
|
+
without_present_default.column_names
|
115
115
|
end
|
116
116
|
|
117
|
-
def
|
118
|
-
|
117
|
+
def column_name_to_attribute(column_name)
|
118
|
+
@validation_reflector.attributes.detect do |attribute|
|
119
|
+
@column_reflector.for_attributes(attribute).column_names.
|
120
|
+
include? column_name
|
121
|
+
end
|
119
122
|
end
|
120
123
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
attributes -= [:id]
|
125
|
-
attributes
|
124
|
+
def not_null_column_names
|
125
|
+
@column_reflector.not_null.
|
126
|
+
without_present_default.column_names
|
126
127
|
end
|
127
128
|
|
128
|
-
def
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
validators.each do |validator|
|
134
|
-
condition = [:on, :if, :unless].detect do |option_key|
|
135
|
-
!Array(validator.options[option_key]).empty?
|
136
|
-
end
|
137
|
-
|
138
|
-
condition ||= [:allow_nil, :allow_blank].detect do |option_key|
|
139
|
-
validator.options[option_key]
|
140
|
-
end
|
141
|
-
|
142
|
-
return { condition => validator.options[condition] } if condition
|
143
|
-
end
|
129
|
+
def filter_column_names(column_names)
|
130
|
+
column_names &= @only if @only
|
131
|
+
column_names -= @except if @except
|
132
|
+
column_names
|
133
|
+
end
|
144
134
|
|
145
|
-
|
135
|
+
def validator_conditions_for_column_name(column_name)
|
136
|
+
@validation_reflector.conditions_for_attribute column_name_to_attribute column_name
|
146
137
|
end
|
147
138
|
end
|
148
139
|
end
|
data/lib/schema_expectations.rb
CHANGED
data/schema_expectations.gemspec
CHANGED
@@ -22,15 +22,24 @@ Gem::Specification.new do |gem|
|
|
22
22
|
|
23
23
|
gem.required_ruby_version = '>= 2.0.0'
|
24
24
|
|
25
|
-
gem.add_dependency 'activerecord', '
|
25
|
+
gem.add_dependency 'activerecord', '>= 3.1', '< 4.3'
|
26
|
+
gem.add_dependency 'activesupport', '>= 3.1', '< 4.3'
|
27
|
+
|
28
|
+
# optional dependency
|
29
|
+
gem.add_development_dependency 'rspec', '>= 3.0', '< 3.3'
|
26
30
|
|
27
31
|
gem.add_development_dependency 'pry'
|
28
32
|
gem.add_development_dependency 'rake', '~> 10.4'
|
29
33
|
gem.add_development_dependency 'yard', '~> 0.8.7'
|
30
34
|
|
31
35
|
# tests
|
32
|
-
gem.add_development_dependency '
|
33
|
-
gem.add_development_dependency '
|
36
|
+
gem.add_development_dependency 'wwtd'
|
37
|
+
gem.add_development_dependency 'appraisal', '~> 1.0'
|
38
|
+
gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4', '>= 0.4.6'
|
39
|
+
gem.add_development_dependency 'simplecov'
|
34
40
|
gem.add_development_dependency 'guard-rspec', '~> 4.5'
|
35
41
|
gem.add_development_dependency 'sqlite3', '~> 1.3'
|
42
|
+
gem.add_development_dependency 'pg', '~> 0.18'
|
43
|
+
gem.add_development_dependency 'mysql2', '~> 0.3.18'
|
44
|
+
gem.add_development_dependency 'database_cleaner', '~> 1.4'
|
36
45
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
sqlite3:
|
2
|
+
adapter: sqlite3
|
3
|
+
database: ':memory:'
|
4
|
+
postgresql:
|
5
|
+
adapter: postgresql
|
6
|
+
encoding: unicode
|
7
|
+
database: schema_expectations_test
|
8
|
+
username: postgres
|
9
|
+
mysql2:
|
10
|
+
adapter: mysql2
|
11
|
+
encoding: utf8
|
12
|
+
database: schema_expectations_test
|
13
|
+
username: root
|