pg_shrink 0.0.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.
@@ -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'