csvops 0.6.0.alpha → 0.7.0.alpha
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/README.md +51 -12
- data/docs/architecture.md +61 -4
- data/docs/release-v0.7.0-alpha.md +87 -0
- data/lib/csvtool/application/use_cases/run_csv_split.rb +97 -0
- data/lib/csvtool/cli.rb +5 -1
- data/lib/csvtool/domain/csv_split_session/split_options.rb +27 -0
- data/lib/csvtool/domain/csv_split_session/split_session.rb +20 -0
- data/lib/csvtool/domain/csv_split_session/split_source.rb +17 -0
- data/lib/csvtool/infrastructure/csv/csv_splitter.rb +64 -0
- data/lib/csvtool/infrastructure/output/csv_split_manifest_writer.rb +20 -0
- data/lib/csvtool/interface/cli/errors/presenter.rb +8 -0
- data/lib/csvtool/interface/cli/menu_loop.rb +5 -2
- data/lib/csvtool/interface/cli/prompts/chunk_size_prompt.rb +21 -0
- data/lib/csvtool/interface/cli/prompts/split_manifest_prompt.rb +30 -0
- data/lib/csvtool/interface/cli/prompts/split_output_prompt.rb +38 -0
- data/lib/csvtool/interface/cli/workflows/builders/csv_split_session_builder.rb +44 -0
- data/lib/csvtool/interface/cli/workflows/presenters/csv_split_presenter.rb +26 -0
- data/lib/csvtool/interface/cli/workflows/run_csv_split_workflow.rb +89 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_split/build_session_step.rb +30 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_inputs_step.rb +43 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step.rb +30 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step.rb +31 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_split/execute_step.rb +36 -0
- data/lib/csvtool/version.rb +1 -1
- data/test/csvtool/application/use_cases/run_csv_split_test.rb +124 -0
- data/test/csvtool/cli_test.rb +76 -29
- data/test/csvtool/infrastructure/csv/csv_splitter_test.rb +68 -0
- data/test/csvtool/infrastructure/output/csv_split_manifest_writer_test.rb +25 -0
- data/test/csvtool/interface/cli/menu_loop_test.rb +81 -130
- data/test/csvtool/interface/cli/prompts/chunk_size_prompt_test.rb +17 -0
- data/test/csvtool/interface/cli/prompts/split_manifest_prompt_test.rb +42 -0
- data/test/csvtool/interface/cli/prompts/split_output_prompt_test.rb +22 -0
- data/test/csvtool/interface/cli/workflows/builders/csv_split_session_builder_test.rb +30 -0
- data/test/csvtool/interface/cli/workflows/presenters/csv_split_presenter_test.rb +26 -0
- data/test/csvtool/interface/cli/workflows/run_csv_split_workflow_test.rb +200 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_split/build_session_step_test.rb +40 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_inputs_step_test.rb +64 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step_test.rb +30 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step_test.rb +32 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_split/execute_step_test.rb +83 -0
- data/test/fixtures/split_people_25.csv +26 -0
- metadata +34 -1
data/test/csvtool/cli_test.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "../test_helper"
|
|
4
4
|
require "csvtool/cli"
|
|
5
5
|
require "tmpdir"
|
|
6
|
+
require "fileutils"
|
|
6
7
|
|
|
7
8
|
class TestCli < Minitest::Test
|
|
8
9
|
def fixture_path(name)
|
|
@@ -11,11 +12,57 @@ class TestCli < Minitest::Test
|
|
|
11
12
|
|
|
12
13
|
def test_menu_can_exit_cleanly
|
|
13
14
|
output = StringIO.new
|
|
14
|
-
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new("
|
|
15
|
+
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new("7\n"), stdout: output, stderr: StringIO.new)
|
|
15
16
|
assert_equal 0, status
|
|
16
17
|
assert_includes output.string, "CSV Tool Menu"
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
def test_split_workflow_splits_csv_in_menu_flow
|
|
21
|
+
output = StringIO.new
|
|
22
|
+
Dir.mktmpdir do |dir|
|
|
23
|
+
source_path = File.join(dir, "people.csv")
|
|
24
|
+
FileUtils.cp(fixture_path("split_people_25.csv"), source_path)
|
|
25
|
+
input = [
|
|
26
|
+
"6",
|
|
27
|
+
source_path,
|
|
28
|
+
"",
|
|
29
|
+
"",
|
|
30
|
+
"10",
|
|
31
|
+
"",
|
|
32
|
+
"",
|
|
33
|
+
"",
|
|
34
|
+
"",
|
|
35
|
+
"7"
|
|
36
|
+
].join("\n") + "\n"
|
|
37
|
+
|
|
38
|
+
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
39
|
+
|
|
40
|
+
assert_equal 0, status
|
|
41
|
+
assert_includes output.string, "Chunks written: 3"
|
|
42
|
+
assert File.file?(File.join(dir, "people_part_001.csv"))
|
|
43
|
+
assert File.file?(File.join(dir, "people_part_002.csv"))
|
|
44
|
+
assert File.file?(File.join(dir, "people_part_003.csv"))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_split_workflow_invalid_chunk_size_returns_to_menu
|
|
49
|
+
output = StringIO.new
|
|
50
|
+
input = [
|
|
51
|
+
"6",
|
|
52
|
+
fixture_path("sample_people.csv"),
|
|
53
|
+
"",
|
|
54
|
+
"",
|
|
55
|
+
"0",
|
|
56
|
+
"7"
|
|
57
|
+
].join("\n") + "\n"
|
|
58
|
+
|
|
59
|
+
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
60
|
+
|
|
61
|
+
assert_equal 0, status
|
|
62
|
+
assert_includes output.string, "Chunk size must be a positive integer."
|
|
63
|
+
assert_operator output.string.scan("CSV Tool Menu").length, :>=, 2
|
|
64
|
+
end
|
|
65
|
+
|
|
19
66
|
def test_end_to_end_console_happy_path_prints_expected_values
|
|
20
67
|
input = [
|
|
21
68
|
"1",
|
|
@@ -26,7 +73,7 @@ class TestCli < Minitest::Test
|
|
|
26
73
|
"",
|
|
27
74
|
"y",
|
|
28
75
|
"",
|
|
29
|
-
"
|
|
76
|
+
"7"
|
|
30
77
|
].join("\n") + "\n"
|
|
31
78
|
|
|
32
79
|
output = StringIO.new
|
|
@@ -58,7 +105,7 @@ class TestCli < Minitest::Test
|
|
|
58
105
|
"2",
|
|
59
106
|
"3",
|
|
60
107
|
"",
|
|
61
|
-
"
|
|
108
|
+
"7"
|
|
62
109
|
].join("\n") + "\n"
|
|
63
110
|
|
|
64
111
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -79,7 +126,7 @@ class TestCli < Minitest::Test
|
|
|
79
126
|
"0",
|
|
80
127
|
"3",
|
|
81
128
|
"",
|
|
82
|
-
"
|
|
129
|
+
"7"
|
|
83
130
|
].join("\n") + "\n"
|
|
84
131
|
|
|
85
132
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -98,7 +145,7 @@ class TestCli < Minitest::Test
|
|
|
98
145
|
"2",
|
|
99
146
|
"3",
|
|
100
147
|
"",
|
|
101
|
-
"
|
|
148
|
+
"7"
|
|
102
149
|
].join("\n") + "\n"
|
|
103
150
|
|
|
104
151
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -119,7 +166,7 @@ class TestCli < Minitest::Test
|
|
|
119
166
|
"2",
|
|
120
167
|
"3",
|
|
121
168
|
"",
|
|
122
|
-
"
|
|
169
|
+
"7"
|
|
123
170
|
].join("\n") + "\n"
|
|
124
171
|
|
|
125
172
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -144,7 +191,7 @@ class TestCli < Minitest::Test
|
|
|
144
191
|
"3",
|
|
145
192
|
"2",
|
|
146
193
|
output_path,
|
|
147
|
-
"
|
|
194
|
+
"7"
|
|
148
195
|
].join("\n") + "\n"
|
|
149
196
|
|
|
150
197
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -164,7 +211,7 @@ class TestCli < Minitest::Test
|
|
|
164
211
|
"1",
|
|
165
212
|
"2",
|
|
166
213
|
"",
|
|
167
|
-
"
|
|
214
|
+
"7"
|
|
168
215
|
].join("\n") + "\n"
|
|
169
216
|
|
|
170
217
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -184,7 +231,7 @@ class TestCli < Minitest::Test
|
|
|
184
231
|
"",
|
|
185
232
|
"",
|
|
186
233
|
"",
|
|
187
|
-
"
|
|
234
|
+
"7"
|
|
188
235
|
].join("\n") + "\n"
|
|
189
236
|
|
|
190
237
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -209,7 +256,7 @@ class TestCli < Minitest::Test
|
|
|
209
256
|
"",
|
|
210
257
|
"2",
|
|
211
258
|
output_path,
|
|
212
|
-
"
|
|
259
|
+
"7"
|
|
213
260
|
].join("\n") + "\n"
|
|
214
261
|
|
|
215
262
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -231,7 +278,7 @@ class TestCli < Minitest::Test
|
|
|
231
278
|
"",
|
|
232
279
|
"",
|
|
233
280
|
"",
|
|
234
|
-
"
|
|
281
|
+
"7"
|
|
235
282
|
].join("\n") + "\n"
|
|
236
283
|
|
|
237
284
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -250,7 +297,7 @@ class TestCli < Minitest::Test
|
|
|
250
297
|
"n",
|
|
251
298
|
"",
|
|
252
299
|
"",
|
|
253
|
-
"
|
|
300
|
+
"7"
|
|
254
301
|
].join("\n") + "\n"
|
|
255
302
|
|
|
256
303
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -270,7 +317,7 @@ class TestCli < Minitest::Test
|
|
|
270
317
|
"",
|
|
271
318
|
"",
|
|
272
319
|
"abc",
|
|
273
|
-
"
|
|
320
|
+
"7"
|
|
274
321
|
].join("\n") + "\n"
|
|
275
322
|
|
|
276
323
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -295,7 +342,7 @@ class TestCli < Minitest::Test
|
|
|
295
342
|
"",
|
|
296
343
|
"",
|
|
297
344
|
"",
|
|
298
|
-
"
|
|
345
|
+
"7"
|
|
299
346
|
].join("\n") + "\n"
|
|
300
347
|
|
|
301
348
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -329,7 +376,7 @@ class TestCli < Minitest::Test
|
|
|
329
376
|
"",
|
|
330
377
|
"2",
|
|
331
378
|
output_path,
|
|
332
|
-
"
|
|
379
|
+
"7"
|
|
333
380
|
].join("\n") + "\n"
|
|
334
381
|
|
|
335
382
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -356,7 +403,7 @@ class TestCli < Minitest::Test
|
|
|
356
403
|
"",
|
|
357
404
|
"",
|
|
358
405
|
"",
|
|
359
|
-
"
|
|
406
|
+
"7"
|
|
360
407
|
].join("\n") + "\n"
|
|
361
408
|
|
|
362
409
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -382,7 +429,7 @@ class TestCli < Minitest::Test
|
|
|
382
429
|
"",
|
|
383
430
|
"",
|
|
384
431
|
"",
|
|
385
|
-
"
|
|
432
|
+
"7"
|
|
386
433
|
].join("\n") + "\n"
|
|
387
434
|
|
|
388
435
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -402,7 +449,7 @@ class TestCli < Minitest::Test
|
|
|
402
449
|
fixture_path("sample_people.csv"),
|
|
403
450
|
"",
|
|
404
451
|
"",
|
|
405
|
-
"
|
|
452
|
+
"7"
|
|
406
453
|
].join("\n") + "\n"
|
|
407
454
|
|
|
408
455
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -423,7 +470,7 @@ class TestCli < Minitest::Test
|
|
|
423
470
|
fixture_path("parity_people_reordered.tsv"),
|
|
424
471
|
"2",
|
|
425
472
|
"",
|
|
426
|
-
"
|
|
473
|
+
"7"
|
|
427
474
|
].join("\n") + "\n"
|
|
428
475
|
|
|
429
476
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -441,7 +488,7 @@ class TestCli < Minitest::Test
|
|
|
441
488
|
fixture_path("sample_people_no_headers.csv"),
|
|
442
489
|
"",
|
|
443
490
|
"n",
|
|
444
|
-
"
|
|
491
|
+
"7"
|
|
445
492
|
].join("\n") + "\n"
|
|
446
493
|
|
|
447
494
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -459,7 +506,7 @@ class TestCli < Minitest::Test
|
|
|
459
506
|
fixture_path("parity_people_header_mismatch.csv"),
|
|
460
507
|
"",
|
|
461
508
|
"",
|
|
462
|
-
"
|
|
509
|
+
"7"
|
|
463
510
|
].join("\n") + "\n"
|
|
464
511
|
|
|
465
512
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -477,7 +524,7 @@ class TestCli < Minitest::Test
|
|
|
477
524
|
fixture_path("parity_people_mismatch.csv"),
|
|
478
525
|
"",
|
|
479
526
|
"",
|
|
480
|
-
"
|
|
527
|
+
"7"
|
|
481
528
|
].join("\n") + "\n"
|
|
482
529
|
|
|
483
530
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -499,7 +546,7 @@ class TestCli < Minitest::Test
|
|
|
499
546
|
fixture_path("sample_people.csv"),
|
|
500
547
|
"",
|
|
501
548
|
"",
|
|
502
|
-
"
|
|
549
|
+
"7"
|
|
503
550
|
].join("\n") + "\n"
|
|
504
551
|
|
|
505
552
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -518,7 +565,7 @@ class TestCli < Minitest::Test
|
|
|
518
565
|
"/tmp/not-there-right.csv",
|
|
519
566
|
"",
|
|
520
567
|
"",
|
|
521
|
-
"
|
|
568
|
+
"7"
|
|
522
569
|
].join("\n") + "\n"
|
|
523
570
|
|
|
524
571
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -537,7 +584,7 @@ class TestCli < Minitest::Test
|
|
|
537
584
|
fixture_path("sample_people_bad_tail.csv"),
|
|
538
585
|
"",
|
|
539
586
|
"",
|
|
540
|
-
"
|
|
587
|
+
"7"
|
|
541
588
|
].join("\n") + "\n"
|
|
542
589
|
|
|
543
590
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -564,7 +611,7 @@ class TestCli < Minitest::Test
|
|
|
564
611
|
"y",
|
|
565
612
|
"2",
|
|
566
613
|
output_path,
|
|
567
|
-
"
|
|
614
|
+
"7"
|
|
568
615
|
].join("\n") + "\n"
|
|
569
616
|
|
|
570
617
|
status = Csvtool::CLI.start(["menu"], stdin: StringIO.new(input), stdout: output, stderr: StringIO.new)
|
|
@@ -584,7 +631,7 @@ class TestCli < Minitest::Test
|
|
|
584
631
|
"1",
|
|
585
632
|
"",
|
|
586
633
|
"n",
|
|
587
|
-
"
|
|
634
|
+
"7"
|
|
588
635
|
].join("\n") + "\n"
|
|
589
636
|
|
|
590
637
|
output = StringIO.new
|
|
@@ -599,7 +646,7 @@ class TestCli < Minitest::Test
|
|
|
599
646
|
output = StringIO.new
|
|
600
647
|
status = Csvtool::CLI.start(
|
|
601
648
|
["menu"],
|
|
602
|
-
stdin: StringIO.new("1\n/tmp/does-not-exist.csv\n4\
|
|
649
|
+
stdin: StringIO.new("1\n/tmp/does-not-exist.csv\n4\n7\n"),
|
|
603
650
|
stdout: output,
|
|
604
651
|
stderr: StringIO.new
|
|
605
652
|
)
|
|
@@ -620,7 +667,7 @@ class TestCli < Minitest::Test
|
|
|
620
667
|
"y",
|
|
621
668
|
"2",
|
|
622
669
|
"/tmp/not-a-dir/out.csv",
|
|
623
|
-
"
|
|
670
|
+
"7"
|
|
624
671
|
].join("\n") + "\n"
|
|
625
672
|
|
|
626
673
|
output = StringIO.new
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../test_helper"
|
|
4
|
+
require "csvtool/infrastructure/csv/csv_splitter"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class CsvSplitterTest < Minitest::Test
|
|
8
|
+
def test_splits_large_file_in_order
|
|
9
|
+
splitter = Csvtool::Infrastructure::CSV::CsvSplitter.new
|
|
10
|
+
|
|
11
|
+
Dir.mktmpdir do |dir|
|
|
12
|
+
source_path = File.join(dir, "large.csv")
|
|
13
|
+
File.open(source_path, "w") do |f|
|
|
14
|
+
f.puts "id,value"
|
|
15
|
+
5_000.times { |i| f.puts "#{i + 1},v#{i + 1}" }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
stats = splitter.call(
|
|
19
|
+
file_path: source_path,
|
|
20
|
+
col_sep: ",",
|
|
21
|
+
headers_present: true,
|
|
22
|
+
chunk_size: 1_000,
|
|
23
|
+
output_directory: dir,
|
|
24
|
+
file_prefix: "large",
|
|
25
|
+
overwrite_existing: false
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
assert_equal 5, stats[:chunk_count]
|
|
29
|
+
assert_equal 5_000, stats[:data_rows]
|
|
30
|
+
assert_equal [1_000, 1_000, 1_000, 1_000, 1_000], stats[:chunk_row_counts]
|
|
31
|
+
|
|
32
|
+
first_chunk = File.read(File.join(dir, "large_part_001.csv")).lines.map(&:strip)
|
|
33
|
+
last_chunk = File.read(File.join(dir, "large_part_005.csv")).lines.map(&:strip)
|
|
34
|
+
assert_equal "id,value", first_chunk.first
|
|
35
|
+
assert_equal "1,v1", first_chunk[1]
|
|
36
|
+
assert_equal "1000,v1000", first_chunk[1000]
|
|
37
|
+
assert_equal "4001,v4001", last_chunk[1]
|
|
38
|
+
assert_equal "5000,v5000", last_chunk[1000]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_streaming_split_handles_headerless_file
|
|
43
|
+
splitter = Csvtool::Infrastructure::CSV::CsvSplitter.new
|
|
44
|
+
|
|
45
|
+
Dir.mktmpdir do |dir|
|
|
46
|
+
source_path = File.join(dir, "large_no_headers.csv")
|
|
47
|
+
File.open(source_path, "w") do |f|
|
|
48
|
+
2_500.times { |i| f.puts "#{i + 1},v#{i + 1}" }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
stats = splitter.call(
|
|
52
|
+
file_path: source_path,
|
|
53
|
+
col_sep: ",",
|
|
54
|
+
headers_present: false,
|
|
55
|
+
chunk_size: 1_000,
|
|
56
|
+
output_directory: dir,
|
|
57
|
+
file_prefix: "large_no_headers",
|
|
58
|
+
overwrite_existing: false
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
assert_equal 3, stats[:chunk_count]
|
|
62
|
+
assert_equal 2_500, stats[:data_rows]
|
|
63
|
+
assert_equal [1_000, 1_000, 500], stats[:chunk_row_counts]
|
|
64
|
+
first_line = File.read(File.join(dir, "large_no_headers_part_001.csv")).lines.first.strip
|
|
65
|
+
assert_equal "1,v1", first_line
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../test_helper"
|
|
4
|
+
require "csvtool/infrastructure/output/csv_split_manifest_writer"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class CsvSplitManifestWriterTest < Minitest::Test
|
|
8
|
+
def test_writes_manifest_csv
|
|
9
|
+
writer = Csvtool::Infrastructure::Output::CsvSplitManifestWriter.new
|
|
10
|
+
|
|
11
|
+
Dir.mktmpdir do |dir|
|
|
12
|
+
path = File.join(dir, "manifest.csv")
|
|
13
|
+
writer.call(
|
|
14
|
+
path: path,
|
|
15
|
+
chunk_paths: ["/tmp/a.csv", "/tmp/b.csv"],
|
|
16
|
+
chunk_row_counts: [10, 5]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
lines = File.read(path).lines.map(&:strip)
|
|
20
|
+
assert_equal "chunk_index,chunk_path,row_count", lines[0]
|
|
21
|
+
assert_equal "1,/tmp/a.csv,10", lines[1]
|
|
22
|
+
assert_equal "2,/tmp/b.csv,5", lines[2]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -17,171 +17,122 @@ class MenuLoopTest < Minitest::Test
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def test_routes_extract_column_then_exit
|
|
20
|
-
|
|
21
|
-
rows_action = FakeAction.new
|
|
22
|
-
randomize_rows_action = FakeAction.new
|
|
23
|
-
dedupe_action = FakeAction.new
|
|
24
|
-
parity_action = FakeAction.new
|
|
25
|
-
stdout = StringIO.new
|
|
26
|
-
menu = Csvtool::Interface::CLI::MenuLoop.new(
|
|
27
|
-
stdin: StringIO.new("1\n6\n"),
|
|
28
|
-
stdout: stdout,
|
|
29
|
-
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
|
|
30
|
-
extract_column_action: column_action,
|
|
31
|
-
extract_rows_action: rows_action,
|
|
32
|
-
randomize_rows_action: randomize_rows_action,
|
|
33
|
-
dedupe_action: dedupe_action,
|
|
34
|
-
parity_action: parity_action
|
|
35
|
-
)
|
|
36
|
-
|
|
20
|
+
menu, actions, = build_menu("1\n7\n")
|
|
37
21
|
status = menu.run
|
|
38
22
|
|
|
39
23
|
assert_equal 0, status
|
|
40
|
-
assert_equal 1,
|
|
41
|
-
assert_equal 0,
|
|
42
|
-
assert_equal 0,
|
|
43
|
-
assert_equal 0,
|
|
44
|
-
assert_equal 0,
|
|
45
|
-
|
|
24
|
+
assert_equal 1, actions[:column].runs
|
|
25
|
+
assert_equal 0, actions[:rows].runs
|
|
26
|
+
assert_equal 0, actions[:randomize].runs
|
|
27
|
+
assert_equal 0, actions[:dedupe].runs
|
|
28
|
+
assert_equal 0, actions[:parity].runs
|
|
29
|
+
assert_equal 0, actions[:split].runs
|
|
46
30
|
end
|
|
47
31
|
|
|
48
32
|
def test_routes_extract_rows_then_exit
|
|
49
|
-
|
|
50
|
-
rows_action = FakeAction.new
|
|
51
|
-
randomize_rows_action = FakeAction.new
|
|
52
|
-
dedupe_action = FakeAction.new
|
|
53
|
-
parity_action = FakeAction.new
|
|
54
|
-
stdout = StringIO.new
|
|
55
|
-
menu = Csvtool::Interface::CLI::MenuLoop.new(
|
|
56
|
-
stdin: StringIO.new("2\n6\n"),
|
|
57
|
-
stdout: stdout,
|
|
58
|
-
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
|
|
59
|
-
extract_column_action: column_action,
|
|
60
|
-
extract_rows_action: rows_action,
|
|
61
|
-
randomize_rows_action: randomize_rows_action,
|
|
62
|
-
dedupe_action: dedupe_action,
|
|
63
|
-
parity_action: parity_action
|
|
64
|
-
)
|
|
65
|
-
|
|
33
|
+
menu, actions, = build_menu("2\n7\n")
|
|
66
34
|
status = menu.run
|
|
67
35
|
|
|
68
36
|
assert_equal 0, status
|
|
69
|
-
assert_equal 0,
|
|
70
|
-
assert_equal 1,
|
|
71
|
-
assert_equal 0,
|
|
72
|
-
assert_equal 0,
|
|
73
|
-
assert_equal 0,
|
|
37
|
+
assert_equal 0, actions[:column].runs
|
|
38
|
+
assert_equal 1, actions[:rows].runs
|
|
39
|
+
assert_equal 0, actions[:randomize].runs
|
|
40
|
+
assert_equal 0, actions[:dedupe].runs
|
|
41
|
+
assert_equal 0, actions[:parity].runs
|
|
42
|
+
assert_equal 0, actions[:split].runs
|
|
74
43
|
end
|
|
75
44
|
|
|
76
45
|
def test_routes_randomize_rows_then_exit
|
|
77
|
-
|
|
78
|
-
rows_action = FakeAction.new
|
|
79
|
-
randomize_rows_action = FakeAction.new
|
|
80
|
-
dedupe_action = FakeAction.new
|
|
81
|
-
parity_action = FakeAction.new
|
|
82
|
-
stdout = StringIO.new
|
|
83
|
-
menu = Csvtool::Interface::CLI::MenuLoop.new(
|
|
84
|
-
stdin: StringIO.new("3\n6\n"),
|
|
85
|
-
stdout: stdout,
|
|
86
|
-
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
|
|
87
|
-
extract_column_action: column_action,
|
|
88
|
-
extract_rows_action: rows_action,
|
|
89
|
-
randomize_rows_action: randomize_rows_action,
|
|
90
|
-
dedupe_action: dedupe_action,
|
|
91
|
-
parity_action: parity_action
|
|
92
|
-
)
|
|
93
|
-
|
|
46
|
+
menu, actions, = build_menu("3\n7\n")
|
|
94
47
|
status = menu.run
|
|
95
48
|
|
|
96
49
|
assert_equal 0, status
|
|
97
|
-
assert_equal 0,
|
|
98
|
-
assert_equal 0,
|
|
99
|
-
assert_equal 1,
|
|
100
|
-
assert_equal 0,
|
|
101
|
-
assert_equal 0,
|
|
50
|
+
assert_equal 0, actions[:column].runs
|
|
51
|
+
assert_equal 0, actions[:rows].runs
|
|
52
|
+
assert_equal 1, actions[:randomize].runs
|
|
53
|
+
assert_equal 0, actions[:dedupe].runs
|
|
54
|
+
assert_equal 0, actions[:parity].runs
|
|
55
|
+
assert_equal 0, actions[:split].runs
|
|
102
56
|
end
|
|
103
57
|
|
|
104
58
|
def test_routes_dedupe_then_exit
|
|
105
|
-
|
|
106
|
-
rows_action = FakeAction.new
|
|
107
|
-
randomize_rows_action = FakeAction.new
|
|
108
|
-
dedupe_action = FakeAction.new
|
|
109
|
-
parity_action = FakeAction.new
|
|
110
|
-
stdout = StringIO.new
|
|
111
|
-
menu = Csvtool::Interface::CLI::MenuLoop.new(
|
|
112
|
-
stdin: StringIO.new("4\n6\n"),
|
|
113
|
-
stdout: stdout,
|
|
114
|
-
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
|
|
115
|
-
extract_column_action: column_action,
|
|
116
|
-
extract_rows_action: rows_action,
|
|
117
|
-
randomize_rows_action: randomize_rows_action,
|
|
118
|
-
dedupe_action: dedupe_action,
|
|
119
|
-
parity_action: parity_action
|
|
120
|
-
)
|
|
121
|
-
|
|
59
|
+
menu, actions, = build_menu("4\n7\n")
|
|
122
60
|
status = menu.run
|
|
123
61
|
|
|
124
62
|
assert_equal 0, status
|
|
125
|
-
assert_equal 0,
|
|
126
|
-
assert_equal 0,
|
|
127
|
-
assert_equal 0,
|
|
128
|
-
assert_equal 1,
|
|
129
|
-
assert_equal 0,
|
|
63
|
+
assert_equal 0, actions[:column].runs
|
|
64
|
+
assert_equal 0, actions[:rows].runs
|
|
65
|
+
assert_equal 0, actions[:randomize].runs
|
|
66
|
+
assert_equal 1, actions[:dedupe].runs
|
|
67
|
+
assert_equal 0, actions[:parity].runs
|
|
68
|
+
assert_equal 0, actions[:split].runs
|
|
130
69
|
end
|
|
131
70
|
|
|
132
71
|
def test_routes_parity_then_exit
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
extract_rows_action: rows_action,
|
|
145
|
-
randomize_rows_action: randomize_rows_action,
|
|
146
|
-
dedupe_action: dedupe_action,
|
|
147
|
-
parity_action: parity_action
|
|
148
|
-
)
|
|
72
|
+
menu, actions, = build_menu("5\n7\n")
|
|
73
|
+
status = menu.run
|
|
74
|
+
|
|
75
|
+
assert_equal 0, status
|
|
76
|
+
assert_equal 0, actions[:column].runs
|
|
77
|
+
assert_equal 0, actions[:rows].runs
|
|
78
|
+
assert_equal 0, actions[:randomize].runs
|
|
79
|
+
assert_equal 0, actions[:dedupe].runs
|
|
80
|
+
assert_equal 1, actions[:parity].runs
|
|
81
|
+
assert_equal 0, actions[:split].runs
|
|
82
|
+
end
|
|
149
83
|
|
|
84
|
+
def test_routes_split_then_exit
|
|
85
|
+
menu, actions, stdout = build_menu("6\n7\n")
|
|
150
86
|
status = menu.run
|
|
151
87
|
|
|
152
88
|
assert_equal 0, status
|
|
153
|
-
assert_equal 0,
|
|
154
|
-
assert_equal 0,
|
|
155
|
-
assert_equal 0,
|
|
156
|
-
assert_equal 0,
|
|
157
|
-
assert_equal
|
|
89
|
+
assert_equal 0, actions[:column].runs
|
|
90
|
+
assert_equal 0, actions[:rows].runs
|
|
91
|
+
assert_equal 0, actions[:randomize].runs
|
|
92
|
+
assert_equal 0, actions[:dedupe].runs
|
|
93
|
+
assert_equal 0, actions[:parity].runs
|
|
94
|
+
assert_equal 1, actions[:split].runs
|
|
95
|
+
assert_includes stdout.string, "CSV Tool Menu"
|
|
158
96
|
end
|
|
159
97
|
|
|
160
98
|
def test_invalid_choice_shows_prompt
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
99
|
+
menu, actions, stdout = build_menu("x\n7\n")
|
|
100
|
+
menu.run
|
|
101
|
+
|
|
102
|
+
assert_includes stdout.string, "Please choose 1, 2, 3, 4, 5, 6, or 7."
|
|
103
|
+
assert_equal 0, actions[:column].runs
|
|
104
|
+
assert_equal 0, actions[:rows].runs
|
|
105
|
+
assert_equal 0, actions[:randomize].runs
|
|
106
|
+
assert_equal 0, actions[:dedupe].runs
|
|
107
|
+
assert_equal 0, actions[:parity].runs
|
|
108
|
+
assert_equal 0, actions[:split].runs
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def build_menu(input)
|
|
114
|
+
actions = {
|
|
115
|
+
column: FakeAction.new,
|
|
116
|
+
rows: FakeAction.new,
|
|
117
|
+
randomize: FakeAction.new,
|
|
118
|
+
dedupe: FakeAction.new,
|
|
119
|
+
parity: FakeAction.new,
|
|
120
|
+
split: FakeAction.new
|
|
121
|
+
}
|
|
166
122
|
stdout = StringIO.new
|
|
123
|
+
|
|
167
124
|
menu = Csvtool::Interface::CLI::MenuLoop.new(
|
|
168
|
-
stdin: StringIO.new(
|
|
125
|
+
stdin: StringIO.new(input),
|
|
169
126
|
stdout: stdout,
|
|
170
|
-
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
|
|
171
|
-
extract_column_action:
|
|
172
|
-
extract_rows_action:
|
|
173
|
-
randomize_rows_action:
|
|
174
|
-
dedupe_action:
|
|
175
|
-
parity_action:
|
|
127
|
+
menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Split CSV into chunks", "Exit"],
|
|
128
|
+
extract_column_action: actions[:column],
|
|
129
|
+
extract_rows_action: actions[:rows],
|
|
130
|
+
randomize_rows_action: actions[:randomize],
|
|
131
|
+
dedupe_action: actions[:dedupe],
|
|
132
|
+
parity_action: actions[:parity],
|
|
133
|
+
split_action: actions[:split]
|
|
176
134
|
)
|
|
177
135
|
|
|
178
|
-
menu
|
|
179
|
-
|
|
180
|
-
assert_includes stdout.string, "Please choose 1, 2, 3, 4, 5, or 6."
|
|
181
|
-
assert_equal 0, column_action.runs
|
|
182
|
-
assert_equal 0, rows_action.runs
|
|
183
|
-
assert_equal 0, randomize_rows_action.runs
|
|
184
|
-
assert_equal 0, dedupe_action.runs
|
|
185
|
-
assert_equal 0, parity_action.runs
|
|
136
|
+
[menu, actions, stdout]
|
|
186
137
|
end
|
|
187
138
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/prompts/chunk_size_prompt"
|
|
5
|
+
|
|
6
|
+
class ChunkSizePromptTest < Minitest::Test
|
|
7
|
+
def test_returns_entered_value
|
|
8
|
+
out = StringIO.new
|
|
9
|
+
prompt = Csvtool::Interface::CLI::Prompts::ChunkSizePrompt.new(
|
|
10
|
+
stdin: StringIO.new("25\n"),
|
|
11
|
+
stdout: out
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
assert_equal "25", prompt.call
|
|
15
|
+
assert_includes out.string, "Rows per chunk: "
|
|
16
|
+
end
|
|
17
|
+
end
|