tapsoob 0.8.4-java → 0.8.5-java
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/.gitlab-ci.yml +5 -3
- data/lib/tapsoob/schema.rb +13 -9
- data/lib/tapsoob/version.rb +1 -1
- data/spec/integration/postgres_spec.rb +15 -4
- data/spec/spec_helper.rb +2 -5
- data/spec/support/fixtures.rb +1 -1
- data/spec/support/shared_examples/round_trip.rb +35 -21
- metadata +1 -2
- data/spec/system/large_dataset_spec.rb +0 -163
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb9e75e4bea67137c1dff8803d7c49dbe96b6c46740eba03bb372cb8bf2d0c14
|
|
4
|
+
data.tar.gz: dda86ad6ec9d02734fce9f1ea9cedeafd745ebf7a0c316a0a329dfa461c61331
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '093b732d4cdc720f2e6dae958077581f31791236ea40ef57b84ef5a11f7d4282fe79ec8f6cbfffa1009d08c98f8e939a33440138cc901a61774f1f4d368f6ee6'
|
|
7
|
+
data.tar.gz: cd1fb4eaa2eab1aeb561a53ee4d1b1b6aef040ab461917c2577a75479060a692fb90ce9aedf601e04acd5fe8cdb8882759209b6fce9b8997c88c99336a20a6c3
|
data/.gitlab-ci.yml
CHANGED
|
@@ -22,10 +22,11 @@ stages:
|
|
|
22
22
|
rules:
|
|
23
23
|
- if: $CI_COMMIT_TAG
|
|
24
24
|
when: never
|
|
25
|
+
- if: $CI_COMMIT_MESSAGE =~ /Bumped version/
|
|
26
|
+
when: never
|
|
25
27
|
- when: on_success
|
|
26
28
|
variables:
|
|
27
29
|
INTEGRATION_TESTS: "1"
|
|
28
|
-
SYSTEM_TESTS: "1"
|
|
29
30
|
before_script:
|
|
30
31
|
- apt-get update -qq && apt-get install -y -qq git
|
|
31
32
|
- bundle install --quiet
|
|
@@ -49,10 +50,9 @@ test-sqlite:
|
|
|
49
50
|
SRC_DATABASE_URL: "sqlite://tmp/tapsoob_src.db"
|
|
50
51
|
DST_DATABASE_URL: "sqlite://tmp/tapsoob_dst.db"
|
|
51
52
|
INTEGRATION_TESTS: "1"
|
|
52
|
-
SYSTEM_TESTS: "1"
|
|
53
53
|
script:
|
|
54
54
|
- mkdir -p tmp
|
|
55
|
-
- bundle exec rspec spec/unit spec/integration/sqlite_spec.rb
|
|
55
|
+
- bundle exec rspec spec/unit spec/integration/sqlite_spec.rb
|
|
56
56
|
--format progress
|
|
57
57
|
--format RspecJunitFormatter --out rspec.xml
|
|
58
58
|
|
|
@@ -142,6 +142,8 @@ test-jruby:
|
|
|
142
142
|
rules:
|
|
143
143
|
- if: $CI_COMMIT_TAG
|
|
144
144
|
when: never
|
|
145
|
+
- if: $CI_COMMIT_MESSAGE =~ /Bumped version/
|
|
146
|
+
when: never
|
|
145
147
|
- when: on_success
|
|
146
148
|
before_script:
|
|
147
149
|
- apt-get update -qq && apt-get install -y -qq git libsqlite3-dev
|
data/lib/tapsoob/schema.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'sequel/extensions/schema_dumper'
|
|
|
4
4
|
require 'sequel/extensions/migration'
|
|
5
5
|
require 'erb'
|
|
6
6
|
require 'json'
|
|
7
|
+
require 'tapsoob/log'
|
|
7
8
|
|
|
8
9
|
module Tapsoob
|
|
9
10
|
module Schema
|
|
@@ -158,15 +159,18 @@ END_MIG
|
|
|
158
159
|
end
|
|
159
160
|
|
|
160
161
|
def reset_db_sequences(database_url)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
Sequel.connect(database_url) do |db|
|
|
163
|
+
db.extension :schema_dumper
|
|
164
|
+
next unless db.respond_to?(:reset_primary_key_sequence)
|
|
165
|
+
db.tables.each do |table|
|
|
166
|
+
pk = db.primary_key(table)
|
|
167
|
+
next unless pk
|
|
168
|
+
pk_type = db.schema(table).find { |col, _| col.to_s == pk.to_s }&.last&.dig(:db_type)
|
|
169
|
+
next unless pk_type&.match?(/int|serial/i)
|
|
170
|
+
db.reset_primary_key_sequence(table)
|
|
171
|
+
rescue Sequel::DatabaseError => e
|
|
172
|
+
Tapsoob.log.warn "Could not reset sequence for table '#{table}': #{e.message.lines.first.chomp}"
|
|
173
|
+
end
|
|
170
174
|
end
|
|
171
175
|
end
|
|
172
176
|
end
|
data/lib/tapsoob/version.rb
CHANGED
|
@@ -107,10 +107,21 @@ RSpec.describe 'PostgreSQL round-trip', :integration do
|
|
|
107
107
|
@src_db.run("DROP TABLE IF EXISTS varchar_pk_table")
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
it '
|
|
111
|
-
expect
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
it 'skips reset without logging a warning (no sequence attached to varchar PK)' do
|
|
111
|
+
expect(Tapsoob.log).not_to receive(:warn)
|
|
112
|
+
Tapsoob::Schema.reset_db_sequences(@src_url)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context 'when reset_primary_key_sequence raises a DatabaseError' do
|
|
117
|
+
it 'logs a warning per failing table and does not re-raise' do
|
|
118
|
+
allow(Sequel).to receive(:connect).and_yield(@src_db)
|
|
119
|
+
allow(@src_db).to receive(:reset_primary_key_sequence).and_raise(
|
|
120
|
+
Sequel::DatabaseError, 'ERROR: identity column type must be smallint, integer, or bigint'
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
expect(Tapsoob.log).to receive(:warn).at_least(:once)
|
|
124
|
+
expect { Tapsoob::Schema.reset_db_sequences(@src_url) }.not_to raise_error
|
|
114
125
|
end
|
|
115
126
|
end
|
|
116
127
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -42,12 +42,9 @@ RSpec.configure do |config|
|
|
|
42
42
|
config.order = :random
|
|
43
43
|
Kernel.srand config.seed
|
|
44
44
|
|
|
45
|
-
# Integration
|
|
45
|
+
# Integration tests require a real DB — skip unless env vars are set.
|
|
46
46
|
config.filter_run_excluding :integration unless ENV['INTEGRATION_TESTS'] || ENV['SRC_DATABASE_URL']
|
|
47
|
-
config.filter_run_excluding :system unless ENV['SYSTEM_TESTS'] || ENV['SRC_DATABASE_URL']
|
|
48
47
|
|
|
49
|
-
config.include DbHelpers,
|
|
50
|
-
config.include DbHelpers, :system
|
|
48
|
+
config.include DbHelpers, :integration
|
|
51
49
|
config.include RoundTripHelper, :integration
|
|
52
|
-
config.include RoundTripHelper, :system
|
|
53
50
|
end
|
data/spec/support/fixtures.rb
CHANGED
|
@@ -6,49 +6,53 @@
|
|
|
6
6
|
# defined in DbHelpers (which delegate to the ivars set in before(:all)).
|
|
7
7
|
|
|
8
8
|
RSpec.shared_examples 'a complete round-trip' do
|
|
9
|
+
# Pull once into a shared dir and reuse across all examples in this group.
|
|
10
|
+
before(:all) do
|
|
11
|
+
@shared_dump_dir = Dir.mktmpdir('tapsoob_shared_')
|
|
12
|
+
pull(src_url, @shared_dump_dir)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
after(:all) do
|
|
16
|
+
FileUtils.rm_rf(@shared_dump_dir)
|
|
17
|
+
end
|
|
18
|
+
|
|
9
19
|
it 'pulls without error' do
|
|
10
|
-
expect
|
|
20
|
+
expect(File).to exist(File.join(@shared_dump_dir, 'schemas'))
|
|
11
21
|
end
|
|
12
22
|
|
|
13
23
|
it 'creates schema dump files for every table' do
|
|
14
|
-
pull(src_url, dump_dir)
|
|
15
24
|
src_db.tables.each do |table|
|
|
16
|
-
expect(File).to exist(File.join(
|
|
25
|
+
expect(File).to exist(File.join(@shared_dump_dir, 'schemas', "#{table}.rb"))
|
|
17
26
|
end
|
|
18
27
|
end
|
|
19
28
|
|
|
20
29
|
it 'creates data dump files for every seeded table' do
|
|
21
|
-
pull(src_url, dump_dir)
|
|
22
30
|
%i[users orders products documents attachments events large_table null_heavy].each do |table|
|
|
23
|
-
expect(File).to exist(File.join(
|
|
31
|
+
expect(File).to exist(File.join(@shared_dump_dir, 'data', "#{table}.json"))
|
|
24
32
|
end
|
|
25
33
|
end
|
|
26
34
|
|
|
27
35
|
it 'pushes without error' do
|
|
28
|
-
|
|
29
|
-
expect { push(dst_url, dump_dir) }.not_to raise_error
|
|
36
|
+
expect { push(dst_url, @shared_dump_dir) }.not_to raise_error
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
it 'preserves row counts for all tables' do
|
|
33
|
-
|
|
40
|
+
push(dst_url, @shared_dump_dir)
|
|
34
41
|
expect_same_counts(src_db, dst_db)
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
it 'preserves NULL values in null_heavy' do
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expect(null_rows).to be > 0
|
|
45
|
+
push(dst_url, @shared_dump_dir)
|
|
46
|
+
expect(dst_db[:null_heavy].where(maybe_name: nil).count).to be > 0
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
it 'preserves string content in users.email' do
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
dst_emails = dst_db[:users].select_map(:email).sort
|
|
47
|
-
expect(dst_emails).to eq(src_emails)
|
|
50
|
+
push(dst_url, @shared_dump_dir)
|
|
51
|
+
expect(dst_db[:users].select_map(:email).sort).to eq(src_db[:users].select_map(:email).sort)
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
it 'preserves BLOB payloads in attachments' do
|
|
51
|
-
|
|
55
|
+
push(dst_url, @shared_dump_dir)
|
|
52
56
|
src_db[:attachments].order(:id).each do |src_row|
|
|
53
57
|
dst_row = dst_db[:attachments][id: src_row[:id]]
|
|
54
58
|
expect(dst_row).not_to be_nil
|
|
@@ -57,7 +61,7 @@ RSpec.shared_examples 'a complete round-trip' do
|
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
it 'preserves large TEXT bodies in documents' do
|
|
60
|
-
|
|
64
|
+
push(dst_url, @shared_dump_dir)
|
|
61
65
|
src_db[:documents].order(:id).each do |src_row|
|
|
62
66
|
dst_row = dst_db[:documents][id: src_row[:id]]
|
|
63
67
|
expect(dst_row[:body]).to eq(src_row[:body])
|
|
@@ -65,19 +69,29 @@ RSpec.shared_examples 'a complete round-trip' do
|
|
|
65
69
|
end
|
|
66
70
|
|
|
67
71
|
it 'handles the no-PK events table' do
|
|
68
|
-
|
|
72
|
+
push(dst_url, @shared_dump_dir)
|
|
69
73
|
expect(dst_db[:events].count).to eq(src_db[:events].count)
|
|
70
74
|
end
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
RSpec.shared_examples 'a parallel round-trip' do |workers:|
|
|
78
|
+
# Pull once, push with parallel workers.
|
|
79
|
+
before(:all) do
|
|
80
|
+
@parallel_dump_dir = Dir.mktmpdir('tapsoob_parallel_')
|
|
81
|
+
pull(src_url, @parallel_dump_dir, parallel: workers)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
after(:all) do
|
|
85
|
+
FileUtils.rm_rf(@parallel_dump_dir)
|
|
86
|
+
end
|
|
87
|
+
|
|
74
88
|
it "preserves row counts with #{workers} parallel workers" do
|
|
75
|
-
|
|
89
|
+
push(dst_url, @parallel_dump_dir, parallel: workers)
|
|
76
90
|
expect_same_counts(src_db, dst_db)
|
|
77
91
|
end
|
|
78
92
|
|
|
79
|
-
it "handles the large_table
|
|
80
|
-
|
|
93
|
+
it "handles the large_table with #{workers} workers" do
|
|
94
|
+
push(dst_url, @parallel_dump_dir, parallel: workers)
|
|
81
95
|
expect(dst_db[:large_table].count).to eq(Fixtures::LARGE_TABLE_ROWS)
|
|
82
96
|
end
|
|
83
97
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tapsoob
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.5
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Félix Bellanger
|
|
@@ -132,7 +132,6 @@ files:
|
|
|
132
132
|
- spec/support/fixtures.rb
|
|
133
133
|
- spec/support/round_trip_helper.rb
|
|
134
134
|
- spec/support/shared_examples/round_trip.rb
|
|
135
|
-
- spec/system/large_dataset_spec.rb
|
|
136
135
|
- spec/unit/tapsoob/chunksize_spec.rb
|
|
137
136
|
- spec/unit/tapsoob/data_stream_spec.rb
|
|
138
137
|
- spec/unit/tapsoob/operation_base_spec.rb
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe 'Large dataset system tests', :system do
|
|
4
|
-
before(:all) do
|
|
5
|
-
@src_url = DbHelpers.adapt_url(ENV.fetch('SRC_DATABASE_URL', 'sqlite://tmp/tapsoob_system_src.db'))
|
|
6
|
-
@dst_url = DbHelpers.adapt_url(ENV.fetch('DST_DATABASE_URL', 'sqlite://tmp/tapsoob_system_dst.db'))
|
|
7
|
-
|
|
8
|
-
FileUtils.mkdir_p('tmp')
|
|
9
|
-
File.delete('tmp/tapsoob_system_src.db') rescue nil
|
|
10
|
-
File.delete('tmp/tapsoob_system_dst.db') rescue nil
|
|
11
|
-
|
|
12
|
-
@src_db = DbHelpers.connect(@src_url)
|
|
13
|
-
@dst_db = DbHelpers.connect(@dst_url)
|
|
14
|
-
|
|
15
|
-
Fixtures.create_tables(@src_db)
|
|
16
|
-
Fixtures.seed(@src_db)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
before(:each) do
|
|
20
|
-
Fixtures.drop_tables(@dst_db)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
after(:all) do
|
|
24
|
-
Fixtures.drop_tables(@src_db)
|
|
25
|
-
Fixtures.drop_tables(@dst_db)
|
|
26
|
-
DbHelpers.disconnect_all
|
|
27
|
-
File.delete('tmp/tapsoob_system_src.db') rescue nil
|
|
28
|
-
File.delete('tmp/tapsoob_system_dst.db') rescue nil
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# ── large_table: intra-table parallelization threshold ───────────────────────
|
|
32
|
-
|
|
33
|
-
describe 'large_table (150K rows)' do
|
|
34
|
-
it 'transfers all rows in serial mode' do
|
|
35
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
36
|
-
expect(dst_db[:large_table].count).to eq(Fixtures::LARGE_TABLE_ROWS)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'transfers all rows with parallel: 2' do
|
|
40
|
-
round_trip(src_url, dst_url, dump_dir, parallel: 2)
|
|
41
|
-
expect(dst_db[:large_table].count).to eq(Fixtures::LARGE_TABLE_ROWS)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'transfers all rows with parallel: 4' do
|
|
45
|
-
round_trip(src_url, dst_url, dump_dir, parallel: 4)
|
|
46
|
-
expect(dst_db[:large_table].count).to eq(Fixtures::LARGE_TABLE_ROWS)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'has no duplicate rows after parallel pull' do
|
|
50
|
-
round_trip(src_url, dst_url, dump_dir, parallel: 4)
|
|
51
|
-
total = dst_db[:large_table].count
|
|
52
|
-
distinct = dst_db[:large_table].select(:id).distinct.count
|
|
53
|
-
expect(distinct).to eq(total)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# ── documents: large TEXT columns ────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
describe 'documents table (large TEXT)' do
|
|
60
|
-
it 'preserves body content exactly' do
|
|
61
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
62
|
-
src_db[:documents].order(:id).each do |src_row|
|
|
63
|
-
dst_row = dst_db[:documents][id: src_row[:id]]
|
|
64
|
-
expect(dst_row[:body]).to eq(src_row[:body]),
|
|
65
|
-
"body mismatch for document #{src_row[:id]}: " \
|
|
66
|
-
"src=#{src_row[:body]&.length} bytes dst=#{dst_row[:body]&.length} bytes"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it 'handles documents with nil body' do
|
|
71
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
72
|
-
expect(dst_db[:documents].where(body: nil).count).to eq(src_db[:documents].where(body: nil).count)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# ── attachments: BLOB encoding/decoding ──────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
describe 'attachments table (binary BLOBs up to 256 KB)' do
|
|
79
|
-
it 'preserves every byte of every payload' do
|
|
80
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
81
|
-
mismatch_count = 0
|
|
82
|
-
src_db[:attachments].order(:id).each do |src_row|
|
|
83
|
-
dst_row = dst_db[:attachments][id: src_row[:id]]
|
|
84
|
-
mismatch_count += 1 unless dst_row[:payload].to_s.bytes == src_row[:payload].to_s.bytes
|
|
85
|
-
end
|
|
86
|
-
expect(mismatch_count).to eq(0)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
it 'preserves size_bytes metadata' do
|
|
90
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
91
|
-
src_db[:attachments].order(:id).each do |src_row|
|
|
92
|
-
dst_row = dst_db[:attachments][id: src_row[:id]]
|
|
93
|
-
expect(dst_row[:size_bytes]).to eq(src_row[:size_bytes])
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# ── null_heavy: NULL preservation ────────────────────────────────────────────
|
|
99
|
-
|
|
100
|
-
describe 'null_heavy table' do
|
|
101
|
-
it 'preserves NULLs in every nullable column' do
|
|
102
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
103
|
-
%i[maybe_name maybe_number maybe_score maybe_date maybe_text].each do |col|
|
|
104
|
-
src_nulls = src_db[:null_heavy].where(col => nil).count
|
|
105
|
-
dst_nulls = dst_db[:null_heavy].where(col => nil).count
|
|
106
|
-
expect(dst_nulls).to eq(src_nulls),
|
|
107
|
-
"NULL count mismatch for null_heavy.#{col}: src=#{src_nulls} dst=#{dst_nulls}"
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# ── events: table without primary key ────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
describe 'events table (no primary key)' do
|
|
115
|
-
it 'uses the Base (non-keyed) stream' do
|
|
116
|
-
round_trip(src_url, dst_url, dump_dir)
|
|
117
|
-
expect(dst_db[:events].count).to eq(src_db[:events].count)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# ── adaptive chunksize: very small chunks ────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
describe 'adaptive chunksize under load' do
|
|
124
|
-
it 'completes with chunksize=1 (extreme case)' do
|
|
125
|
-
small_src = DbHelpers.adapt_url('sqlite://tmp/tapsoob_small_src.db')
|
|
126
|
-
small_dst = DbHelpers.adapt_url('sqlite://tmp/tapsoob_small_dst.db')
|
|
127
|
-
small_dir = Dir.mktmpdir
|
|
128
|
-
|
|
129
|
-
begin
|
|
130
|
-
sdb = DbHelpers.connect(small_src)
|
|
131
|
-
sdb.create_table!(:small_test) { primary_key :id; String :v, size: 50 }
|
|
132
|
-
100.times { |i| sdb[:small_test].insert(v: "row_#{i}") }
|
|
133
|
-
|
|
134
|
-
round_trip(small_src, small_dst, small_dir, default_chunksize: 1)
|
|
135
|
-
expect(DbHelpers.connect(small_dst)[:small_test].count).to eq(100)
|
|
136
|
-
ensure
|
|
137
|
-
FileUtils.rm_rf(small_dir)
|
|
138
|
-
File.delete('tmp/tapsoob_small_src.db') rescue nil
|
|
139
|
-
File.delete('tmp/tapsoob_small_dst.db') rescue nil
|
|
140
|
-
# Reconnect suite DBs after disconnect_all clears the pool
|
|
141
|
-
DbHelpers.disconnect_all
|
|
142
|
-
@src_db = DbHelpers.connect(@src_url)
|
|
143
|
-
@dst_db = DbHelpers.connect(@dst_url)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# ── FK order: orders depends on users ────────────────────────────────────────
|
|
149
|
-
|
|
150
|
-
describe 'foreign key dependency ordering' do
|
|
151
|
-
it 'pushes users before orders (table_order.txt respected)' do
|
|
152
|
-
pull(src_url, dump_dir)
|
|
153
|
-
order_file = File.join(dump_dir, 'table_order.txt')
|
|
154
|
-
if File.exist?(order_file)
|
|
155
|
-
order = File.readlines(order_file).map(&:strip)
|
|
156
|
-
users_idx = order.index('users')
|
|
157
|
-
orders_idx = order.index('orders')
|
|
158
|
-
expect(users_idx).to be < orders_idx if users_idx && orders_idx
|
|
159
|
-
end
|
|
160
|
-
expect { push(dst_url, dump_dir) }.not_to raise_error
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|