pg_shrink 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,459 @@
1
+ require 'spec_helper'
2
+ require 'pg_spec_helper'
3
+
4
+ describe PgShrink do
5
+ before(:all) do
6
+ PgSpecHelper.reset_database
7
+ end
8
+
9
+ let(:database) {
10
+ PgShrink::Database::Postgres.new(PgSpecHelper.pg_config)
11
+ }
12
+ after(:each) do
13
+ database.connection.disconnect
14
+ end
15
+
16
+ describe "simple foreign_key setup" do
17
+ before(:all) do
18
+ # Rspec doesn't want you using 'let' defined things in before(:all)
19
+ connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
20
+ connection
21
+ PgSpecHelper.create_table(connection, :users,
22
+ {'name' => 'character varying(256)',
23
+ 'email' => 'character varying(256)'})
24
+ PgSpecHelper.create_table(connection, :user_preferences,
25
+ {'user_id' => 'integer',
26
+ 'name' => 'character varying(256)',
27
+ 'value' => 'character varying(256)'})
28
+ end
29
+
30
+
31
+ describe "simple two table filtering" do
32
+
33
+ describe "with 20 users and associated preferences" do
34
+ before(:each) do
35
+ PgSpecHelper.clear_table(database.connection, :users)
36
+ PgSpecHelper.clear_table(database.connection, :user_preferences)
37
+ (1..20).each do |i|
38
+ database.connection.run("insert into users (name, email) " +
39
+ "values ('test #{i}', 'test#{i}@test.com')")
40
+ u = database.connection.from(:users).where(:name => "test #{i}").first
41
+ (1..3).each do |j|
42
+ database.connection.run(
43
+ "insert into user_preferences (user_id, name, value) " +
44
+ "values (#{u[:id]}, 'pref#{i}', 'prefvalue#{i}')"
45
+ )
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "with a test shrinkfile" do
51
+ let(:shrinkfile) {"spec/Shrinkfile.basic"}
52
+ let(:url) {database.connection_string}
53
+
54
+ it "should set up a postgres database" do
55
+ expect(PgShrink::Database::Postgres).to receive(:new) do |opts|
56
+ expect(opts[:postgres_url]).to eq(database.connection_string)
57
+ end.and_return(database)
58
+ PgShrink.run(config: shrinkfile, url: url, force: true)
59
+ end
60
+ end
61
+
62
+
63
+ describe "a simple filter and subtable" do
64
+ before(:each) do
65
+ database.filter_table(:users) do |f|
66
+ f.filter_by do |u|
67
+ u[:name] == "test 1"
68
+ end
69
+ f.filter_subtable(:user_preferences, :foreign_key => :user_id)
70
+ end
71
+ database.filter!
72
+ end
73
+
74
+ it "will filter users down to the one matching" do
75
+ remaining_users = database.connection.from(:users).all
76
+ expect(remaining_users.size).to eq(1)
77
+ end
78
+
79
+ it "will filter preferences to only those associated with the user" do
80
+ remaining_user = database.connection.from(:users).first
81
+ remaining_preferences = database.connection.
82
+ from(:user_preferences).all
83
+ expect(remaining_preferences.size).to eq(3)
84
+ expect(remaining_preferences.map {|u| u[:user_id]}.uniq).
85
+ to eq([remaining_user[:id]])
86
+ end
87
+ end
88
+
89
+ describe "a simple filter and subtable with sanitization on each" do
90
+
91
+ before(:each) do
92
+ database.filter_table(:users) do |f|
93
+ f.filter_by do |u|
94
+ u[:name] == "test 1"
95
+ end
96
+ f.sanitize do |u|
97
+ u[:name] = "sanitized #{u[:name]}"
98
+ u[:email] = "blank_email#{u[:id]}@foo.bar"
99
+ u
100
+ end
101
+ f.filter_subtable(:user_preferences, :foreign_key => :user_id)
102
+ end
103
+
104
+ database.filter_table(:user_preferences) do |f|
105
+ f.sanitize do |u|
106
+ u[:value] = "sanitized #{u[:value]}"
107
+ u
108
+ end
109
+ end
110
+
111
+ database.shrink!
112
+ end
113
+
114
+ it "should result in 1 sanitized users" do
115
+ remaining_users = database.connection.from(:users).all
116
+ expect(remaining_users.size).to eq(1)
117
+ expect(remaining_users.first[:name]).to match(/sanitized/)
118
+ expect(remaining_users.first[:email]).to match(/blank_email/)
119
+ end
120
+
121
+ it "should result in 3 sanitized preferences" do
122
+ remaining_user = database.connection.from(:users).first
123
+ remaining_preferences = database.connection.
124
+ from(:user_preferences).all
125
+ expect(remaining_preferences.size).to eq(3)
126
+ expect(remaining_preferences.all? do |p|
127
+ p[:value] =~ /sanitized/
128
+ end).to be_true
129
+ end
130
+ end
131
+ end
132
+ describe "with users and preferences including email as value" do
133
+ before(:each) do
134
+ PgSpecHelper.clear_table(database.connection, :users)
135
+ PgSpecHelper.clear_table(database.connection, :user_preferences)
136
+ (1..20).each do |i|
137
+ database.connection.run("insert into users (name, email) " +
138
+ "values ('test #{i}', 'test#{i}@test.com')")
139
+ u = database.connection.from(:users).where(:name => "test #{i}").first
140
+ database.connection.run(
141
+ "insert into user_preferences (user_id, name, value) " +
142
+ "values (#{u[:id]}, 'email', '#{u[:email]}')"
143
+ )
144
+ database.connection.run(
145
+ "insert into user_preferences (user_id, name, value) " +
146
+ "values (#{u[:id]}, 'name', '#{u[:name]}')"
147
+ )
148
+ end
149
+ end
150
+
151
+ describe "sanitizing subtable" do
152
+ before(:each) do
153
+ database.filter_table(:users) do |f|
154
+ f.sanitize do |u|
155
+ u[:email] = "blank_email#{u[:id]}@foo.bar"
156
+ u
157
+ end
158
+ f.sanitize_subtable(:user_preferences,
159
+ :foreign_key => :user_id,
160
+ :local_field => :email,
161
+ :foreign_field => :value,
162
+ :type_key => :name,
163
+ :type => 'email')
164
+ end
165
+ database.shrink!
166
+ end
167
+
168
+ it "should sanitize user preference emails" do
169
+ remaining_preferences = database.connection.
170
+ from(:user_preferences).where(:name => 'email').all
171
+ remaining_values = remaining_preferences.map {|p| p[:value]}
172
+ expect(remaining_values.grep(/blank_email/).size).to eq(20)
173
+ end
174
+ it "should not sanitize preferences with a different type" do
175
+ remaining_preferences = database.connection.
176
+ from(:user_preferences).where(:name => 'name').all
177
+ remaining_values = remaining_preferences.map {|p| p[:value]}
178
+ expect(remaining_values.grep(/blank_email/).size).to eq(0)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+
185
+ describe "three table filter chain" do
186
+ before(:all) do
187
+ # Rspec doesn't want you using 'let' defined things in before(:all)
188
+ connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
189
+ connection
190
+ PgSpecHelper.create_table(connection, :user_preference_values,
191
+ {'user_preference_id' => 'integer', 'value' =>
192
+ 'character varying(256)'})
193
+ end
194
+
195
+ describe "with 20 users and associated preferences" do
196
+ before(:each) do
197
+ PgSpecHelper.clear_table(database.connection, :users)
198
+ PgSpecHelper.clear_table(database.connection, :user_preferences)
199
+ PgSpecHelper.clear_table(database.connection, :user_preference_values)
200
+ (1..20).each do |i|
201
+ database.connection.run(
202
+ "insert into users (name, email) " +
203
+ "values ('test #{i}', 'test#{i}@test.com')"
204
+ )
205
+ u = database.connection.from(:users).
206
+ where(:name => "test #{i}").first
207
+ (1..3).each do |j|
208
+ database.connection.run(
209
+ "insert into user_preferences (user_id, name) " +
210
+ "values (#{u[:id]}, 'pref#{i}#{j}')"
211
+ )
212
+ pref = database.connection.from(:user_preferences).
213
+ where(:name => "pref#{i}#{j}").first
214
+ database.connection.run(
215
+ "insert into user_preference_values " +
216
+ "(user_preference_id, value) " +
217
+ "values (#{pref[:id]}, 'val#{i}#{j}')"
218
+ )
219
+ end
220
+ end
221
+ end
222
+
223
+ describe "a simple filter and chained subtables" do
224
+ before(:each) do
225
+ database.filter_table(:users) do |f|
226
+ f.filter_by do |u|
227
+ u[:name] == "test 1"
228
+ end
229
+ f.filter_subtable(:user_preferences, :foreign_key => :user_id)
230
+ end
231
+ database.filter_table(:user_preferences) do |f|
232
+ f.filter_subtable(:user_preference_values,
233
+ :foreign_key => :user_preference_id)
234
+ end
235
+
236
+ database.filter!
237
+ end
238
+ it "filters users down to the one matching" do
239
+ remaining_users = database.connection.from(:users).all
240
+ expect(remaining_users.size).to eq(1)
241
+ end
242
+ it "filters preferences to only those associated with the user" do
243
+ remaining_user = database.connection.from(:users).first
244
+ remaining_preferences = database.connection.
245
+ from(:user_preferences).all
246
+ expect(remaining_preferences.size).to eq(3)
247
+ expect(remaining_preferences.map {|u| u[:user_id]}.uniq).
248
+ to eq([remaining_user[:id]])
249
+ end
250
+ it "filters preference values to those associated with the " +
251
+ "preferences remaining" do
252
+ remaining_user = database.connection.from(:users).first
253
+ remaining_preferences = database.connection.
254
+ from(:user_preferences).all
255
+ remaining_preference_values = database.
256
+ connection.from(:user_preference_values).all
257
+ expect(remaining_preference_values.size).to eq(3)
258
+ expect(remaining_preference_values.map {|v|
259
+ v[:user_preference_id]
260
+ }).to match_array(remaining_preferences.map {|p| p[:id]})
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end
266
+ describe "polymorphic foreign key subtables" do
267
+ before(:all) do
268
+ # Rspec doesn't want you using 'let' defined things in before(:all)
269
+ connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
270
+ connection
271
+ PgSpecHelper.create_table(connection, :users,
272
+ {'name' => 'character varying(256)',
273
+ 'email' => 'character varying(256)'})
274
+ PgSpecHelper.create_table(connection, :preferences,
275
+ {'context_id' => 'integer',
276
+ 'context_type' => 'character varying(256)',
277
+ 'name' => 'character varying(256)',
278
+ 'value' => 'character varying(256)'})
279
+ end
280
+ describe "with 20 users, associated prefs, and prefs for different type" do
281
+ before(:each) do
282
+ PgSpecHelper.clear_table(database.connection, :users)
283
+ PgSpecHelper.clear_table(database.connection, :preferences)
284
+ (1..20).each do |i|
285
+ database.connection.run(
286
+ "insert into users (name, email) " +
287
+ "values ('test #{i}', 'test#{i}@test.com')")
288
+ u = database.connection.from(:users).where(:name => "test #{i}").first
289
+ (1..3).each do |j|
290
+ database.connection.run(
291
+ "insert into preferences (context_id, context_type, name, value)"+
292
+ " values (#{u[:id]}, 'User', 'pref#{i}', 'prefvalue#{i}')")
293
+ end
294
+ database.connection.run(
295
+ "insert into preferences (context_id, context_type, name, value) " +
296
+ "values(#{u[:id]}, 'OtherClass', 'pref#{i}', 'prefvalue#{i}')")
297
+ end
298
+ end
299
+
300
+ describe "simple two table filtering" do
301
+ before(:each) do
302
+ database.filter_table(:users) do |f|
303
+ f.filter_by do |u|
304
+ u[:name] == "test 1"
305
+ end
306
+ f.filter_subtable(:preferences, :foreign_key => :context_id,
307
+ :type_key => :context_type, :type => 'User')
308
+ end
309
+ database.filter!
310
+ end
311
+
312
+ it "will filter prefs with context_type 'User'" do
313
+ remaining_user = database.connection.from(:users).first
314
+ remaining_preferences = database.connection.from(:preferences).
315
+ where(:context_type => 'User').all
316
+ expect(remaining_preferences.size).to eq(3)
317
+ expect(remaining_preferences.map {|u| u[:context_id]}.uniq).
318
+ to eq([remaining_user[:id]])
319
+ end
320
+
321
+ it "will not filter preferences without context_type user" do
322
+ remaining_preferences = database.connection.from(:preferences).
323
+ where(:context_type => 'OtherClass').all
324
+ expect(remaining_preferences.size).to eq(20)
325
+ end
326
+ end
327
+
328
+ describe "an extra layer of polymorphic subtables" do
329
+ before(:all) do
330
+ connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
331
+ connection
332
+ PgSpecHelper.create_table(connection, :preference_dependents,
333
+ {'context_id' => 'integer',
334
+ 'context_type' => 'character varying(256)',
335
+ 'value' => 'character varying(256)'})
336
+ end
337
+
338
+ before(:each) do
339
+ PgSpecHelper.clear_table(database.connection, :preference_dependents)
340
+ prefs = database.connection.from(:preferences).all
341
+ prefs.each do |pref|
342
+ database.connection.run(
343
+ "insert into preference_dependents " +
344
+ "(context_id, context_type, value) " +
345
+ "values (#{pref[:id]}, 'Preference', 'depvalue#{pref[:id]}')")
346
+
347
+ database.connection.run(
348
+ "insert into preference_dependents " +
349
+ "(context_id, context_type, value) " +
350
+ "values (#{pref[:id]}, 'SomeOtherClass', 'fakevalue#{pref[:id]}')")
351
+
352
+ end
353
+
354
+ database.filter_table(:users) do |f|
355
+ f.filter_by do |u|
356
+ u[:name] == "test 1"
357
+ end
358
+ f.filter_subtable(:preferences, :foreign_key => :context_id,
359
+ :type_key => :context_type, :type => 'User')
360
+ end
361
+
362
+ database.filter_table(:preferences) do |f|
363
+ f.filter_subtable(:preference_dependents,
364
+ :foreign_key => :context_id,
365
+ :type_key => :context_type,
366
+ :type => 'Preference')
367
+ end
368
+ database.filter!
369
+ end
370
+
371
+ it "will filter preference dependents associated with preferences" do
372
+ remaining_preferences = database.connection.from(:preferences).all
373
+ remaining_dependents = database.connection.
374
+ from(:preference_dependents).
375
+ where(:context_type => 'Preference').all
376
+
377
+ expect(remaining_dependents.size).to eq(remaining_preferences.size)
378
+ end
379
+
380
+ it "will not filter preference dependents with different type" do
381
+ other_dependents = database.connection.
382
+ from(:preference_dependents).
383
+ where(:context_type => 'SomeOtherClass').all
384
+ expect(other_dependents.size).to eq(80)
385
+ end
386
+ end
387
+ end
388
+ end
389
+ describe "has_and_belongs_to_many join tables" do
390
+ before(:all) do
391
+ # Rspec doesn't want you using 'let' defined things in before(;all)
392
+ connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
393
+ connection
394
+ PgSpecHelper.create_table(connection, :users,
395
+ {'name' => 'character varying(256)',
396
+ 'email' => 'character varying(256)'})
397
+ PgSpecHelper.create_table(connection, :apartments_users,
398
+ {'user_id' => 'integer',
399
+ 'apartment_id' => 'integer'}, nil)
400
+ PgSpecHelper.create_table(connection, :apartments,
401
+ {'name' => 'character varying(256)'})
402
+ end
403
+
404
+ describe "with 5 users, each with 2 apartments, and 1 apartment shared by all 5" do
405
+ before(:each) do
406
+ PgSpecHelper.clear_table(database.connection, :users)
407
+ PgSpecHelper.clear_table(database.connection, :apartments_users)
408
+ PgSpecHelper.clear_table(database.connection, :apartments)
409
+ database.connection.run("insert into apartments (name) values ('shared_apt')")
410
+ shared_apt = database.connection.from(:apartments).first
411
+ (1..5).each do |i|
412
+ database.connection.run(
413
+ "insert into users (name, email) " +
414
+ "values ('test #{i}', 'test#{i}@test.com')")
415
+ u = database.connection.from(:users).where(:name => "test #{i}").first
416
+ (1..2).each do |j|
417
+ database.connection.run(
418
+ "insert into apartments (name) values ('apartment #{i}#{j}')")
419
+ end
420
+ apartments = database.connection.from(:apartments).
421
+ where(:name => ["apartment #{i}1", "apartment #{i}2"]).all
422
+ ([shared_apt] + apartments).each do |apt|
423
+ database.connection.run(
424
+ "insert into apartments_users (user_id, apartment_id) " +
425
+ "values (#{u[:id]}, #{apt[:id]})")
426
+ end
427
+ end
428
+ end
429
+ describe "With a simple cascading filter" do
430
+ before(:each) do
431
+ database.filter_table(:users) do |f|
432
+ f.filter_by do |u|
433
+ u[:name] == "test 1"
434
+ end
435
+ f.filter_subtable(:apartments_users,
436
+ :foreign_key => :user_id) do |t|
437
+ t.filter_subtable(:apartments, :foreign_key => :id,
438
+ :primary_key => :apartment_id)
439
+ end
440
+ end
441
+ database.filter_table(:apartments_users, :primary_key => false)
442
+ database.shrink!
443
+ end
444
+
445
+ it "Should filter down apartments_users" do
446
+ u = database.connection.from(:users).where(:name => "test 1").first
447
+ remaining_join_table = database.connection.from(:apartments_users).all
448
+ expect(remaining_join_table.size).to eq(3)
449
+ end
450
+
451
+ it "Should filter apartments as well" do
452
+ remaining_join_table = database.connection.from(:apartments_users).all
453
+ remaining_apartments = database.connection.from(:apartments).all
454
+ expect(remaining_apartments.size).to eq(3)
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end
@@ -0,0 +1,45 @@
1
+ require 'pg'
2
+ require 'yaml'
3
+ require 'sequel'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ module PgSpecHelper
7
+
8
+ # TODO: Make the db name and user (and other access stuff in
9
+ # test) easily configurable.
10
+ def self.reset_database
11
+ db_name = pg_config['database']
12
+ user = pg_config['user']
13
+ `psql --username=#{user} --command="drop database #{db_name};"`
14
+ `psql --username=#{user} --command="create database #{db_name};"`
15
+ end
16
+
17
+ def self.pg_config
18
+ @pg_config ||= YAML.load_file('spec/pg_config.yml')['test']
19
+ end
20
+
21
+ def self.drop_table_if_exists(connection, table)
22
+ connection.run("drop table if exists #{table}")
23
+ end
24
+
25
+ def self.create_table(connection, table, columns = {}, primary_key = :id)
26
+ # For ease of testing, whenever we create we want to override any previous
27
+ # tables
28
+ self.drop_table_if_exists(connection, table)
29
+ columns = if primary_key
30
+ primary_key = primary_key.to_sym
31
+ columns = {primary_key=> 'serial primary key'}.merge(columns.symbolize_keys)
32
+ else
33
+ columns.symbolize_keys
34
+ end
35
+ sql = "create table #{table} (" +
36
+ columns.map {|col, type| "#{col} #{type}"}.join(',') +
37
+ ")"
38
+ connection.run(sql)
39
+ end
40
+
41
+ def self.clear_table(connection, table)
42
+ connection.run("delete from #{table};")
43
+ end
44
+
45
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'pg_shrink'