csvops 0.7.0.alpha → 0.9.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 +80 -20
- data/docs/architecture.md +67 -4
- data/docs/cli-output-conventions.md +49 -0
- data/docs/release-v0.8.0-alpha.md +88 -0
- data/docs/release-v0.9.0-alpha.md +80 -0
- data/lib/csvtool/application/use_cases/run_csv_stats.rb +64 -0
- data/lib/csvtool/cli.rb +136 -12
- data/lib/csvtool/domain/csv_stats_session/stats_options.rb +11 -0
- data/lib/csvtool/domain/csv_stats_session/stats_session.rb +25 -0
- data/lib/csvtool/domain/csv_stats_session/stats_source.rb +17 -0
- data/lib/csvtool/infrastructure/csv/csv_stats_scanner.rb +67 -0
- data/lib/csvtool/infrastructure/output/csv_stats_file_writer.rb +26 -0
- data/lib/csvtool/interface/cli/menu_loop.rb +9 -5
- data/lib/csvtool/interface/cli/output/color_policy.rb +25 -0
- data/lib/csvtool/interface/cli/output/colorizer.rb +27 -0
- data/lib/csvtool/interface/cli/output/formatters/csv_row_formatter.rb +19 -0
- data/lib/csvtool/interface/cli/output/formatters/stats_formatter.rb +57 -0
- data/lib/csvtool/interface/cli/output/streams.rb +22 -0
- data/lib/csvtool/interface/cli/output/table_renderer.rb +70 -0
- data/lib/csvtool/interface/cli/workflows/builders/csv_stats_session_builder.rb +28 -0
- data/lib/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter.rb +17 -5
- data/lib/csvtool/interface/cli/workflows/presenters/csv_parity_presenter.rb +15 -4
- data/lib/csvtool/interface/cli/workflows/presenters/csv_split_presenter.rb +15 -6
- data/lib/csvtool/interface/cli/workflows/presenters/csv_stats_presenter.rb +43 -0
- data/lib/csvtool/interface/cli/workflows/presenters/row_extraction_presenter.rb +5 -4
- data/lib/csvtool/interface/cli/workflows/presenters/row_randomization_presenter.rb +5 -4
- data/lib/csvtool/interface/cli/workflows/run_cross_csv_dedupe_workflow.rb +9 -8
- data/lib/csvtool/interface/cli/workflows/run_csv_parity_workflow.rb +6 -5
- data/lib/csvtool/interface/cli/workflows/run_csv_split_workflow.rb +11 -10
- data/lib/csvtool/interface/cli/workflows/run_csv_stats_workflow.rb +78 -0
- data/lib/csvtool/interface/cli/workflows/run_extraction_workflow.rb +9 -8
- data/lib/csvtool/interface/cli/workflows/run_row_extraction_workflow.rb +7 -6
- data/lib/csvtool/interface/cli/workflows/run_row_randomization_workflow.rb +8 -7
- data/lib/csvtool/interface/cli/workflows/steps/csv_stats/build_session_step.rb +25 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_stats/collect_destination_step.rb +27 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_stats/collect_inputs_step.rb +31 -0
- data/lib/csvtool/interface/cli/workflows/steps/csv_stats/execute_step.rb +27 -0
- data/lib/csvtool/version.rb +1 -1
- data/test/csvtool/application/use_cases/run_csv_stats_test.rb +165 -0
- data/test/csvtool/cli_test.rb +376 -68
- data/test/csvtool/cli_unit_test.rb +5 -5
- data/test/csvtool/infrastructure/csv/csv_stats_scanner_test.rb +68 -0
- data/test/csvtool/infrastructure/output/csv_stats_file_writer_test.rb +38 -0
- data/test/csvtool/interface/cli/menu_loop_test.rb +34 -11
- data/test/csvtool/interface/cli/output/color_policy_test.rb +40 -0
- data/test/csvtool/interface/cli/output/colorizer_test.rb +28 -0
- data/test/csvtool/interface/cli/output/formatters/csv_row_formatter_test.rb +22 -0
- data/test/csvtool/interface/cli/output/formatters/stats_formatter_test.rb +51 -0
- data/test/csvtool/interface/cli/output/streams_test.rb +25 -0
- data/test/csvtool/interface/cli/output/table_renderer_test.rb +36 -0
- data/test/csvtool/interface/cli/workflows/builders/csv_stats_session_builder_test.rb +19 -0
- data/test/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter_test.rb +4 -1
- data/test/csvtool/interface/cli/workflows/presenters/csv_parity_presenter_test.rb +5 -1
- data/test/csvtool/interface/cli/workflows/presenters/csv_split_presenter_test.rb +22 -4
- data/test/csvtool/interface/cli/workflows/presenters/csv_stats_presenter_test.rb +39 -0
- data/test/csvtool/interface/cli/workflows/run_cross_csv_dedupe_workflow_test.rb +10 -7
- data/test/csvtool/interface/cli/workflows/run_csv_parity_workflow_test.rb +3 -1
- data/test/csvtool/interface/cli/workflows/run_csv_split_workflow_test.rb +5 -3
- data/test/csvtool/interface/cli/workflows/run_csv_stats_workflow_test.rb +151 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_stats/build_session_step_test.rb +36 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_stats/collect_destination_step_test.rb +49 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_stats/collect_inputs_step_test.rb +61 -0
- data/test/csvtool/interface/cli/workflows/steps/csv_stats/execute_step_test.rb +65 -0
- metadata +39 -1
|
@@ -38,7 +38,10 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
38
38
|
assert_includes output.string, "3,Cara"
|
|
39
39
|
refute_includes output.string, "2,Bob"
|
|
40
40
|
refute_includes output.string, "4,Dan"
|
|
41
|
-
assert_includes output.string, "Summary
|
|
41
|
+
assert_includes output.string, "Summary"
|
|
42
|
+
assert_includes output.string, "Source rows"
|
|
43
|
+
assert_includes output.string, "Removed rows"
|
|
44
|
+
assert_includes output.string, "Kept rows"
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
def test_can_write_deduped_rows_to_file
|
|
@@ -67,7 +70,7 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
67
70
|
|
|
68
71
|
assert_includes output.string, "Wrote output to #{output_path}"
|
|
69
72
|
assert_equal "customer_id,name\n1,Alice\n3,Cara\n", File.read(output_path)
|
|
70
|
-
assert_includes output.string, "Summary
|
|
73
|
+
assert_includes output.string, "Summary"
|
|
71
74
|
end
|
|
72
75
|
end
|
|
73
76
|
|
|
@@ -119,7 +122,7 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
119
122
|
refute_includes output.string, "customer_id,name"
|
|
120
123
|
assert_includes output.string, "1,Alice"
|
|
121
124
|
assert_includes output.string, "3,Cara"
|
|
122
|
-
assert_includes output.string, "Summary
|
|
125
|
+
assert_includes output.string, "Summary"
|
|
123
126
|
end
|
|
124
127
|
|
|
125
128
|
def test_reports_column_not_found_when_missing
|
|
@@ -164,7 +167,7 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
164
167
|
.new(stdin: StringIO.new(input), stdout: output)
|
|
165
168
|
.call
|
|
166
169
|
|
|
167
|
-
assert_includes output.string, "Summary
|
|
170
|
+
assert_includes output.string, "Summary"
|
|
168
171
|
assert_includes output.string, "No rows removed; no matching keys found."
|
|
169
172
|
end
|
|
170
173
|
|
|
@@ -188,7 +191,7 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
188
191
|
.new(stdin: StringIO.new(input), stdout: output)
|
|
189
192
|
.call
|
|
190
193
|
|
|
191
|
-
assert_includes output.string, "Summary
|
|
194
|
+
assert_includes output.string, "Summary"
|
|
192
195
|
assert_includes output.string, "All source rows were removed by dedupe."
|
|
193
196
|
end
|
|
194
197
|
|
|
@@ -215,7 +218,7 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
215
218
|
refute_includes output.string, " A1 ,Alice"
|
|
216
219
|
refute_includes output.string, "c3,Cara"
|
|
217
220
|
assert_includes output.string, "B2,Bob"
|
|
218
|
-
assert_includes output.string, "Summary
|
|
221
|
+
assert_includes output.string, "Summary"
|
|
219
222
|
end
|
|
220
223
|
|
|
221
224
|
def test_normalization_disabled_preserves_exact_match_behavior
|
|
@@ -241,6 +244,6 @@ class RunCrossCsvDedupeWorkflowTest < Minitest::Test
|
|
|
241
244
|
assert_includes output.string, " A1 ,Alice"
|
|
242
245
|
assert_includes output.string, "B2,Bob"
|
|
243
246
|
assert_includes output.string, "c3,Cara"
|
|
244
|
-
assert_includes output.string, "Summary
|
|
247
|
+
assert_includes output.string, "Summary"
|
|
245
248
|
end
|
|
246
249
|
end
|
|
@@ -62,7 +62,9 @@ class RunCsvParityWorkflowTest < Minitest::Test
|
|
|
62
62
|
assert_includes stdout.string, "Choose separator:"
|
|
63
63
|
assert_includes stdout.string, "Headers present? [Y/n]: "
|
|
64
64
|
assert_includes stdout.string, "MATCH"
|
|
65
|
-
assert_includes stdout.string, "
|
|
65
|
+
assert_includes stdout.string, "Metric"
|
|
66
|
+
assert_includes stdout.string, "Left rows"
|
|
67
|
+
assert_includes stdout.string, "Right rows"
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
def test_prints_mismatch_examples_when_not_equal
|
|
@@ -24,8 +24,9 @@ class RunCsvSplitWorkflowTest < Minitest::Test
|
|
|
24
24
|
).call
|
|
25
25
|
|
|
26
26
|
assert_includes out.string, "Split complete."
|
|
27
|
-
assert_includes out.string, "
|
|
28
|
-
assert_includes out.string, "
|
|
27
|
+
assert_includes out.string, "Metric"
|
|
28
|
+
assert_includes out.string, "Chunk size"
|
|
29
|
+
assert_includes out.string, "Chunks written"
|
|
29
30
|
assert File.file?(File.join(dir, "people_part_001.csv"))
|
|
30
31
|
assert File.file?(File.join(dir, "people_part_002.csv"))
|
|
31
32
|
assert File.file?(File.join(dir, "people_part_003.csv"))
|
|
@@ -194,7 +195,8 @@ class RunCsvSplitWorkflowTest < Minitest::Test
|
|
|
194
195
|
assert_includes lines[1], ",10"
|
|
195
196
|
assert_includes lines[2], ",10"
|
|
196
197
|
assert_includes lines[3], ",5"
|
|
197
|
-
assert_includes out.string, "Manifest
|
|
198
|
+
assert_includes out.string, "Manifest"
|
|
199
|
+
assert_includes out.string, "manifest.csv"
|
|
198
200
|
end
|
|
199
201
|
end
|
|
200
202
|
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/workflows/run_csv_stats_workflow"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class RunCsvStatsWorkflowTest < Minitest::Test
|
|
8
|
+
def fixture_path(name)
|
|
9
|
+
File.expand_path("../../../../fixtures/#{name}", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_workflow_prints_core_stats_summary
|
|
13
|
+
out = StringIO.new
|
|
14
|
+
input = [fixture_path("sample_people.csv"), "", ""].join("\n") + "\n"
|
|
15
|
+
|
|
16
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
17
|
+
stdin: StringIO.new(input),
|
|
18
|
+
stdout: out
|
|
19
|
+
).call
|
|
20
|
+
|
|
21
|
+
assert_includes out.string, "CSV Stats Summary"
|
|
22
|
+
assert_includes out.string, "Metric"
|
|
23
|
+
assert_includes out.string, "Rows"
|
|
24
|
+
assert_includes out.string, "Columns"
|
|
25
|
+
assert_includes out.string, "Headers"
|
|
26
|
+
assert_includes out.string, "Column completeness:"
|
|
27
|
+
assert_includes out.string, "name"
|
|
28
|
+
assert_includes out.string, "city"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_workflow_supports_tsv_separator
|
|
32
|
+
out = StringIO.new
|
|
33
|
+
input = [fixture_path("sample_people.tsv"), "2", ""].join("\n") + "\n"
|
|
34
|
+
|
|
35
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
36
|
+
stdin: StringIO.new(input),
|
|
37
|
+
stdout: out
|
|
38
|
+
).call
|
|
39
|
+
|
|
40
|
+
assert_includes out.string, "Rows"
|
|
41
|
+
assert_includes out.string, "Columns"
|
|
42
|
+
assert_includes out.string, "Headers"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_workflow_supports_headerless_mode
|
|
46
|
+
out = StringIO.new
|
|
47
|
+
input = [fixture_path("sample_people_no_headers.csv"), "", "n"].join("\n") + "\n"
|
|
48
|
+
|
|
49
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
50
|
+
stdin: StringIO.new(input),
|
|
51
|
+
stdout: out
|
|
52
|
+
).call
|
|
53
|
+
|
|
54
|
+
assert_includes out.string, "Rows"
|
|
55
|
+
assert_includes out.string, "Columns"
|
|
56
|
+
refute_includes out.string, "Headers |"
|
|
57
|
+
assert_includes out.string, "column_1"
|
|
58
|
+
assert_includes out.string, "column_2"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_workflow_supports_custom_separator
|
|
62
|
+
out = StringIO.new
|
|
63
|
+
input = [fixture_path("sample_people_colon.txt"), "5", ":", ""].join("\n") + "\n"
|
|
64
|
+
|
|
65
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
66
|
+
stdin: StringIO.new(input),
|
|
67
|
+
stdout: out
|
|
68
|
+
).call
|
|
69
|
+
|
|
70
|
+
assert_includes out.string, "Rows"
|
|
71
|
+
assert_includes out.string, "Columns"
|
|
72
|
+
assert_includes out.string, "Headers"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_workflow_prints_column_completeness_for_blank_values
|
|
76
|
+
out = StringIO.new
|
|
77
|
+
input = [fixture_path("sample_people_blanks.csv"), "", ""].join("\n") + "\n"
|
|
78
|
+
|
|
79
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
80
|
+
stdin: StringIO.new(input),
|
|
81
|
+
stdout: out
|
|
82
|
+
).call
|
|
83
|
+
|
|
84
|
+
assert_includes out.string, "name"
|
|
85
|
+
assert_includes out.string, "3"
|
|
86
|
+
assert_includes out.string, "2"
|
|
87
|
+
assert_includes out.string, "city"
|
|
88
|
+
assert_includes out.string, "4"
|
|
89
|
+
assert_includes out.string, "1"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_workflow_reports_missing_file
|
|
93
|
+
out = StringIO.new
|
|
94
|
+
input = ["/tmp/does-not-exist.csv", "", ""].join("\n") + "\n"
|
|
95
|
+
|
|
96
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
97
|
+
stdin: StringIO.new(input),
|
|
98
|
+
stdout: out
|
|
99
|
+
).call
|
|
100
|
+
|
|
101
|
+
assert_includes out.string, "File not found: /tmp/does-not-exist.csv"
|
|
102
|
+
refute_includes out.string, "Traceback"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_workflow_reports_parse_error
|
|
106
|
+
out = StringIO.new
|
|
107
|
+
input = [fixture_path("sample_people_bad_tail.csv"), "", ""].join("\n") + "\n"
|
|
108
|
+
|
|
109
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
110
|
+
stdin: StringIO.new(input),
|
|
111
|
+
stdout: out
|
|
112
|
+
).call
|
|
113
|
+
|
|
114
|
+
assert_includes out.string, "Could not parse CSV file."
|
|
115
|
+
refute_includes out.string, "Traceback"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_workflow_can_write_stats_to_file
|
|
119
|
+
out = StringIO.new
|
|
120
|
+
|
|
121
|
+
Dir.mktmpdir do |dir|
|
|
122
|
+
output_path = File.join(dir, "stats.csv")
|
|
123
|
+
input = [fixture_path("sample_people.csv"), "", "", "2", output_path].join("\n") + "\n"
|
|
124
|
+
|
|
125
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
126
|
+
stdin: StringIO.new(input),
|
|
127
|
+
stdout: out
|
|
128
|
+
).call
|
|
129
|
+
|
|
130
|
+
assert_includes out.string, "Wrote output to #{output_path}"
|
|
131
|
+
csv_text = File.read(output_path)
|
|
132
|
+
assert_includes csv_text, "metric,value"
|
|
133
|
+
assert_includes csv_text, "row_count,3"
|
|
134
|
+
assert_includes csv_text, "column_count,2"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_workflow_reports_cannot_write_output_file
|
|
139
|
+
out = StringIO.new
|
|
140
|
+
output_path = "/tmp/does-not-exist-dir/stats.csv"
|
|
141
|
+
input = [fixture_path("sample_people.csv"), "", "", "2", output_path].join("\n") + "\n"
|
|
142
|
+
|
|
143
|
+
Csvtool::Interface::CLI::Workflows::RunCsvStatsWorkflow.new(
|
|
144
|
+
stdin: StringIO.new(input),
|
|
145
|
+
stdout: out
|
|
146
|
+
).call
|
|
147
|
+
|
|
148
|
+
assert_includes out.string, "Cannot write output file: #{output_path} (Errno::ENOENT)"
|
|
149
|
+
refute_includes out.string, "Traceback"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/workflows/steps/csv_stats/build_session_step"
|
|
5
|
+
|
|
6
|
+
class CsvStatsBuildSessionStepTest < Minitest::Test
|
|
7
|
+
class FakeBuilder
|
|
8
|
+
attr_reader :params
|
|
9
|
+
|
|
10
|
+
def call(**params)
|
|
11
|
+
@params = params
|
|
12
|
+
:session
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_builds_session_from_context
|
|
17
|
+
builder = FakeBuilder.new
|
|
18
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::BuildSessionStep.new
|
|
19
|
+
context = {
|
|
20
|
+
session_builder: builder,
|
|
21
|
+
file_path: "/tmp/data.csv",
|
|
22
|
+
col_sep: "\t",
|
|
23
|
+
headers_present: true,
|
|
24
|
+
output_destination: :destination
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
result = step.call(context)
|
|
28
|
+
|
|
29
|
+
assert_nil result
|
|
30
|
+
assert_equal :session, context[:session]
|
|
31
|
+
assert_equal "/tmp/data.csv", builder.params[:file_path]
|
|
32
|
+
assert_equal "\t", builder.params[:col_sep]
|
|
33
|
+
assert_equal true, builder.params[:headers_present]
|
|
34
|
+
assert_equal :destination, builder.params[:destination]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/workflows/steps/csv_stats/collect_destination_step"
|
|
5
|
+
|
|
6
|
+
class CsvStatsCollectDestinationStepTest < Minitest::Test
|
|
7
|
+
class FakePrompt
|
|
8
|
+
def initialize(result)
|
|
9
|
+
@result = result
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
@result
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class FakeMapper
|
|
18
|
+
attr_reader :input
|
|
19
|
+
|
|
20
|
+
def call(input)
|
|
21
|
+
@input = input
|
|
22
|
+
:mapped_destination
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_collects_and_maps_destination
|
|
27
|
+
mapper = FakeMapper.new
|
|
28
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::CollectDestinationStep.new(
|
|
29
|
+
output_destination_prompt: FakePrompt.new({ mode: :file, path: "/tmp/out.csv" })
|
|
30
|
+
)
|
|
31
|
+
context = { output_destination_mapper: mapper }
|
|
32
|
+
|
|
33
|
+
result = step.call(context)
|
|
34
|
+
|
|
35
|
+
assert_nil result
|
|
36
|
+
assert_equal({ mode: :file, path: "/tmp/out.csv" }, mapper.input)
|
|
37
|
+
assert_equal :mapped_destination, context[:output_destination]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_halts_when_destination_prompt_returns_nil
|
|
41
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::CollectDestinationStep.new(
|
|
42
|
+
output_destination_prompt: FakePrompt.new(nil)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
result = step.call(output_destination_mapper: FakeMapper.new)
|
|
46
|
+
|
|
47
|
+
assert_equal :halt, result
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/workflows/steps/csv_stats/collect_inputs_step"
|
|
5
|
+
|
|
6
|
+
class CsvStatsCollectInputsStepTest < Minitest::Test
|
|
7
|
+
class FakeFilePathPrompt
|
|
8
|
+
attr_reader :label
|
|
9
|
+
|
|
10
|
+
def call(label:)
|
|
11
|
+
@label = label
|
|
12
|
+
"/tmp/input.csv"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class FakeSeparatorPrompt
|
|
17
|
+
def initialize(result)
|
|
18
|
+
@result = result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
@result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class FakeHeadersPresentPrompt
|
|
27
|
+
def call
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_collects_inputs_into_context
|
|
33
|
+
file_prompt = FakeFilePathPrompt.new
|
|
34
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::CollectInputsStep.new(
|
|
35
|
+
file_path_prompt: file_prompt,
|
|
36
|
+
separator_prompt: FakeSeparatorPrompt.new(";"),
|
|
37
|
+
headers_present_prompt: FakeHeadersPresentPrompt.new
|
|
38
|
+
)
|
|
39
|
+
context = {}
|
|
40
|
+
|
|
41
|
+
result = step.call(context)
|
|
42
|
+
|
|
43
|
+
assert_nil result
|
|
44
|
+
assert_equal "CSV file path: ", file_prompt.label
|
|
45
|
+
assert_equal "/tmp/input.csv", context[:file_path]
|
|
46
|
+
assert_equal ";", context[:col_sep]
|
|
47
|
+
assert_equal false, context[:headers_present]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_halts_when_separator_prompt_returns_nil
|
|
51
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::CollectInputsStep.new(
|
|
52
|
+
file_path_prompt: FakeFilePathPrompt.new,
|
|
53
|
+
separator_prompt: FakeSeparatorPrompt.new(nil),
|
|
54
|
+
headers_present_prompt: FakeHeadersPresentPrompt.new
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
result = step.call({})
|
|
58
|
+
|
|
59
|
+
assert_equal :halt, result
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../../../test_helper"
|
|
4
|
+
require "csvtool/interface/cli/workflows/steps/csv_stats/execute_step"
|
|
5
|
+
|
|
6
|
+
class CsvStatsExecuteStepTest < Minitest::Test
|
|
7
|
+
Result = Struct.new(:ok, :data) do
|
|
8
|
+
def ok? = ok
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class FakeUseCase
|
|
12
|
+
def initialize(result)
|
|
13
|
+
@result = result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(session:)
|
|
17
|
+
@result
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class FakePresenter
|
|
22
|
+
attr_reader :summary_data, :written_path
|
|
23
|
+
|
|
24
|
+
def print_summary(data)
|
|
25
|
+
@summary_data = data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def print_file_written(path)
|
|
29
|
+
@written_path = path
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_prints_summary_and_file_path_when_present
|
|
34
|
+
presenter = FakePresenter.new
|
|
35
|
+
result = Result.new(true, { row_count: 2, output_path: "/tmp/stats.csv" })
|
|
36
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::ExecuteStep.new
|
|
37
|
+
|
|
38
|
+
outcome = step.call(
|
|
39
|
+
session: :session,
|
|
40
|
+
use_case: FakeUseCase.new(result),
|
|
41
|
+
presenter: presenter,
|
|
42
|
+
handle_error: ->(_result) { raise "unexpected" }
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
assert_nil outcome
|
|
46
|
+
assert_equal result.data, presenter.summary_data
|
|
47
|
+
assert_equal "/tmp/stats.csv", presenter.written_path
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_halts_on_use_case_failure
|
|
51
|
+
fail_result = Result.new(false, { reason: :bad })
|
|
52
|
+
handled = []
|
|
53
|
+
step = Csvtool::Interface::CLI::Workflows::Steps::CsvStats::ExecuteStep.new
|
|
54
|
+
|
|
55
|
+
outcome = step.call(
|
|
56
|
+
session: :session,
|
|
57
|
+
use_case: FakeUseCase.new(fail_result),
|
|
58
|
+
presenter: FakePresenter.new,
|
|
59
|
+
handle_error: ->(result) { handled << result }
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert_equal :halt, outcome
|
|
63
|
+
assert_equal [fail_result], handled
|
|
64
|
+
end
|
|
65
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: csvops
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0.alpha
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Hall
|
|
@@ -68,6 +68,7 @@ files:
|
|
|
68
68
|
- bin/tool
|
|
69
69
|
- csvops.gemspec
|
|
70
70
|
- docs/architecture.md
|
|
71
|
+
- docs/cli-output-conventions.md
|
|
71
72
|
- docs/release-v0.1.0-alpha.md
|
|
72
73
|
- docs/release-v0.2.0-alpha.md
|
|
73
74
|
- docs/release-v0.3.0-alpha.md
|
|
@@ -75,10 +76,13 @@ files:
|
|
|
75
76
|
- docs/release-v0.5.0-alpha.md
|
|
76
77
|
- docs/release-v0.6.0-alpha.md
|
|
77
78
|
- docs/release-v0.7.0-alpha.md
|
|
79
|
+
- docs/release-v0.8.0-alpha.md
|
|
80
|
+
- docs/release-v0.9.0-alpha.md
|
|
78
81
|
- exe/csvtool
|
|
79
82
|
- lib/csvtool/application/use_cases/run_cross_csv_dedupe.rb
|
|
80
83
|
- lib/csvtool/application/use_cases/run_csv_parity.rb
|
|
81
84
|
- lib/csvtool/application/use_cases/run_csv_split.rb
|
|
85
|
+
- lib/csvtool/application/use_cases/run_csv_stats.rb
|
|
82
86
|
- lib/csvtool/application/use_cases/run_extraction.rb
|
|
83
87
|
- lib/csvtool/application/use_cases/run_row_extraction.rb
|
|
84
88
|
- lib/csvtool/application/use_cases/run_row_randomization.rb
|
|
@@ -101,6 +105,9 @@ files:
|
|
|
101
105
|
- lib/csvtool/domain/csv_split_session/split_options.rb
|
|
102
106
|
- lib/csvtool/domain/csv_split_session/split_session.rb
|
|
103
107
|
- lib/csvtool/domain/csv_split_session/split_source.rb
|
|
108
|
+
- lib/csvtool/domain/csv_stats_session/stats_options.rb
|
|
109
|
+
- lib/csvtool/domain/csv_stats_session/stats_session.rb
|
|
110
|
+
- lib/csvtool/domain/csv_stats_session/stats_source.rb
|
|
104
111
|
- lib/csvtool/domain/row_randomization_session/randomization_options.rb
|
|
105
112
|
- lib/csvtool/domain/row_randomization_session/randomization_session.rb
|
|
106
113
|
- lib/csvtool/domain/row_randomization_session/randomization_source.rb
|
|
@@ -111,6 +118,7 @@ files:
|
|
|
111
118
|
- lib/csvtool/infrastructure/csv/cross_csv_deduper.rb
|
|
112
119
|
- lib/csvtool/infrastructure/csv/csv_parity_comparator.rb
|
|
113
120
|
- lib/csvtool/infrastructure/csv/csv_splitter.rb
|
|
121
|
+
- lib/csvtool/infrastructure/csv/csv_stats_scanner.rb
|
|
114
122
|
- lib/csvtool/infrastructure/csv/header_reader.rb
|
|
115
123
|
- lib/csvtool/infrastructure/csv/row_randomizer.rb
|
|
116
124
|
- lib/csvtool/infrastructure/csv/row_streamer.rb
|
|
@@ -123,8 +131,15 @@ files:
|
|
|
123
131
|
- lib/csvtool/infrastructure/output/csv_row_console_writer.rb
|
|
124
132
|
- lib/csvtool/infrastructure/output/csv_row_file_writer.rb
|
|
125
133
|
- lib/csvtool/infrastructure/output/csv_split_manifest_writer.rb
|
|
134
|
+
- lib/csvtool/infrastructure/output/csv_stats_file_writer.rb
|
|
126
135
|
- lib/csvtool/interface/cli/errors/presenter.rb
|
|
127
136
|
- lib/csvtool/interface/cli/menu_loop.rb
|
|
137
|
+
- lib/csvtool/interface/cli/output/color_policy.rb
|
|
138
|
+
- lib/csvtool/interface/cli/output/colorizer.rb
|
|
139
|
+
- lib/csvtool/interface/cli/output/formatters/csv_row_formatter.rb
|
|
140
|
+
- lib/csvtool/interface/cli/output/formatters/stats_formatter.rb
|
|
141
|
+
- lib/csvtool/interface/cli/output/streams.rb
|
|
142
|
+
- lib/csvtool/interface/cli/output/table_renderer.rb
|
|
128
143
|
- lib/csvtool/interface/cli/prompts/chunk_size_prompt.rb
|
|
129
144
|
- lib/csvtool/interface/cli/prompts/column_selector_prompt.rb
|
|
130
145
|
- lib/csvtool/interface/cli/prompts/confirm_prompt.rb
|
|
@@ -142,17 +157,20 @@ files:
|
|
|
142
157
|
- lib/csvtool/interface/cli/workflows/builders/cross_csv_dedupe_session_builder.rb
|
|
143
158
|
- lib/csvtool/interface/cli/workflows/builders/csv_parity_session_builder.rb
|
|
144
159
|
- lib/csvtool/interface/cli/workflows/builders/csv_split_session_builder.rb
|
|
160
|
+
- lib/csvtool/interface/cli/workflows/builders/csv_stats_session_builder.rb
|
|
145
161
|
- lib/csvtool/interface/cli/workflows/builders/row_extraction_session_builder.rb
|
|
146
162
|
- lib/csvtool/interface/cli/workflows/builders/row_randomization_session_builder.rb
|
|
147
163
|
- lib/csvtool/interface/cli/workflows/presenters/column_extraction_presenter.rb
|
|
148
164
|
- lib/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter.rb
|
|
149
165
|
- lib/csvtool/interface/cli/workflows/presenters/csv_parity_presenter.rb
|
|
150
166
|
- lib/csvtool/interface/cli/workflows/presenters/csv_split_presenter.rb
|
|
167
|
+
- lib/csvtool/interface/cli/workflows/presenters/csv_stats_presenter.rb
|
|
151
168
|
- lib/csvtool/interface/cli/workflows/presenters/row_extraction_presenter.rb
|
|
152
169
|
- lib/csvtool/interface/cli/workflows/presenters/row_randomization_presenter.rb
|
|
153
170
|
- lib/csvtool/interface/cli/workflows/run_cross_csv_dedupe_workflow.rb
|
|
154
171
|
- lib/csvtool/interface/cli/workflows/run_csv_parity_workflow.rb
|
|
155
172
|
- lib/csvtool/interface/cli/workflows/run_csv_split_workflow.rb
|
|
173
|
+
- lib/csvtool/interface/cli/workflows/run_csv_stats_workflow.rb
|
|
156
174
|
- lib/csvtool/interface/cli/workflows/run_extraction_workflow.rb
|
|
157
175
|
- lib/csvtool/interface/cli/workflows/run_row_extraction_workflow.rb
|
|
158
176
|
- lib/csvtool/interface/cli/workflows/run_row_randomization_workflow.rb
|
|
@@ -164,6 +182,10 @@ files:
|
|
|
164
182
|
- lib/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step.rb
|
|
165
183
|
- lib/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step.rb
|
|
166
184
|
- lib/csvtool/interface/cli/workflows/steps/csv_split/execute_step.rb
|
|
185
|
+
- lib/csvtool/interface/cli/workflows/steps/csv_stats/build_session_step.rb
|
|
186
|
+
- lib/csvtool/interface/cli/workflows/steps/csv_stats/collect_destination_step.rb
|
|
187
|
+
- lib/csvtool/interface/cli/workflows/steps/csv_stats/collect_inputs_step.rb
|
|
188
|
+
- lib/csvtool/interface/cli/workflows/steps/csv_stats/execute_step.rb
|
|
167
189
|
- lib/csvtool/interface/cli/workflows/steps/extraction/build_preview_step.rb
|
|
168
190
|
- lib/csvtool/interface/cli/workflows/steps/extraction/collect_destination_step.rb
|
|
169
191
|
- lib/csvtool/interface/cli/workflows/steps/extraction/collect_inputs_step.rb
|
|
@@ -188,6 +210,7 @@ files:
|
|
|
188
210
|
- test/csvtool/application/use_cases/run_cross_csv_dedupe_test.rb
|
|
189
211
|
- test/csvtool/application/use_cases/run_csv_parity_test.rb
|
|
190
212
|
- test/csvtool/application/use_cases/run_csv_split_test.rb
|
|
213
|
+
- test/csvtool/application/use_cases/run_csv_stats_test.rb
|
|
191
214
|
- test/csvtool/application/use_cases/run_extraction_test.rb
|
|
192
215
|
- test/csvtool/application/use_cases/run_row_extraction_test.rb
|
|
193
216
|
- test/csvtool/application/use_cases/run_row_randomization_test.rb
|
|
@@ -218,6 +241,7 @@ files:
|
|
|
218
241
|
- test/csvtool/infrastructure/csv/cross_csv_deduper_test.rb
|
|
219
242
|
- test/csvtool/infrastructure/csv/csv_parity_comparator_test.rb
|
|
220
243
|
- test/csvtool/infrastructure/csv/csv_splitter_test.rb
|
|
244
|
+
- test/csvtool/infrastructure/csv/csv_stats_scanner_test.rb
|
|
221
245
|
- test/csvtool/infrastructure/csv/header_reader_test.rb
|
|
222
246
|
- test/csvtool/infrastructure/csv/row_randomizer_test.rb
|
|
223
247
|
- test/csvtool/infrastructure/csv/row_streamer_test.rb
|
|
@@ -230,8 +254,15 @@ files:
|
|
|
230
254
|
- test/csvtool/infrastructure/output/csv_row_console_writer_test.rb
|
|
231
255
|
- test/csvtool/infrastructure/output/csv_row_file_writer_test.rb
|
|
232
256
|
- test/csvtool/infrastructure/output/csv_split_manifest_writer_test.rb
|
|
257
|
+
- test/csvtool/infrastructure/output/csv_stats_file_writer_test.rb
|
|
233
258
|
- test/csvtool/interface/cli/errors/presenter_test.rb
|
|
234
259
|
- test/csvtool/interface/cli/menu_loop_test.rb
|
|
260
|
+
- test/csvtool/interface/cli/output/color_policy_test.rb
|
|
261
|
+
- test/csvtool/interface/cli/output/colorizer_test.rb
|
|
262
|
+
- test/csvtool/interface/cli/output/formatters/csv_row_formatter_test.rb
|
|
263
|
+
- test/csvtool/interface/cli/output/formatters/stats_formatter_test.rb
|
|
264
|
+
- test/csvtool/interface/cli/output/streams_test.rb
|
|
265
|
+
- test/csvtool/interface/cli/output/table_renderer_test.rb
|
|
235
266
|
- test/csvtool/interface/cli/prompts/chunk_size_prompt_test.rb
|
|
236
267
|
- test/csvtool/interface/cli/prompts/column_selector_prompt_test.rb
|
|
237
268
|
- test/csvtool/interface/cli/prompts/confirm_prompt_test.rb
|
|
@@ -249,17 +280,20 @@ files:
|
|
|
249
280
|
- test/csvtool/interface/cli/workflows/builders/cross_csv_dedupe_session_builder_test.rb
|
|
250
281
|
- test/csvtool/interface/cli/workflows/builders/csv_parity_session_builder_test.rb
|
|
251
282
|
- test/csvtool/interface/cli/workflows/builders/csv_split_session_builder_test.rb
|
|
283
|
+
- test/csvtool/interface/cli/workflows/builders/csv_stats_session_builder_test.rb
|
|
252
284
|
- test/csvtool/interface/cli/workflows/builders/row_extraction_session_builder_test.rb
|
|
253
285
|
- test/csvtool/interface/cli/workflows/builders/row_randomization_session_builder_test.rb
|
|
254
286
|
- test/csvtool/interface/cli/workflows/presenters/column_extraction_presenter_test.rb
|
|
255
287
|
- test/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter_test.rb
|
|
256
288
|
- test/csvtool/interface/cli/workflows/presenters/csv_parity_presenter_test.rb
|
|
257
289
|
- test/csvtool/interface/cli/workflows/presenters/csv_split_presenter_test.rb
|
|
290
|
+
- test/csvtool/interface/cli/workflows/presenters/csv_stats_presenter_test.rb
|
|
258
291
|
- test/csvtool/interface/cli/workflows/presenters/row_extraction_presenter_test.rb
|
|
259
292
|
- test/csvtool/interface/cli/workflows/presenters/row_randomization_presenter_test.rb
|
|
260
293
|
- test/csvtool/interface/cli/workflows/run_cross_csv_dedupe_workflow_test.rb
|
|
261
294
|
- test/csvtool/interface/cli/workflows/run_csv_parity_workflow_test.rb
|
|
262
295
|
- test/csvtool/interface/cli/workflows/run_csv_split_workflow_test.rb
|
|
296
|
+
- test/csvtool/interface/cli/workflows/run_csv_stats_workflow_test.rb
|
|
263
297
|
- test/csvtool/interface/cli/workflows/run_extraction_workflow_test.rb
|
|
264
298
|
- test/csvtool/interface/cli/workflows/run_row_extraction_workflow_test.rb
|
|
265
299
|
- test/csvtool/interface/cli/workflows/run_row_randomization_workflow_test.rb
|
|
@@ -269,6 +303,10 @@ files:
|
|
|
269
303
|
- test/csvtool/interface/cli/workflows/steps/csv_split/collect_manifest_step_test.rb
|
|
270
304
|
- test/csvtool/interface/cli/workflows/steps/csv_split/collect_output_step_test.rb
|
|
271
305
|
- test/csvtool/interface/cli/workflows/steps/csv_split/execute_step_test.rb
|
|
306
|
+
- test/csvtool/interface/cli/workflows/steps/csv_stats/build_session_step_test.rb
|
|
307
|
+
- test/csvtool/interface/cli/workflows/steps/csv_stats/collect_destination_step_test.rb
|
|
308
|
+
- test/csvtool/interface/cli/workflows/steps/csv_stats/collect_inputs_step_test.rb
|
|
309
|
+
- test/csvtool/interface/cli/workflows/steps/csv_stats/execute_step_test.rb
|
|
272
310
|
- test/csvtool/interface/cli/workflows/steps/extraction/collect_inputs_step_test.rb
|
|
273
311
|
- test/csvtool/interface/cli/workflows/steps/parity/build_session_step_test.rb
|
|
274
312
|
- test/csvtool/interface/cli/workflows/steps/parity/collect_inputs_step_test.rb
|