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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -12
  3. data/docs/architecture.md +61 -4
  4. data/docs/release-v0.7.0-alpha.md +87 -0
  5. data/lib/csvtool/application/use_cases/run_csv_split.rb +97 -0
  6. data/lib/csvtool/cli.rb +5 -1
  7. data/lib/csvtool/domain/csv_split_session/split_options.rb +27 -0
  8. data/lib/csvtool/domain/csv_split_session/split_session.rb +20 -0
  9. data/lib/csvtool/domain/csv_split_session/split_source.rb +17 -0
  10. data/lib/csvtool/infrastructure/csv/csv_splitter.rb +64 -0
  11. data/lib/csvtool/infrastructure/output/csv_split_manifest_writer.rb +20 -0
  12. data/lib/csvtool/interface/cli/errors/presenter.rb +8 -0
  13. data/lib/csvtool/interface/cli/menu_loop.rb +5 -2
  14. data/lib/csvtool/interface/cli/prompts/chunk_size_prompt.rb +21 -0
  15. data/lib/csvtool/interface/cli/prompts/split_manifest_prompt.rb +30 -0
  16. data/lib/csvtool/interface/cli/prompts/split_output_prompt.rb +38 -0
  17. data/lib/csvtool/interface/cli/workflows/builders/csv_split_session_builder.rb +44 -0
  18. data/lib/csvtool/interface/cli/workflows/presenters/csv_split_presenter.rb +26 -0
  19. data/lib/csvtool/interface/cli/workflows/run_csv_split_workflow.rb +89 -0
  20. data/lib/csvtool/interface/cli/workflows/steps/csv_split/build_session_step.rb +30 -0
  21. data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_inputs_step.rb +43 -0
  22. data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step.rb +30 -0
  23. data/lib/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step.rb +31 -0
  24. data/lib/csvtool/interface/cli/workflows/steps/csv_split/execute_step.rb +36 -0
  25. data/lib/csvtool/version.rb +1 -1
  26. data/test/csvtool/application/use_cases/run_csv_split_test.rb +124 -0
  27. data/test/csvtool/cli_test.rb +76 -29
  28. data/test/csvtool/infrastructure/csv/csv_splitter_test.rb +68 -0
  29. data/test/csvtool/infrastructure/output/csv_split_manifest_writer_test.rb +25 -0
  30. data/test/csvtool/interface/cli/menu_loop_test.rb +81 -130
  31. data/test/csvtool/interface/cli/prompts/chunk_size_prompt_test.rb +17 -0
  32. data/test/csvtool/interface/cli/prompts/split_manifest_prompt_test.rb +42 -0
  33. data/test/csvtool/interface/cli/prompts/split_output_prompt_test.rb +22 -0
  34. data/test/csvtool/interface/cli/workflows/builders/csv_split_session_builder_test.rb +30 -0
  35. data/test/csvtool/interface/cli/workflows/presenters/csv_split_presenter_test.rb +26 -0
  36. data/test/csvtool/interface/cli/workflows/run_csv_split_workflow_test.rb +200 -0
  37. data/test/csvtool/interface/cli/workflows/steps/csv_split/build_session_step_test.rb +40 -0
  38. data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_inputs_step_test.rb +64 -0
  39. data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step_test.rb +30 -0
  40. data/test/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step_test.rb +32 -0
  41. data/test/csvtool/interface/cli/workflows/steps/csv_split/execute_step_test.rb +83 -0
  42. data/test/fixtures/split_people_25.csv +26 -0
  43. metadata +34 -1
@@ -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("6\n"), stdout: output, stderr: 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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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
- "6"
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\n6\n"),
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
- "6"
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
- column_action = FakeAction.new
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, column_action.runs
41
- assert_equal 0, rows_action.runs
42
- assert_equal 0, randomize_rows_action.runs
43
- assert_equal 0, dedupe_action.runs
44
- assert_equal 0, parity_action.runs
45
- assert_includes stdout.string, "CSV Tool Menu"
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
- column_action = FakeAction.new
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, column_action.runs
70
- assert_equal 1, rows_action.runs
71
- assert_equal 0, randomize_rows_action.runs
72
- assert_equal 0, dedupe_action.runs
73
- assert_equal 0, parity_action.runs
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
- column_action = FakeAction.new
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, column_action.runs
98
- assert_equal 0, rows_action.runs
99
- assert_equal 1, randomize_rows_action.runs
100
- assert_equal 0, dedupe_action.runs
101
- assert_equal 0, parity_action.runs
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
- column_action = FakeAction.new
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, column_action.runs
126
- assert_equal 0, rows_action.runs
127
- assert_equal 0, randomize_rows_action.runs
128
- assert_equal 1, dedupe_action.runs
129
- assert_equal 0, parity_action.runs
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
- column_action = FakeAction.new
134
- rows_action = FakeAction.new
135
- randomize_rows_action = FakeAction.new
136
- dedupe_action = FakeAction.new
137
- parity_action = FakeAction.new
138
- stdout = StringIO.new
139
- menu = Csvtool::Interface::CLI::MenuLoop.new(
140
- stdin: StringIO.new("5\n6\n"),
141
- stdout: stdout,
142
- menu_options: ["Extract column", "Extract rows (range)", "Randomize rows", "Dedupe using another CSV", "Validate parity", "Exit"],
143
- extract_column_action: column_action,
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, column_action.runs
154
- assert_equal 0, rows_action.runs
155
- assert_equal 0, randomize_rows_action.runs
156
- assert_equal 0, dedupe_action.runs
157
- assert_equal 1, parity_action.runs
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
- column_action = FakeAction.new
162
- rows_action = FakeAction.new
163
- randomize_rows_action = FakeAction.new
164
- dedupe_action = FakeAction.new
165
- parity_action = FakeAction.new
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("x\n6\n"),
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: column_action,
172
- extract_rows_action: rows_action,
173
- randomize_rows_action: randomize_rows_action,
174
- dedupe_action: dedupe_action,
175
- parity_action: 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.run
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