rails_redshift_replicator 0.0.1

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/rails_redshift_replicator/application.js +13 -0
  6. data/app/assets/stylesheets/rails_redshift_replicator/application.css +15 -0
  7. data/app/controllers/rails_redshift_replicator/application_controller.rb +5 -0
  8. data/app/helpers/rails_redshift_replicator/application_helper.rb +4 -0
  9. data/app/models/rails_redshift_replicator/replication.rb +98 -0
  10. data/app/views/layouts/rails_redshift_replicator/application.html.erb +14 -0
  11. data/config/locales/rails_redshift_replicator.en.yml +20 -0
  12. data/config/routes.rb +2 -0
  13. data/db/migrate/20160503214955_create_rails_redshift_replicator_replications.rb +24 -0
  14. data/db/migrate/20160509193335_create_table_rails_redshift_replicator_deleted_ids.rb +8 -0
  15. data/lib/generators/rails_redshift_replicator/install_generator.rb +25 -0
  16. data/lib/generators/templates/rails_redshift_replicator.rb +74 -0
  17. data/lib/rails_redshift_replicator.rb +229 -0
  18. data/lib/rails_redshift_replicator/adapters/generic.rb +40 -0
  19. data/lib/rails_redshift_replicator/adapters/mysql2.rb +22 -0
  20. data/lib/rails_redshift_replicator/adapters/postgresql.rb +37 -0
  21. data/lib/rails_redshift_replicator/adapters/sqlite.rb +27 -0
  22. data/lib/rails_redshift_replicator/deleter.rb +67 -0
  23. data/lib/rails_redshift_replicator/engine.rb +14 -0
  24. data/lib/rails_redshift_replicator/exporters/base.rb +215 -0
  25. data/lib/rails_redshift_replicator/exporters/full_replicator.rb +9 -0
  26. data/lib/rails_redshift_replicator/exporters/identity_replicator.rb +9 -0
  27. data/lib/rails_redshift_replicator/exporters/timed_replicator.rb +9 -0
  28. data/lib/rails_redshift_replicator/file_manager.rb +134 -0
  29. data/lib/rails_redshift_replicator/importers/base.rb +158 -0
  30. data/lib/rails_redshift_replicator/importers/full_replicator.rb +17 -0
  31. data/lib/rails_redshift_replicator/importers/identity_replicator.rb +15 -0
  32. data/lib/rails_redshift_replicator/importers/timed_replicator.rb +18 -0
  33. data/lib/rails_redshift_replicator/model/extension.rb +45 -0
  34. data/lib/rails_redshift_replicator/model/hair_trigger_extension.rb +8 -0
  35. data/lib/rails_redshift_replicator/replicable.rb +143 -0
  36. data/lib/rails_redshift_replicator/rlogger.rb +12 -0
  37. data/lib/rails_redshift_replicator/tools/analyze.rb +18 -0
  38. data/lib/rails_redshift_replicator/tools/vacuum.rb +77 -0
  39. data/lib/rails_redshift_replicator/version.rb +3 -0
  40. data/lib/tasks/rails_redshift_replicator_tasks.rake +4 -0
  41. data/spec/dummy/README.rdoc +28 -0
  42. data/spec/dummy/Rakefile +6 -0
  43. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  44. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  45. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/models/post.rb +4 -0
  48. data/spec/dummy/app/models/tag.rb +4 -0
  49. data/spec/dummy/app/models/user.rb +5 -0
  50. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  51. data/spec/dummy/bin/bundle +3 -0
  52. data/spec/dummy/bin/rails +4 -0
  53. data/spec/dummy/bin/rake +4 -0
  54. data/spec/dummy/bin/setup +29 -0
  55. data/spec/dummy/config.ru +4 -0
  56. data/spec/dummy/config/application.rb +26 -0
  57. data/spec/dummy/config/boot.rb +5 -0
  58. data/spec/dummy/config/database.yml +37 -0
  59. data/spec/dummy/config/environment.rb +5 -0
  60. data/spec/dummy/config/environments/development.rb +41 -0
  61. data/spec/dummy/config/environments/production.rb +79 -0
  62. data/spec/dummy/config/environments/test.rb +42 -0
  63. data/spec/dummy/config/initializers/assets.rb +11 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  66. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/spec/dummy/config/initializers/inflections.rb +16 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  69. data/spec/dummy/config/initializers/rails_redshift_replicator.rb +59 -0
  70. data/spec/dummy/config/initializers/session_store.rb +3 -0
  71. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  72. data/spec/dummy/config/locales/en.yml +23 -0
  73. data/spec/dummy/config/locales/rails_redshift_replicator.en.yml +19 -0
  74. data/spec/dummy/config/routes.rb +4 -0
  75. data/spec/dummy/config/secrets.yml +22 -0
  76. data/spec/dummy/db/development.sqlite3 +0 -0
  77. data/spec/dummy/db/migrate/20160504120421_create_test_tables.rb +40 -0
  78. data/spec/dummy/db/migrate/20160509225445_create_triggers_posts_delete_or_tags_delete_or_users_delete.rb +33 -0
  79. data/spec/dummy/db/migrate/20160511000937_create_rails_redshift_replicator_replications.rails_redshift_replicator.rb +25 -0
  80. data/spec/dummy/db/migrate/20160511000938_create_table_rails_redshift_replicator_deleted_ids.rails_redshift_replicator.rb +9 -0
  81. data/spec/dummy/db/schema.rb +99 -0
  82. data/spec/dummy/db/test.sqlite3 +0 -0
  83. data/spec/dummy/log/development.log +1623 -0
  84. data/spec/dummy/log/test.log +95379 -0
  85. data/spec/dummy/public/404.html +67 -0
  86. data/spec/dummy/public/422.html +67 -0
  87. data/spec/dummy/public/500.html +66 -0
  88. data/spec/dummy/public/favicon.ico +0 -0
  89. data/spec/dummy/rails_redshift_replicator_development +0 -0
  90. data/spec/factories/rails_redshift_replicator_replications.rb +31 -0
  91. data/spec/integration/rails_redshift_replicator_spec.rb +148 -0
  92. data/spec/integration/setup_spec.rb +149 -0
  93. data/spec/lib/rails_redshift_replicator/deleter_spec.rb +90 -0
  94. data/spec/lib/rails_redshift_replicator/exporters/base_spec.rb +326 -0
  95. data/spec/lib/rails_redshift_replicator/exporters/full_replicator_spec.rb +33 -0
  96. data/spec/lib/rails_redshift_replicator/exporters/identity_replicator_spec.rb +40 -0
  97. data/spec/lib/rails_redshift_replicator/exporters/timed_replicator_spec.rb +43 -0
  98. data/spec/lib/rails_redshift_replicator/file_manager_spec.rb +90 -0
  99. data/spec/lib/rails_redshift_replicator/importers/base_spec.rb +102 -0
  100. data/spec/lib/rails_redshift_replicator/importers/full_replicator_spec.rb +27 -0
  101. data/spec/lib/rails_redshift_replicator/importers/identity_replicator_spec.rb +26 -0
  102. data/spec/lib/rails_redshift_replicator/importers/timed_replicator_spec.rb +26 -0
  103. data/spec/lib/rails_redshift_replicator/model/extension_spec.rb +36 -0
  104. data/spec/lib/rails_redshift_replicator/replicable_spec.rb +230 -0
  105. data/spec/lib/rails_redshift_replicator/rlogger_spec.rb +22 -0
  106. data/spec/lib/rails_redshift_replicator/tools/analyze_spec.rb +15 -0
  107. data/spec/lib/rails_redshift_replicator/tools/vacuum_spec.rb +65 -0
  108. data/spec/lib/rails_redshift_replicator_spec.rb +110 -0
  109. data/spec/models/rails_redshift_replicator/replication_spec.rb +104 -0
  110. data/spec/spec_helper.rb +36 -0
  111. data/spec/support/csv/invalid_user.csv +12 -0
  112. data/spec/support/csv/valid_post.csv +2 -0
  113. data/spec/support/csv/valid_tags_users.csv +1 -0
  114. data/spec/support/csv/valid_user.csv +2 -0
  115. data/spec/support/rails_redshift_replicator_helpers.rb +95 -0
  116. metadata +430 -0
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsRedshiftReplicator::Deleter do
4
+ let(:replicable) { RailsRedshiftReplicator.replicables[:users] }
5
+ let(:deleter) { RailsRedshiftReplicator::Deleter.new(replicable) }
6
+ describe '#purge_deleted' do
7
+ before { 2.times { User.create } }
8
+ it 'purge deleted records for the given table' do
9
+ expect(deleter.deleted_ids.count).to eq 0
10
+ User.first.destroy
11
+ expect(deleter.deleted_ids.count).to eq 1
12
+ deleter.purge_deleted
13
+ expect(deleter.deleted_ids.count).to eq 0
14
+ end
15
+ end
16
+ describe '#delete_on_target' do
17
+ it 'calls redshift to delete' do
18
+ expect(RailsRedshiftReplicator.connection).to receive(:exec).and_return(double("result", result_status: 1))
19
+ deleter.delete_on_target
20
+ end
21
+ end
22
+ describe '#handle_delete_propagation' do
23
+ context 'when tracking deleted' do
24
+ context 'and has deleted records' do
25
+ before do
26
+ allow(RailsRedshiftReplicator.connection).to receive(:exec)
27
+ 2.times { User.create }
28
+ User.delete_all
29
+ end
30
+ it 'calls #delete_on_target' do
31
+ expect(deleter).to receive(:delete_on_target)
32
+ deleter.handle_delete_propagation
33
+ end
34
+ context 'if delete on target is success' do
35
+ before { allow(RailsRedshiftReplicator.connection).to receive(:exec).and_return(double("result", result_status: 1)) }
36
+ it 'calls #purge_deleted' do
37
+ expect(deleter).to receive(:purge_deleted)
38
+ deleter.handle_delete_propagation
39
+ end
40
+ end
41
+ context 'if delete on target fails' do
42
+ before { allow(RailsRedshiftReplicator.connection).to receive(:exec).and_return(double("result", result_status: 0)) }
43
+ it 'does not call calls #purge_deleted and logs error' do
44
+ expect(deleter).not_to receive(:purge_deleted)
45
+ expect(RailsRedshiftReplicator.logger).to receive(:error)
46
+ deleter.handle_delete_propagation
47
+ end
48
+ end
49
+ end
50
+ context 'and without deleted records' do
51
+ it 'does not call #delete_on_target' do
52
+ expect(deleter).not_to receive(:delete_on_target)
53
+ deleter.handle_delete_propagation
54
+ end
55
+ end
56
+ end
57
+ context 'if not tracking deleted' do
58
+ before { replicable.instance_variable_set("@tracking", false) }
59
+ it 'does not call #delete_on_target' do
60
+ expect(deleter).not_to receive(:delete_on_target)
61
+ deleter.handle_delete_propagation
62
+ end
63
+ end
64
+ end
65
+ describe '#deleted_ids' do
66
+ it 'returns array of deleted ids' do
67
+ user = create :user
68
+ id = user.id
69
+ User.delete_all
70
+ expect(deleter.deleted_ids).to eq [id.to_s]
71
+ end
72
+ end
73
+ describe '#has_deleted_ids?' do
74
+ context 'when there are deleted records' do
75
+ before do
76
+ 2.times { User.create }
77
+ User.delete_all
78
+ end
79
+ it 'returns true' do
80
+ expect(deleter.has_deleted_ids?).to be_truthy
81
+ end
82
+ end
83
+ context "when there aren't deleted records" do
84
+ it 'returns false' do
85
+ expect(deleter.has_deleted_ids?).to be_falsy
86
+ end
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,326 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext/time/calculations'
3
+
4
+ describe RailsRedshiftReplicator::Exporters::Base do
5
+ let(:user_replicable) { RailsRedshiftReplicator::Replicable.new(:identity_replicator, source_table: :users)}
6
+ let(:post_replicable) { RailsRedshiftReplicator::Replicable.new(:timed_replicator, source_table: :posts)}
7
+ let(:full_replicable) { RailsRedshiftReplicator::Replicable.new(:full_replicator, source_table: :tags_users)}
8
+ let(:user_exporter) { RailsRedshiftReplicator::Exporters::IdentityReplicator.new(user_replicable) }
9
+ let(:post_exporter) { RailsRedshiftReplicator::Exporters::TimedReplicator.new(post_replicable) }
10
+ let(:habtm_exporter) { RailsRedshiftReplicator::Exporters::FullReplicator.new(full_replicable) }
11
+ before { RailsRedshiftReplicator.debug_mode = true }
12
+
13
+
14
+
15
+ describe 'Instance Methods' do
16
+ describe "#table_indexes" do
17
+ context "with User model" do
18
+ it "returns user's table indexes" do
19
+ expect(user_exporter.table_indexes).to contain_exactly("id", "login", "age")
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#upload" do
25
+ context "with csv format" do
26
+ let(:files) { %w(file1 file2) }
27
+ before { user_exporter.replication = create(:redshift_replication, source_table: "users", export_format: "csv") }
28
+ before { user_exporter.file_names = files }
29
+ context 'when exporter is in an error state' do
30
+ before { user_exporter.errors = "some error"}
31
+ it 'returns nil' do
32
+ expect(user_exporter.upload).to be_nil
33
+ end
34
+ end
35
+ context 'when exporter is valid', focus: true do
36
+ it "calls csv uploader" do
37
+ expect(user_exporter.file_manager).to receive(:upload_csv).with(files)
38
+ user_exporter.upload
39
+ end
40
+ it "changes replication state to uploaded" do
41
+ expect(user_exporter.file_manager).to receive(:upload_csv).with(files)
42
+ user_exporter.upload
43
+ expect(user_exporter.replication.state).to eq "uploaded"
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#connection_adapter' do
50
+ context 'when Mysql2' do
51
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return("Mysql2") }
52
+ it 'returns RailsRedshiftReplicator::Adapters::Mysql2' do
53
+ expect(user_exporter.connection_adapter).to be_an RailsRedshiftReplicator::Adapters::Mysql2
54
+ end
55
+ end
56
+ context 'when PostgreSQL' do
57
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return("PostgreSQL") }
58
+ it 'returns RailsRedshiftReplicator::Adapters::Postgresql' do
59
+ expect(user_exporter.connection_adapter).to be_an RailsRedshiftReplicator::Adapters::PostgreSQL
60
+ end
61
+ end
62
+ context 'when SQLite' do
63
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return("SQLite") }
64
+ it 'returns RailsRedshiftReplicator::Adapters::Sqlite' do
65
+ expect(user_exporter.connection_adapter).to be_an RailsRedshiftReplicator::Adapters::SQLite
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#has_index?" do
71
+ before { allow(user_exporter).to receive(:table_indexes).and_return [:id, :updated_at] }
72
+ context "when replication_field has an index" do
73
+ before { allow(user_exporter).to receive(:replication_field).and_return(:updated_at) }
74
+ it "returns true" do
75
+ expect(user_exporter).to be_has_index
76
+ end
77
+ end
78
+ context "when replication_field doesn't have an index" do
79
+ before { allow(user_exporter).to receive(:replication_field).and_return(:login) }
80
+ it "returns false" do
81
+ expect(user_exporter).not_to be_has_index
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ describe "#replication_field" do
88
+ it "returns the replicable replication_field" do
89
+ expect(RailsRedshiftReplicator::Exporters::Base.new(user_replicable).replication_field).to eq 'id'
90
+ end
91
+ end
92
+
93
+ describe "#check_target_table" do
94
+ context "when the target table exists on redshift" do
95
+ before { recreate_users_table }
96
+ it "doesn't set an error" do
97
+ user_exporter.check_target_table
98
+ expect(user_exporter.errors).to be_nil
99
+ end
100
+ end
101
+ context "when the target table doesn't exist on redshift" do
102
+ before(:all) { drop_redshift_table(:users) }
103
+ after(:all) { recreate_users_table }
104
+ it "sets the exporter to an error state" do
105
+ user_exporter.check_target_table
106
+ expect(user_exporter.errors).to be_present
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#query_command' do
112
+ before { allow(user_exporter).to receive(:fields_to_sync).and_return(%w(id user_id publication_id)) }
113
+ before { allow(user_exporter).to receive(:last_record).and_return(10) }
114
+
115
+ let(:sql) { "SELECT id,user_id,publication_id FROM users WHERE 1=1 AND id > '5' AND id <= '10' OR id IS NULL" }
116
+ context 'when adapter is Mysql2' do
117
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return('Mysql2') }
118
+ it "executes query with streaming option" do
119
+ expect(user_exporter.ar_client.instance_variable_get("@connection")).to receive(:query).with(sql, stream: true)
120
+ user_exporter.records(5)
121
+ end
122
+ end
123
+ context "when adapter is SQLite", focus: true do
124
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return('SQLite') }
125
+ it "executes query without other arguments" do
126
+ expect(user_exporter.ar_client).to receive(:exec_query).with(sql)
127
+ user_exporter.records(5)
128
+ end
129
+ end
130
+ context "when adapter is PostgreSQL" do
131
+ before { allow(user_exporter.ar_client).to receive(:adapter_name).and_return('PostgreSQL') }
132
+ xit "executes query using single row mode" do
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#records' do
138
+
139
+ end
140
+
141
+ describe "#build_query_sql" do
142
+ before { allow(user_exporter).to receive(:fields_to_sync).and_return(%w(id user_id publication_id)) }
143
+ context "when there's a last record" do
144
+ before { allow(user_exporter).to receive(:last_record).and_return(10) }
145
+ context "when an initial record is given" do
146
+ let(:sql) { "SELECT id,user_id,publication_id FROM users WHERE 1=1 AND id > '5' AND id <= '10' OR id IS NULL" }
147
+ it "builds correct sql" do
148
+ expect(user_exporter.build_query_sql(5)).to eq sql
149
+ end
150
+ end
151
+ context "when an initial record isn't given" do
152
+ let(:sql) { "SELECT id,user_id,publication_id FROM users WHERE 1=1 AND id <= '10' OR id IS NULL" }
153
+ it "builds correct sql" do
154
+ expect(user_exporter.build_query_sql).to eq sql
155
+ end
156
+ end
157
+ end
158
+ context "when a last_record can't be found" do
159
+ before { allow(user_exporter).to receive(:last_record).and_return(nil) }
160
+ context "when an initial record is given" do
161
+ let(:sql) { "SELECT id,user_id,publication_id FROM users WHERE 1=1 AND id > '5'" }
162
+ it "builds correct sql" do
163
+ expect(user_exporter.build_query_sql(5)).to eq sql
164
+ end
165
+ end
166
+ context "when an initial record isn't given" do
167
+ let(:sql) { "SELECT id,user_id,publication_id FROM users WHERE 1=1" }
168
+ it "builds correct sql" do
169
+ expect(user_exporter.build_query_sql).to eq sql
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ describe "#from_record" do
176
+ context "when there's a previous replication record" do
177
+ let!(:replication) { create :redshift_replication, target_table: 'users', last_record: 1 }
178
+ let!(:replication2) { create :redshift_replication, target_table: 'users', last_record: 5 }
179
+ it "returns the id or timestamp of the last_record on the most recent complete replication" do
180
+ expect(user_exporter.from_record).to eq '5'
181
+ end
182
+ end
183
+ context "when there isn't a previous replication record" do
184
+ it "returns nil" do
185
+ expect(user_exporter.from_record).to be_nil
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "#initialize_replication" do
191
+ before do
192
+ allow(user_exporter).to receive(:last_record).and_return(3)
193
+ allow(user_exporter).to receive(:from_record).and_return(1)
194
+ user_exporter.initialize_replication("replication_file", "csv", 2)
195
+ end
196
+ let(:replication) { user_exporter.replication }
197
+
198
+ it "creates a replication record" do
199
+ expect(replication.export_format).to eq "csv"
200
+ expect(replication.slices).to eq 2
201
+ expect(replication.record_count).to be_nil
202
+ expect(replication.key).to eq "rrr/users/replication_file"
203
+ expect(replication.last_record).to eq '3'
204
+ expect(replication.first_record).to eq '1'
205
+ expect(replication.state).to eq "exporting"
206
+ expect(replication.replication_type).to eq "identity_replicator"
207
+ expect(replication.target_table).to eq "users"
208
+ expect(replication.source_table).to eq "users"
209
+ end
210
+ end
211
+
212
+ describe "#export" do
213
+ before { allow(user_exporter).to receive(:fields_to_sync).and_return(%w(id login age)) }
214
+ context "when there are incomplete replications" do
215
+ before { create :redshift_replication, source_table: "users", state: "uploaded" }
216
+ it "doesn't create replication record" do
217
+ user_exporter.export
218
+ expect(user_exporter.replication).to be_nil
219
+ end
220
+ it "returns nil" do
221
+ expect(user_exporter.export).to be_nil
222
+ end
223
+ end
224
+ context "when there are no incomplete replications" do
225
+ before { create :redshift_replication, source_table: "users", state: "imported" }
226
+ context "and there are records to export" do
227
+ before { create :user }
228
+ context 'and exporter is in error state' do
229
+ before { user_exporter.errors = 'some error' }
230
+ it 'returns false' do
231
+ expect(user_exporter.export).to be_nil
232
+ end
233
+ end
234
+ context 'and exporter is valid' do
235
+ it "creates export files" do
236
+ file_paths = user_exporter.export
237
+ expect(file_paths).to be_an_instance_of Array
238
+ expect(file_paths).to be_present
239
+ end
240
+ it "flags replication as exported" do
241
+ user_exporter.export
242
+ expect(user_exporter.replication.state).to eq "exported"
243
+ end
244
+ end
245
+ end
246
+ context "and there aren't any records to export" do
247
+ it "doesn't create replication record" do
248
+ user_exporter.export
249
+ expect(user_exporter.replication).to be_nil
250
+ end
251
+ it "retorns nil" do
252
+ expect(user_exporter.export).to be_nil
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ describe "#last_replication" do
259
+ let!(:replication1) { create :redshift_replication, source_table: "users" }
260
+ let!(:replication2) { create :redshift_replication, source_table: "users" }
261
+ let!(:replication3) { create :redshift_replication, source_table: "posts" }
262
+ it "retuns the last replication record for a given table" do
263
+ expect(user_exporter.last_replication).to eq replication2
264
+ end
265
+ end
266
+
267
+ describe "#export_and_upload" do
268
+ context "when the target table doesn't exist on redshift" do
269
+ before(:all) { drop_redshift_table(:users) }
270
+ after(:all) { recreate_users_table }
271
+ it "doesn't call #export" do
272
+ expect(user_exporter.export_and_upload).not_to receive(:export)
273
+ end
274
+ end
275
+ context "when the target table exists on redshift" do
276
+ before(:all) { recreate_users_table }
277
+ it "calls #export" do
278
+ expect(user_exporter).to receive(:export)
279
+ user_exporter.export_and_upload
280
+ end
281
+ context "when there are records to export" do
282
+ before { allow(user_exporter).to receive(:export).and_return("file") }
283
+ it "calls #upload" do
284
+ expect(user_exporter).to receive(:upload)
285
+ user_exporter.export_and_upload
286
+ end
287
+ end
288
+ context "when there are no records to export" do
289
+ it "doesn't call #upload" do
290
+ expect(user_exporter.export_and_upload).not_to receive(:upload)
291
+ end
292
+ end
293
+
294
+ end
295
+ end
296
+
297
+ describe "#last_record" do
298
+ let!(:user1) { create :user }
299
+ let!(:post1) { create :post }
300
+ context "when the last replication record uses time based replication" do
301
+ it "returns a date" do
302
+ Timecop.freeze Date.tomorrow do
303
+ post2 = create :post
304
+ formatted = Time.parse(post_exporter.last_record).to_s(:db)
305
+ expect(formatted).to eq post2.updated_at.to_s(:db)
306
+ end
307
+ end
308
+ end
309
+ context "when the last replication record uses an identity column" do
310
+ let!(:post) { create :post }
311
+ it "returns the id" do
312
+ user1 = create :user
313
+ user2 = create :user
314
+ expect(user_exporter.last_record.to_s).to eq user2.id.to_s
315
+ end
316
+ end
317
+ end
318
+
319
+ describe "fields_to_sync" do
320
+ before { recreate_users_table }
321
+ it "returns list of columns on the target table on redshift" do
322
+ expect(user_exporter.fields_to_sync).to contain_exactly("age", "confirmed", "created_at", "id", "login", "updated_at")
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsRedshiftReplicator::Exporters::FullReplicator do
4
+ let(:file_manager) { RailsRedshiftReplicator::FileManager.new}
5
+ let(:replicable) { RailsRedshiftReplicator::Replicable.new(:full_replicator, source_table: :tags_users) }
6
+ let(:s3_bucket) { Aws::S3::Bucket.new(name: RailsRedshiftReplicator.s3_bucket_params[:bucket], client: file_manager.s3_client) }
7
+ describe "#replication_field", :focus do
8
+ it "returns the replicable replication_field" do
9
+ expect(RailsRedshiftReplicator::Exporters::FullReplicator.new(replicable).replication_field).to be_nil
10
+ end
11
+ end
12
+ describe 'Integration Test' do
13
+ before(:all) { recreate_tags_users_table }
14
+ it "exports full replicator type replication" do
15
+ model = :tags_users
16
+ # first export
17
+ tag = create :tag
18
+ user = create :user
19
+ tag.users << user
20
+ RailsRedshiftReplicator::Exporters::FullReplicator.new(replicable).export_and_upload
21
+ replication1 = RailsRedshiftReplicator::Replication.from_table("tags_users").last
22
+ expect(replication1.state).to eq "uploaded"
23
+ expect(replication1.record_count).to eq 1
24
+ file_body = s3_bucket.object("#{replication1.key}.aa").get.body.read
25
+ expect(file_body).to match(/#{user.id},#{tag.id}/)
26
+ replication1.imported!
27
+ # export without new records
28
+ RailsRedshiftReplicator::Exporters::FullReplicator.new(replicable).export_and_upload
29
+ replication2 = RailsRedshiftReplicator::Replication.from_table("tags_users").last
30
+ expect(replication2.record_count).to eq 1
31
+ end
32
+ end
33
+ end