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
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Prompts
7
+ class SplitManifestPrompt
8
+ def initialize(stdin:, stdout:, yes_no_prompt:)
9
+ @stdin = stdin
10
+ @stdout = stdout
11
+ @yes_no_prompt = yes_no_prompt
12
+ end
13
+
14
+ def call(default_path:)
15
+ write_manifest = @yes_no_prompt.call(
16
+ label: "Write manifest file? [y/N]: ",
17
+ default: false
18
+ )
19
+ return { write_manifest: false, manifest_path: nil } unless write_manifest
20
+
21
+ @stdout.print "Manifest file path [#{default_path}]: "
22
+ path = @stdin.gets&.strip.to_s
23
+ path = default_path if path.empty?
24
+ { write_manifest: true, manifest_path: path }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Prompts
7
+ class SplitOutputPrompt
8
+ def initialize(stdin:, stdout:, yes_no_prompt:)
9
+ @stdin = stdin
10
+ @stdout = stdout
11
+ @yes_no_prompt = yes_no_prompt
12
+ end
13
+
14
+ def call(default_directory:, default_prefix:)
15
+ @stdout.print "Output directory [#{default_directory}]: "
16
+ output_directory = @stdin.gets&.strip.to_s
17
+ output_directory = default_directory if output_directory.empty?
18
+
19
+ @stdout.print "Output file prefix [#{default_prefix}]: "
20
+ file_prefix = @stdin.gets&.strip.to_s
21
+ file_prefix = default_prefix if file_prefix.empty?
22
+
23
+ overwrite_existing = @yes_no_prompt.call(
24
+ label: "Overwrite existing chunk files? [y/N]: ",
25
+ default: false
26
+ )
27
+
28
+ {
29
+ output_directory: output_directory,
30
+ file_prefix: file_prefix,
31
+ overwrite_existing: overwrite_existing
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csvtool/domain/csv_split_session/split_source"
4
+ require "csvtool/domain/csv_split_session/split_options"
5
+ require "csvtool/domain/csv_split_session/split_session"
6
+
7
+ module Csvtool
8
+ module Interface
9
+ module CLI
10
+ module Workflows
11
+ module Builders
12
+ class CsvSplitSessionBuilder
13
+ def call(
14
+ file_path:,
15
+ col_sep:,
16
+ headers_present:,
17
+ chunk_size:,
18
+ output_directory: nil,
19
+ file_prefix: nil,
20
+ overwrite_existing: false,
21
+ write_manifest: false,
22
+ manifest_path: nil
23
+ )
24
+ source = Domain::CsvSplitSession::SplitSource.new(
25
+ path: file_path,
26
+ separator: col_sep,
27
+ headers_present: headers_present
28
+ )
29
+ options = Domain::CsvSplitSession::SplitOptions.new(
30
+ chunk_size: chunk_size,
31
+ output_directory: output_directory,
32
+ file_prefix: file_prefix,
33
+ overwrite_existing: overwrite_existing,
34
+ write_manifest: write_manifest,
35
+ manifest_path: manifest_path
36
+ )
37
+ Domain::CsvSplitSession::SplitSession.start(source: source, options: options)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Presenters
8
+ class CsvSplitPresenter
9
+ def initialize(stdout:)
10
+ @stdout = stdout
11
+ end
12
+
13
+ def print_summary(data)
14
+ @stdout.puts "Split complete."
15
+ @stdout.puts "Chunk size: #{data[:chunk_size]}"
16
+ @stdout.puts "Data rows: #{data[:data_rows]}"
17
+ @stdout.puts "Chunks written: #{data[:chunk_count]}"
18
+ @stdout.puts "Manifest: #{data[:manifest_path]}" if data[:manifest_path]
19
+ data[:chunk_paths].each { |path| @stdout.puts path }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csvtool/application/use_cases/run_csv_split"
4
+ require "csvtool/interface/cli/errors/presenter"
5
+ require "csvtool/interface/cli/prompts/file_path_prompt"
6
+ require "csvtool/interface/cli/prompts/separator_prompt"
7
+ require "csvtool/interface/cli/prompts/headers_present_prompt"
8
+ require "csvtool/interface/cli/prompts/chunk_size_prompt"
9
+ require "csvtool/interface/cli/prompts/yes_no_prompt"
10
+ require "csvtool/interface/cli/prompts/split_output_prompt"
11
+ require "csvtool/interface/cli/prompts/split_manifest_prompt"
12
+ require "csvtool/interface/cli/workflows/builders/csv_split_session_builder"
13
+ require "csvtool/interface/cli/workflows/presenters/csv_split_presenter"
14
+ require "csvtool/interface/cli/workflows/support/result_error_handler"
15
+ require "csvtool/interface/cli/workflows/steps/workflow_step_pipeline"
16
+ require "csvtool/interface/cli/workflows/steps/csv_split/collect_inputs_step"
17
+ require "csvtool/interface/cli/workflows/steps/csv_split/collect_output_step"
18
+ require "csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step"
19
+ require "csvtool/interface/cli/workflows/steps/csv_split/build_session_step"
20
+ require "csvtool/interface/cli/workflows/steps/csv_split/execute_step"
21
+
22
+ module Csvtool
23
+ module Interface
24
+ module CLI
25
+ module Workflows
26
+ class RunCsvSplitWorkflow
27
+ def initialize(stdin:, stdout:, use_case: Application::UseCases::RunCsvSplit.new)
28
+ @stdin = stdin
29
+ @stdout = stdout
30
+ @use_case = use_case
31
+ @errors = Interface::CLI::Errors::Presenter.new(stdout: stdout)
32
+ @session_builder = Builders::CsvSplitSessionBuilder.new
33
+ @presenter = Presenters::CsvSplitPresenter.new(stdout: stdout)
34
+ @result_error_handler = Support::ResultErrorHandler.new(errors: @errors)
35
+ end
36
+
37
+ def call
38
+ context = {
39
+ use_case: @use_case,
40
+ session_builder: @session_builder,
41
+ presenter: @presenter,
42
+ handle_error: method(:handle_error)
43
+ }
44
+ pipeline = Steps::WorkflowStepPipeline.new(steps: [
45
+ Steps::CsvSplit::CollectInputsStep.new(
46
+ file_path_prompt: Interface::CLI::Prompts::FilePathPrompt.new(stdin: @stdin, stdout: @stdout),
47
+ separator_prompt: Interface::CLI::Prompts::SeparatorPrompt.new(stdin: @stdin, stdout: @stdout, errors: @errors),
48
+ headers_present_prompt: Interface::CLI::Prompts::HeadersPresentPrompt.new(stdin: @stdin, stdout: @stdout),
49
+ chunk_size_prompt: Interface::CLI::Prompts::ChunkSizePrompt.new(stdin: @stdin, stdout: @stdout),
50
+ errors: @errors
51
+ ),
52
+ Steps::CsvSplit::CollectOutputStep.new(
53
+ split_output_prompt: Interface::CLI::Prompts::SplitOutputPrompt.new(
54
+ stdin: @stdin,
55
+ stdout: @stdout,
56
+ yes_no_prompt: Interface::CLI::Prompts::YesNoPrompt.new(stdin: @stdin, stdout: @stdout)
57
+ )
58
+ ),
59
+ Steps::CsvSplit::CollectManifestStep.new(
60
+ split_manifest_prompt: Interface::CLI::Prompts::SplitManifestPrompt.new(
61
+ stdin: @stdin,
62
+ stdout: @stdout,
63
+ yes_no_prompt: Interface::CLI::Prompts::YesNoPrompt.new(stdin: @stdin, stdout: @stdout)
64
+ )
65
+ ),
66
+ Steps::CsvSplit::BuildSessionStep.new,
67
+ Steps::CsvSplit::ExecuteStep.new
68
+ ])
69
+ pipeline.call(context)
70
+ nil
71
+ end
72
+
73
+ private
74
+
75
+ def handle_error(result)
76
+ @result_error_handler.call(result, {
77
+ file_not_found: ->(r, errors) { errors.file_not_found(r.data[:path]) },
78
+ no_headers: ->(_r, errors) { errors.no_headers },
79
+ could_not_parse_csv: ->(_r, errors) { errors.could_not_parse_csv },
80
+ cannot_read_file: ->(r, errors) { errors.cannot_read_file(r.data[:path]) },
81
+ cannot_write_output_file: ->(r, errors) { errors.cannot_write_output_file(r.data[:path], r.data[:error_class]) },
82
+ output_file_exists: ->(r, errors) { errors.output_file_exists(r.data[:path]) }
83
+ })
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Steps
8
+ module CsvSplit
9
+ class BuildSessionStep
10
+ def call(context)
11
+ context[:session] = context.fetch(:session_builder).call(
12
+ file_path: context.fetch(:file_path),
13
+ col_sep: context.fetch(:col_sep),
14
+ headers_present: context.fetch(:headers_present),
15
+ chunk_size: context.fetch(:chunk_size),
16
+ output_directory: context[:output_directory],
17
+ file_prefix: context[:file_prefix],
18
+ overwrite_existing: context.fetch(:overwrite_existing, false),
19
+ write_manifest: context.fetch(:write_manifest, false),
20
+ manifest_path: context[:manifest_path]
21
+ )
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Steps
8
+ module CsvSplit
9
+ class CollectInputsStep
10
+ def initialize(file_path_prompt:, separator_prompt:, headers_present_prompt:, chunk_size_prompt:, errors:)
11
+ @file_path_prompt = file_path_prompt
12
+ @separator_prompt = separator_prompt
13
+ @headers_present_prompt = headers_present_prompt
14
+ @chunk_size_prompt = chunk_size_prompt
15
+ @errors = errors
16
+ end
17
+
18
+ def call(context)
19
+ context[:file_path] = @file_path_prompt.call(label: "Source CSV file path: ")
20
+ col_sep = @separator_prompt.call
21
+ return :halt if col_sep.nil?
22
+
23
+ context[:col_sep] = col_sep
24
+ context[:headers_present] = @headers_present_prompt.call
25
+ chunk_size = Integer(@chunk_size_prompt.call)
26
+ if chunk_size <= 0
27
+ @errors.invalid_chunk_size
28
+ return :halt
29
+ end
30
+
31
+ context[:chunk_size] = chunk_size
32
+ nil
33
+ rescue ArgumentError, TypeError
34
+ @errors.invalid_chunk_size
35
+ :halt
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Steps
8
+ module CsvSplit
9
+ class CollectManifestStep
10
+ def initialize(split_manifest_prompt:)
11
+ @split_manifest_prompt = split_manifest_prompt
12
+ end
13
+
14
+ def call(context)
15
+ default_path = File.join(
16
+ context.fetch(:output_directory),
17
+ "#{context.fetch(:file_prefix)}_manifest.csv"
18
+ )
19
+ manifest = @split_manifest_prompt.call(default_path: default_path)
20
+ context[:write_manifest] = manifest[:write_manifest]
21
+ context[:manifest_path] = manifest[:manifest_path]
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Steps
8
+ module CsvSplit
9
+ class CollectOutputStep
10
+ def initialize(split_output_prompt:)
11
+ @split_output_prompt = split_output_prompt
12
+ end
13
+
14
+ def call(context)
15
+ file_path = context.fetch(:file_path)
16
+ output = @split_output_prompt.call(
17
+ default_directory: File.dirname(file_path),
18
+ default_prefix: File.basename(file_path, ".*")
19
+ )
20
+ context[:output_directory] = output[:output_directory]
21
+ context[:file_prefix] = output[:file_prefix]
22
+ context[:overwrite_existing] = output[:overwrite_existing]
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Csvtool
4
+ module Interface
5
+ module CLI
6
+ module Workflows
7
+ module Steps
8
+ module CsvSplit
9
+ class ExecuteStep
10
+ def call(context)
11
+ headers_result = context.fetch(:use_case).read_headers(
12
+ file_path: context.fetch(:file_path),
13
+ col_sep: context.fetch(:col_sep),
14
+ headers_present: context.fetch(:headers_present)
15
+ )
16
+ unless headers_result.ok?
17
+ context.fetch(:handle_error).call(headers_result)
18
+ return :halt
19
+ end
20
+
21
+ result = context.fetch(:use_case).call(session: context.fetch(:session))
22
+ unless result.ok?
23
+ context.fetch(:handle_error).call(result)
24
+ return :halt
25
+ end
26
+
27
+ context.fetch(:presenter).print_summary(result.data.merge(chunk_size: context.fetch(:chunk_size)))
28
+ nil
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Csvtool
4
- VERSION = "0.6.0.alpha"
4
+ VERSION = "0.7.0.alpha"
5
5
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../test_helper"
4
+ require "csvtool/application/use_cases/run_csv_split"
5
+ require "csvtool/domain/csv_split_session/split_source"
6
+ require "csvtool/domain/csv_split_session/split_options"
7
+ require "csvtool/domain/csv_split_session/split_session"
8
+ require "tmpdir"
9
+ require "fileutils"
10
+
11
+ class RunCsvSplitTest < Minitest::Test
12
+ def fixture_path(name)
13
+ File.expand_path("../../../fixtures/#{name}", __dir__)
14
+ end
15
+
16
+ def test_splits_25_rows_into_10_10_5_with_headers
17
+ use_case = Csvtool::Application::UseCases::RunCsvSplit.new
18
+
19
+ Dir.mktmpdir do |dir|
20
+ source_path = File.join(dir, "people.csv")
21
+ FileUtils.cp(fixture_path("split_people_25.csv"), source_path)
22
+
23
+ source = Csvtool::Domain::CsvSplitSession::SplitSource.new(path: source_path, separator: ",", headers_present: true)
24
+ options = Csvtool::Domain::CsvSplitSession::SplitOptions.new(chunk_size: 10)
25
+ session = Csvtool::Domain::CsvSplitSession::SplitSession.start(source: source, options: options)
26
+
27
+ result = use_case.call(session: session)
28
+
29
+ assert result.ok?
30
+ assert_equal 3, result.data[:chunk_count]
31
+ assert_equal 25, result.data[:data_rows]
32
+ assert_equal 3, result.data[:chunk_paths].length
33
+
34
+ chunk_1 = File.read(result.data[:chunk_paths][0]).lines.map(&:strip)
35
+ chunk_2 = File.read(result.data[:chunk_paths][1]).lines.map(&:strip)
36
+ chunk_3 = File.read(result.data[:chunk_paths][2]).lines.map(&:strip)
37
+
38
+ assert_equal 11, chunk_1.length
39
+ assert_equal 11, chunk_2.length
40
+ assert_equal 6, chunk_3.length
41
+ assert_equal "name,city", chunk_1.first
42
+ assert_equal "name,city", chunk_2.first
43
+ assert_equal "name,city", chunk_3.first
44
+ assert_equal "Name01,City01", chunk_1[1]
45
+ assert_equal "Name10,City10", chunk_1[10]
46
+ assert_equal "Name11,City11", chunk_2[1]
47
+ assert_equal "Name20,City20", chunk_2[10]
48
+ assert_equal "Name21,City21", chunk_3[1]
49
+ assert_equal "Name25,City25", chunk_3[5]
50
+ end
51
+ end
52
+
53
+ def test_returns_output_file_exists_when_overwrite_is_disabled
54
+ use_case = Csvtool::Application::UseCases::RunCsvSplit.new
55
+
56
+ Dir.mktmpdir do |dir|
57
+ source_path = File.join(dir, "people.csv")
58
+ FileUtils.cp(fixture_path("split_people_25.csv"), source_path)
59
+ File.write(File.join(dir, "people_part_001.csv"), "sentinel\n")
60
+
61
+ source = Csvtool::Domain::CsvSplitSession::SplitSource.new(path: source_path, separator: ",", headers_present: true)
62
+ options = Csvtool::Domain::CsvSplitSession::SplitOptions.new(chunk_size: 10, overwrite_existing: false)
63
+ session = Csvtool::Domain::CsvSplitSession::SplitSession.start(source: source, options: options)
64
+
65
+ result = use_case.call(session: session)
66
+
67
+ refute result.ok?
68
+ assert_equal :output_file_exists, result.error
69
+ assert_equal File.join(dir, "people_part_001.csv"), result.data[:path]
70
+ end
71
+ end
72
+
73
+ def test_creates_output_directory_when_it_does_not_exist
74
+ use_case = Csvtool::Application::UseCases::RunCsvSplit.new
75
+
76
+ Dir.mktmpdir do |dir|
77
+ source_path = File.join(dir, "people.csv")
78
+ output_dir = File.join(dir, "new_chunks")
79
+ FileUtils.cp(fixture_path("split_people_25.csv"), source_path)
80
+
81
+ source = Csvtool::Domain::CsvSplitSession::SplitSource.new(path: source_path, separator: ",", headers_present: true)
82
+ options = Csvtool::Domain::CsvSplitSession::SplitOptions.new(
83
+ chunk_size: 10,
84
+ output_directory: output_dir,
85
+ file_prefix: "batch"
86
+ )
87
+ session = Csvtool::Domain::CsvSplitSession::SplitSession.start(source: source, options: options)
88
+
89
+ result = use_case.call(session: session)
90
+
91
+ assert result.ok?
92
+ assert Dir.exist?(output_dir)
93
+ assert File.file?(File.join(output_dir, "batch_part_001.csv"))
94
+ end
95
+ end
96
+
97
+ def test_writes_manifest_when_enabled
98
+ use_case = Csvtool::Application::UseCases::RunCsvSplit.new
99
+
100
+ Dir.mktmpdir do |dir|
101
+ source_path = File.join(dir, "people.csv")
102
+ manifest_path = File.join(dir, "manifest.csv")
103
+ FileUtils.cp(fixture_path("split_people_25.csv"), source_path)
104
+
105
+ source = Csvtool::Domain::CsvSplitSession::SplitSource.new(path: source_path, separator: ",", headers_present: true)
106
+ options = Csvtool::Domain::CsvSplitSession::SplitOptions.new(
107
+ chunk_size: 10,
108
+ write_manifest: true,
109
+ manifest_path: manifest_path
110
+ )
111
+ session = Csvtool::Domain::CsvSplitSession::SplitSession.start(source: source, options: options)
112
+
113
+ result = use_case.call(session: session)
114
+
115
+ assert result.ok?
116
+ assert_equal manifest_path, result.data[:manifest_path]
117
+ lines = File.read(manifest_path).lines.map(&:strip)
118
+ assert_equal "chunk_index,chunk_path,row_count", lines.first
119
+ assert_includes lines[1], ",10"
120
+ assert_includes lines[2], ",10"
121
+ assert_includes lines[3], ",5"
122
+ end
123
+ end
124
+ end