monkey_butler 1.2.2

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +3 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +122 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE +202 -0
  10. data/README.md +221 -0
  11. data/Rakefile +16 -0
  12. data/bin/mb +6 -0
  13. data/lib/monkey_butler.rb +1 -0
  14. data/lib/monkey_butler/actions.rb +38 -0
  15. data/lib/monkey_butler/cli.rb +354 -0
  16. data/lib/monkey_butler/databases/abstract_database.rb +49 -0
  17. data/lib/monkey_butler/databases/cassandra_database.rb +69 -0
  18. data/lib/monkey_butler/databases/sqlite_database.rb +105 -0
  19. data/lib/monkey_butler/migrations.rb +52 -0
  20. data/lib/monkey_butler/project.rb +90 -0
  21. data/lib/monkey_butler/targets/base.rb +85 -0
  22. data/lib/monkey_butler/targets/cassandra/cassandra_target.rb +72 -0
  23. data/lib/monkey_butler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
  24. data/lib/monkey_butler/targets/cassandra/migration.cql.erb +0 -0
  25. data/lib/monkey_butler/targets/cocoapods/cocoapods_target.rb +43 -0
  26. data/lib/monkey_butler/targets/cocoapods/podspec.erb +12 -0
  27. data/lib/monkey_butler/targets/maven/maven_target.rb +60 -0
  28. data/lib/monkey_butler/targets/maven/project/.gitignore +6 -0
  29. data/lib/monkey_butler/targets/maven/project/MonkeyButler.iml +19 -0
  30. data/lib/monkey_butler/targets/maven/project/build.gradle +54 -0
  31. data/lib/monkey_butler/targets/sqlite/create_monkey_butler_tables.sql.erb +15 -0
  32. data/lib/monkey_butler/targets/sqlite/migration.sql.erb +11 -0
  33. data/lib/monkey_butler/targets/sqlite/sqlite_target.rb +91 -0
  34. data/lib/monkey_butler/templates/Gemfile.erb +4 -0
  35. data/lib/monkey_butler/templates/gitignore.erb +1 -0
  36. data/lib/monkey_butler/util.rb +71 -0
  37. data/lib/monkey_butler/version.rb +3 -0
  38. data/logo.jpg +0 -0
  39. data/monkey_butler.gemspec +33 -0
  40. data/spec/cli_spec.rb +700 -0
  41. data/spec/databases/cassandra_database_spec.rb +241 -0
  42. data/spec/databases/sqlite_database_spec.rb +181 -0
  43. data/spec/migrations_spec.rb +4 -0
  44. data/spec/project_spec.rb +128 -0
  45. data/spec/sandbox/cassandra/.gitignore +2 -0
  46. data/spec/sandbox/cassandra/.monkey_butler.yml +7 -0
  47. data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
  48. data/spec/sandbox/cassandra/sandbox.cql +0 -0
  49. data/spec/sandbox/sqlite/.gitignore +2 -0
  50. data/spec/sandbox/sqlite/.monkey_butler.yml +7 -0
  51. data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
  52. data/spec/sandbox/sqlite/sandbox.sql +0 -0
  53. data/spec/spec_helper.rb +103 -0
  54. data/spec/targets/cassandra_target_spec.rb +191 -0
  55. data/spec/targets/cocoapods_target_spec.rb +197 -0
  56. data/spec/targets/maven_target_spec.rb +156 -0
  57. data/spec/targets/sqlite_target_spec.rb +103 -0
  58. data/spec/util_spec.rb +13 -0
  59. metadata +260 -0
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+ require 'monkey_butler/databases/cassandra_database'
3
+
4
+ describe MonkeyButler::Databases::CassandraDatabase do
5
+ let(:db) { MonkeyButler::Databases::CassandraDatabase.new(cassandra_url) }
6
+ let(:cassandra_url) { URI("cassandra://localhost:9042/monkey_butler") }
7
+
8
+ before(:each) do
9
+ db.drop
10
+ end
11
+
12
+ describe ".migration_ext" do
13
+ MonkeyButler::Databases::CassandraDatabase.migration_ext.should == '.cql'
14
+ end
15
+
16
+ describe '#keyspace' do
17
+ it "extracts keyspace from path" do
18
+ db.keyspace.should == 'monkey_butler'
19
+ end
20
+ end
21
+
22
+ describe "#migrations_table?" do
23
+ context "when the migrations table exists" do
24
+ before(:each) do
25
+ db.create_migrations_table
26
+ end
27
+
28
+ it "is true" do
29
+ expect(db.migrations_table?).to be_true
30
+ end
31
+ end
32
+
33
+ context "when the migrations table does not exist" do
34
+ before(:each) do
35
+ db.drop
36
+ end
37
+
38
+ it "is true" do
39
+ expect(db.migrations_table?).to be_false
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "#origin_version" do
45
+ context "when the schema_migrations table does not exist" do
46
+ before(:each) do
47
+ db.drop
48
+ end
49
+
50
+ it "raises an error" do
51
+ expect { db.origin_version }.to raise_error(Cql::QueryError, "Keyspace 'monkey_butler' does not exist")
52
+ end
53
+ end
54
+
55
+ context "when the schema_migrations table is empty" do
56
+ before(:each) do
57
+ db.create_migrations_table
58
+ end
59
+
60
+ it "returns nil" do
61
+ db.origin_version.should be_nil
62
+ end
63
+ end
64
+
65
+ context "when the database has one version row" do
66
+ before(:each) do
67
+ db.create_migrations_table
68
+ db.insert_version(20140101023213)
69
+ end
70
+
71
+ it "returns that row" do
72
+ db.origin_version.should == 20140101023213
73
+ end
74
+ end
75
+
76
+ context "when the database has three version rows" do
77
+ before(:each) do
78
+ db.create_migrations_table
79
+ db.insert_version(1)
80
+ db.insert_version(2)
81
+ db.insert_version(3)
82
+ end
83
+
84
+ it "returns the lowest value" do
85
+ db.origin_version.should == 1
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "#current_version" do
91
+ context "when the schema_migrations table does not exist" do
92
+ before(:each) do
93
+ db.drop
94
+ end
95
+
96
+ it "raises an error" do
97
+ expect { db.current_version }.to raise_error(Cql::QueryError, "Keyspace 'monkey_butler' does not exist")
98
+ end
99
+ end
100
+
101
+ context "when the schema_migrations table is empty" do
102
+ before(:each) do
103
+ db.create_migrations_table
104
+ end
105
+
106
+ it "returns nil" do
107
+ db.current_version.should be_nil
108
+ end
109
+ end
110
+
111
+ context "when the database has one version row" do
112
+ before(:each) do
113
+ db.create_migrations_table
114
+ db.insert_version(20140101023213)
115
+ end
116
+
117
+ it "returns that row" do
118
+ db.current_version.should == 20140101023213
119
+ end
120
+ end
121
+
122
+ context "when the database has three version rows" do
123
+ before(:each) do
124
+ db.create_migrations_table
125
+ db.insert_version(1)
126
+ db.insert_version(2)
127
+ db.insert_version(3)
128
+ end
129
+
130
+ it "returns the highest value" do
131
+ db.current_version.should == 3
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "#all_versions" do
137
+ context "when the schema_migrations table does not exist" do
138
+ before(:each) do
139
+ db.drop
140
+ end
141
+
142
+ it "raises an error" do
143
+ expect { db.current_version }.to raise_error(Cql::QueryError, "Keyspace 'monkey_butler' does not exist")
144
+ end
145
+ end
146
+
147
+ context "when the schema_migrations table is empty" do
148
+ before(:each) do
149
+ db.create_migrations_table
150
+ end
151
+
152
+ it "returns an empty array" do
153
+ db.all_versions.should be_empty
154
+ end
155
+ end
156
+
157
+ context "when the database has one version row" do
158
+ before(:each) do
159
+ db.create_migrations_table
160
+ db.insert_version(20140101023213)
161
+ end
162
+
163
+ it "returns that row" do
164
+ db.all_versions.should == [20140101023213]
165
+ end
166
+ end
167
+
168
+ context "when the database has three version rows" do
169
+ before(:each) do
170
+ db.create_migrations_table
171
+ db.insert_version(1)
172
+ db.insert_version(2)
173
+ db.insert_version(3)
174
+ end
175
+
176
+ it "returns the highest value" do
177
+ db.all_versions.should == [1,2,3]
178
+ end
179
+ end
180
+ end
181
+
182
+ describe "#insert_version" do
183
+ context "when the schema_migrations table does not exist" do
184
+ before(:each) do
185
+ db.drop
186
+ end
187
+
188
+ it "raises an error" do
189
+ expect { db.insert_version(1234) }.to raise_error(Cql::QueryError, "Keyspace 'monkey_butler' does not exist")
190
+ end
191
+ end
192
+
193
+ context "when the schema_migrations table exists" do
194
+ before(:each) do
195
+ db.create_migrations_table
196
+ end
197
+
198
+ it "inserts successfully" do
199
+ db.insert_version(1234)
200
+ db.all_versions.should == [1234]
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "#execute_migration" do
206
+ context "when the schema_migrations table does not exist" do
207
+ before(:each) do
208
+ db.drop
209
+ end
210
+
211
+ it "raises an error" do
212
+ expect { db.insert_version(1234) }.to raise_error(Cql::QueryError, "Keyspace 'monkey_butler' does not exist")
213
+ end
214
+ end
215
+
216
+ context "when the schema_migrations table exists" do
217
+ before(:each) do
218
+ db.create_migrations_table
219
+ end
220
+
221
+ it "inserts successfully" do
222
+ db.insert_version(1234)
223
+ db.all_versions.should == [1234]
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#truncate" do
229
+ context "when the keyspace has tables" do
230
+ before(:each) do
231
+ db.create_migrations_table
232
+ end
233
+
234
+ it "destroys the tables" do
235
+ expect(db.migrations_table?).to be_true
236
+ db.drop
237
+ expect(db.migrations_table?).to be_false
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+ require 'monkey_butler/databases/sqlite_database'
3
+
4
+ describe MonkeyButler::Databases::SqliteDatabase do
5
+ it "has a SQL file extension" do
6
+ MonkeyButler::Databases::SqliteDatabase.migration_ext.should == '.sql'
7
+ end
8
+
9
+ it "initializes with a URL" do
10
+ path = Dir.mktmpdir + '/sandbox.sqlite'
11
+ database = MonkeyButler::Databases::SqliteDatabase.new(URI("sqlite://#{path}"))
12
+ database.path.should == path
13
+ end
14
+
15
+ it "stores url" do
16
+ url = URI("sqlite://#{Dir.mktmpdir}/sandbox.sqlite")
17
+ database = MonkeyButler::Databases::SqliteDatabase.new(url)
18
+ database.url.should == url
19
+ end
20
+
21
+ it "uses path for to_s" do
22
+ url = URI("sqlite://#{Dir.mktmpdir}/sandbox.sqlite")
23
+ database = MonkeyButler::Databases::SqliteDatabase.new(url)
24
+ database.to_s.should == url.path
25
+ end
26
+
27
+ context "when initialized with a relative path" do
28
+ it "loads the relative path" do
29
+ path = Dir.mktmpdir
30
+ Dir.chdir(path) do
31
+ database = MonkeyButler::Databases::SqliteDatabase.new(URI("sqlite:test.sqlite"))
32
+ database.path.should == 'test.sqlite'
33
+ end
34
+ end
35
+ end
36
+
37
+ let(:db_path) { Tempfile.new('monkey_butler').path }
38
+ let(:db) { MonkeyButler::Databases::SqliteDatabase.create(URI(db_path)) }
39
+
40
+ describe '#origin_version' do
41
+ context "when the schema_migrations table is empty" do
42
+ it "returns nil" do
43
+ db.origin_version.should be_nil
44
+ end
45
+ end
46
+
47
+ context "when the schema_migrations table has a single row" do
48
+ before(:each) do
49
+ @version = MonkeyButler::Util.migration_timestamp
50
+ db.insert_version(@version)
51
+ end
52
+
53
+ it "returns the value of the version column" do
54
+ db.origin_version.should == @version
55
+ end
56
+ end
57
+
58
+ context "when the schema_migrations table has many rows" do
59
+ before(:each) do
60
+ # Inject to guarantee mutation of the timestamp
61
+ @versions = (1..5).inject([]) do |versions, i|
62
+ version = MonkeyButler::Util.migration_timestamp + i
63
+ db.insert_version(version)
64
+ versions << version
65
+ end
66
+ end
67
+
68
+ it "returns the lowest value" do
69
+ db.origin_version.should == @versions.first
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#current_version' do
75
+ context "when the schema_migrations table is empty" do
76
+ it "returns nil" do
77
+ db.current_version.should be_nil
78
+ end
79
+ end
80
+
81
+ context "when the schema_migrations table has a single row" do
82
+ before(:each) do
83
+ @version = MonkeyButler::Util.migration_timestamp
84
+ db.insert_version(@version)
85
+ end
86
+
87
+ it "returns the value of the version column" do
88
+ db.current_version.should == @version
89
+ end
90
+ end
91
+
92
+ context "when the schema_migrations table has many rows" do
93
+ before(:each) do
94
+ # Inject to guarantee mutation of the timestamp
95
+ @versions = (1..5).inject([]) do |versions, i|
96
+ version = MonkeyButler::Util.migration_timestamp + i
97
+ db.insert_version(version)
98
+ versions << version
99
+ end
100
+ end
101
+
102
+ it "returns the highest value" do
103
+ db.current_version.should == @versions.last
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '#all_versions' do
109
+ context "when the database is empty" do
110
+ it "raises a SQL exception" do
111
+ db = MonkeyButler::Databases::SqliteDatabase.new(URI(db_path))
112
+ expect { db.all_versions }.to raise_error(SQLite3::SQLException)
113
+ end
114
+ end
115
+
116
+ context "when the schema_migrations table is empty" do
117
+ it "returns an empty array" do
118
+ db.all_versions.should == []
119
+ end
120
+ end
121
+
122
+ context "when the schema_migrations table has a single row" do
123
+ before(:each) do
124
+ @version = MonkeyButler::Util.migration_timestamp
125
+ db.insert_version(@version)
126
+ end
127
+
128
+ it "returns the only value" do
129
+ db.all_versions.should == [@version]
130
+ end
131
+ end
132
+
133
+ context "when the schema_migrations table has many rows" do
134
+ before(:each) do
135
+ # Inject to guarantee mutation of the timestamp
136
+ @versions = (1..5).inject([]) do |versions, i|
137
+ version = MonkeyButler::Util.migration_timestamp + i
138
+ db.insert_version(version)
139
+ versions << version
140
+ end
141
+ end
142
+
143
+ it "returns all values in ascending order" do
144
+ db.all_versions.should == @versions
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#execute_migration' do
150
+ context "when given valid SQL" do
151
+ it "executes without error" do
152
+ expect { db.execute_migration("CREATE TABLE new_table(version INTEGER UNIQUE NOT NULL)") }.not_to raise_error
153
+ end
154
+ end
155
+
156
+ context "when given invalid SQL" do
157
+ it "raises an error" do
158
+ expect do
159
+ db.execute_migration MonkeyButler::Util.strip_leading_whitespace(
160
+ <<-SQL
161
+ CREATE TABLE new_table(version INTEGER UNIQUE NOT NULL);
162
+ DELETE FROM invalid_table;
163
+ SQL
164
+ )
165
+ end.to raise_error(SQLite3::SQLException)
166
+ end
167
+
168
+ it "rolls back transaction" do
169
+ db.execute_migration("CREATE TABLE new_table(version INTEGER UNIQUE NOT NULL)")
170
+ db.execute_migration MonkeyButler::Util.strip_leading_whitespace(
171
+ <<-SQL
172
+ DROP TABLE new_table;
173
+ DELETE FROM invalid_table;
174
+ SQL
175
+ ) rescue nil
176
+ expect(db.db.get_first_value('SELECT MIN(version) FROM new_table')).to be_nil
177
+ end
178
+ end
179
+ end
180
+
181
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe MonkeyButler::Migrations do
4
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe MonkeyButler::Project do
4
+ let!(:project_root) { clone_temp_sandbox }
5
+ let(:project) { MonkeyButler::Project.load(project_root) }
6
+
7
+ before(:each) do
8
+ Dir.chdir(project_root)
9
+ end
10
+
11
+ describe "#database" do
12
+ context "when given a URL without a scheme" do
13
+ it "assumes SQLite" do
14
+ project = MonkeyButler::Project.new(database_url: 'sandbox.sqlite')
15
+ project.database.should == 'sqlite'
16
+ end
17
+ end
18
+
19
+ context "given a SQLite URL" do
20
+ it "returns sqlite" do
21
+ project = MonkeyButler::Project.new(database_url: 'sqlite:///sandbox.sqlite')
22
+ project.database.should == 'sqlite'
23
+ end
24
+ end
25
+
26
+ context "given a Cassandra URL" do
27
+ it "returns cassandra" do
28
+ project = MonkeyButler::Project.new(database_url: 'cassandra://localhost:9170')
29
+ project.database.should == 'cassandra'
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#git_url' do
35
+ end
36
+
37
+ describe '#git_latest_tag' do
38
+ context "when there are no tags" do
39
+ it "returns nil" do
40
+ project.git_latest_tag.should be_nil
41
+ end
42
+ end
43
+
44
+ context "when there is one tag" do
45
+ before(:each) do
46
+ tag_versions(%w{1.0.0})
47
+ end
48
+
49
+ it "returns that tag" do
50
+ project.git_latest_tag.should == '1.0.0'
51
+ end
52
+ end
53
+
54
+ context "when there are 4 tags" do
55
+ before(:each) do
56
+ tag_versions(%w{1.0.0 0.9.8 2.2.3 1.9.5})
57
+ end
58
+
59
+ it "returns the highest version number" do
60
+ project.git_latest_tag.should == '2.2.3'
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#git_tag_for_version' do
66
+ context "when there are no tags" do
67
+ it "returns nil" do
68
+ project.git_tag_for_version('v1.0.0').should be_nil
69
+ end
70
+ end
71
+
72
+ context "when there is one tag" do
73
+ before(:each) do
74
+ tag_versions(%w{1.0.0})
75
+ end
76
+
77
+ it "returns that tag" do
78
+ project.git_tag_for_version('1.0.0').should == '1.0.0'
79
+ end
80
+
81
+ context "but it doesn't match the query" do
82
+ it "returns nil" do
83
+ project.git_tag_for_version('0.9.6').should be_nil
84
+ end
85
+ end
86
+ end
87
+
88
+ context "when there are 4 tags" do
89
+ before(:each) do
90
+ tag_versions(%w{1.0.0 0.9.8 2.2.3 1.9.5})
91
+ end
92
+
93
+ it "returns the matching tag" do
94
+ project.git_tag_for_version('0.9.8').should == '0.9.8'
95
+ end
96
+
97
+ context "but none match the query" do
98
+ it "returns nil" do
99
+ project.git_tag_for_version('0.5.12').should be_nil
100
+ end
101
+ end
102
+
103
+ context "and there are point releases" do
104
+ before(:each) do
105
+ tag_versions(%w{0.9.8.1 0.9.8.5000 0.9.8.2 0.9.8.3})
106
+ end
107
+
108
+ it "returns the highest matching tag" do
109
+ project.git_tag_for_version('0.9.8').should == '0.9.8.5000'
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def tag_versions(*versions)
116
+ Dir.chdir(project_root) do
117
+ versions.flatten.each do |version|
118
+ filename = random_migration_name
119
+ `echo '' > #{filename}`
120
+ raise "Failed touching file" unless $?.exitstatus.zero?
121
+ `git add #{filename} && git commit -m 'commiting #{random_migration_name}' .`
122
+ raise "Failed commiting" unless $?.exitstatus.zero?
123
+ `git tag #{version}`
124
+ raise "Failed tagging version" unless $?.exitstatus.zero?
125
+ end
126
+ end
127
+ end
128
+ end