cov-loupe 3.0.0 → 4.0.0.pre
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/AGENTS.md +230 -0
- data/CLAUDE.md +5 -0
- data/CODE_OF_CONDUCT.md +62 -0
- data/CONTRIBUTING.md +102 -0
- data/GEMINI.md +5 -0
- data/README.md +154 -51
- data/RELEASE_NOTES.md +452 -0
- data/dev/images/cov-loupe-icon-lores.png +0 -0
- data/dev/images/cov-loupe-icon-square.png +0 -0
- data/dev/images/cov-loupe-icon.png +0 -0
- data/dev/images/cov-loupe-logo.png +0 -0
- data/dev/prompts/README.md +74 -0
- data/dev/prompts/archive/architectural-review-and-actions-prompt.md +53 -0
- data/dev/prompts/archive/investigate-and-report-issues-prompt.md +33 -0
- data/dev/prompts/archive/produce-action-items-prompt.md +25 -0
- data/dev/prompts/guidelines/ai-code-evaluator-guidelines.md +337 -0
- data/dev/prompts/improve/refactor-test-suite.md +18 -0
- data/dev/prompts/improve/simplify-code-logic.md +133 -0
- data/dev/prompts/improve/update-documentation.md +21 -0
- data/dev/prompts/review/comprehensive-codebase-review.md +176 -0
- data/dev/prompts/review/identify-action-items.md +143 -0
- data/dev/prompts/review/verify-code-changes.md +54 -0
- data/dev/prompts/validate/create-screencast-outline.md +234 -0
- data/dev/prompts/validate/test-documentation-examples.md +180 -0
- data/docs/QUICKSTART.md +63 -0
- data/docs/assets/images/cov-loupe-logo-lores.png +0 -0
- data/docs/assets/images/cov-loupe-logo.png +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/assets/stylesheets/branding.css +16 -0
- data/docs/assets/stylesheets/extra.css +15 -0
- data/docs/code_of_conduct.md +1 -0
- data/docs/contributing.md +1 -0
- data/docs/dev/ARCHITECTURE.md +56 -11
- data/docs/dev/DEVELOPMENT.md +116 -12
- data/docs/dev/FUTURE_ENHANCEMENTS.md +14 -0
- data/docs/dev/README.md +3 -2
- data/docs/dev/RELEASING.md +2 -0
- data/docs/dev/arch-decisions/README.md +10 -7
- data/docs/dev/arch-decisions/application-architecture.md +259 -0
- data/docs/dev/arch-decisions/coverage-data-quality.md +193 -0
- data/docs/dev/arch-decisions/output-character-mode.md +217 -0
- data/docs/dev/arch-decisions/path-resolution.md +90 -0
- data/docs/dev/arch-decisions/{004-x-arch-decision.md → policy-validation.md} +32 -28
- data/docs/dev/arch-decisions/{005-x-arch-decision.md → simplecov-integration.md} +47 -44
- data/docs/dev/presentations/cov-loupe-presentation.md +15 -13
- data/docs/examples/mcp-inputs.md +3 -0
- data/docs/examples/prompts.md +3 -0
- data/docs/examples/success_predicates.md +3 -0
- data/docs/fixtures/demo_project/.resultset.json +170 -0
- data/docs/fixtures/demo_project/README.md +6 -0
- data/docs/fixtures/demo_project/app/controllers/admin/audit_logs_controller.rb +19 -0
- data/docs/fixtures/demo_project/app/controllers/orders_controller.rb +26 -0
- data/docs/fixtures/demo_project/app/models/order.rb +20 -0
- data/docs/fixtures/demo_project/app/models/user.rb +19 -0
- data/docs/fixtures/demo_project/lib/api/client.rb +22 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/cleanup_job.rb +16 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/report_job.rb +17 -0
- data/docs/fixtures/demo_project/lib/payments/processor.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/refund_service.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/reporting/exporter.rb +16 -0
- data/docs/index.md +1 -0
- data/docs/license.md +3 -0
- data/docs/release_notes.md +3 -0
- data/docs/user/ADVANCED_USAGE.md +208 -115
- data/docs/user/CLI_FALLBACK_FOR_LLMS.md +2 -0
- data/docs/user/CLI_USAGE.md +276 -101
- data/docs/user/ERROR_HANDLING.md +4 -4
- data/docs/user/EXAMPLES.md +121 -128
- data/docs/user/INSTALLATION.md +9 -28
- data/docs/user/LIBRARY_API.md +227 -122
- data/docs/user/MCP_INTEGRATION.md +114 -203
- data/docs/user/README.md +5 -1
- data/docs/user/TROUBLESHOOTING.md +49 -27
- data/docs/user/installing-a-prelease-version-of-covloupe.md +43 -0
- data/docs/user/{V2-BREAKING-CHANGES.md → migrations/MIGRATING_TO_V2.md} +62 -72
- data/docs/user/migrations/MIGRATING_TO_V3.md +72 -0
- data/docs/user/migrations/MIGRATING_TO_V4.md +591 -0
- data/docs/user/migrations/README.md +22 -0
- data/docs/user/prompts/README.md +9 -0
- data/docs/user/prompts/non-web-coverage-analysis-prompt.md +103 -0
- data/docs/user/prompts/rails-coverage-analysis-prompt.md +94 -0
- data/docs/user/prompts/use-cli-not-mcp-prompt.md +53 -0
- data/examples/cli_demo.sh +77 -0
- data/examples/filter_and_table_demo-output.md +114 -0
- data/examples/filter_and_table_demo.rb +174 -0
- data/examples/fixtures/demo_project/coverage/.resultset.json +10 -0
- data/examples/mcp-inputs/README.md +66 -0
- data/examples/mcp-inputs/coverage_detailed.json +1 -0
- data/examples/mcp-inputs/coverage_raw.json +1 -0
- data/examples/mcp-inputs/coverage_summary.json +1 -0
- data/examples/mcp-inputs/list.json +1 -0
- data/examples/mcp-inputs/uncovered_lines.json +1 -0
- data/examples/prompts/README.md +27 -0
- data/examples/prompts/custom_resultset.txt +2 -0
- data/examples/prompts/detailed_with_source.txt +2 -0
- data/examples/prompts/list_lowest.txt +2 -0
- data/examples/prompts/summary.txt +2 -0
- data/examples/prompts/uncovered.txt +2 -0
- data/examples/success_predicates/README.md +198 -0
- data/examples/success_predicates/all_files_above_threshold_predicate.rb +21 -0
- data/examples/success_predicates/directory_specific_thresholds_predicate.rb +30 -0
- data/examples/success_predicates/project_coverage_minimum_predicate.rb +6 -0
- data/lib/cov_loupe/base_tool.rb +229 -20
- data/lib/cov_loupe/cli.rb +132 -23
- data/lib/cov_loupe/commands/base_command.rb +25 -6
- data/lib/cov_loupe/commands/command_factory.rb +0 -1
- data/lib/cov_loupe/commands/detailed_command.rb +10 -5
- data/lib/cov_loupe/commands/list_command.rb +2 -1
- data/lib/cov_loupe/commands/raw_command.rb +7 -5
- data/lib/cov_loupe/commands/summary_command.rb +12 -7
- data/lib/cov_loupe/commands/totals_command.rb +74 -10
- data/lib/cov_loupe/commands/uncovered_command.rb +7 -5
- data/lib/cov_loupe/commands/validate_command.rb +11 -3
- data/lib/cov_loupe/commands/version_command.rb +6 -4
- data/lib/cov_loupe/{app_config.rb → config/app_config.rb} +13 -5
- data/lib/cov_loupe/config/app_context.rb +43 -0
- data/lib/cov_loupe/config/boolean_type.rb +91 -0
- data/lib/cov_loupe/config/logger.rb +92 -0
- data/lib/cov_loupe/{option_normalizers.rb → config/option_normalizers.rb} +55 -24
- data/lib/cov_loupe/{option_parser_builder.rb → config/option_parser_builder.rb} +46 -24
- data/lib/cov_loupe/coverage/coverage_calculator.rb +53 -0
- data/lib/cov_loupe/coverage/coverage_reporter.rb +63 -0
- data/lib/cov_loupe/coverage/coverage_table_formatter.rb +133 -0
- data/lib/cov_loupe/{error_handler.rb → errors/error_handler.rb} +21 -33
- data/lib/cov_loupe/{errors.rb → errors/errors.rb} +48 -71
- data/lib/cov_loupe/formatters/formatters.rb +75 -0
- data/lib/cov_loupe/formatters/source_formatter.rb +18 -7
- data/lib/cov_loupe/formatters/table_formatter.rb +80 -0
- data/lib/cov_loupe/loaders/all.rb +15 -0
- data/lib/cov_loupe/loaders/all_cli.rb +10 -0
- data/lib/cov_loupe/loaders/all_mcp.rb +23 -0
- data/lib/cov_loupe/loaders/resultset_loader.rb +147 -0
- data/lib/cov_loupe/mcp_server.rb +3 -2
- data/lib/cov_loupe/model/model.rb +520 -0
- data/lib/cov_loupe/model/model_data.rb +13 -0
- data/lib/cov_loupe/model/model_data_cache.rb +116 -0
- data/lib/cov_loupe/option_parsers/env_options_parser.rb +17 -6
- data/lib/cov_loupe/option_parsers/error_helper.rb +16 -10
- data/lib/cov_loupe/output_chars.rb +192 -0
- data/lib/cov_loupe/paths/glob_utils.rb +100 -0
- data/lib/cov_loupe/{path_relativizer.rb → paths/path_relativizer.rb} +5 -13
- data/lib/cov_loupe/paths/path_utils.rb +265 -0
- data/lib/cov_loupe/paths/volume_case_sensitivity.rb +173 -0
- data/lib/cov_loupe/presenters/base_coverage_presenter.rb +9 -13
- data/lib/cov_loupe/presenters/coverage_payload_presenter.rb +21 -0
- data/lib/cov_loupe/presenters/payload_caching.rb +23 -0
- data/lib/cov_loupe/presenters/project_coverage_presenter.rb +73 -21
- data/lib/cov_loupe/presenters/project_totals_presenter.rb +16 -10
- data/lib/cov_loupe/repositories/coverage_repository.rb +149 -0
- data/lib/cov_loupe/resolvers/coverage_line_resolver.rb +90 -76
- data/lib/cov_loupe/resolvers/{resolver_factory.rb → resolver_helpers.rb} +6 -5
- data/lib/cov_loupe/resolvers/resultset_path_resolver.rb +40 -12
- data/lib/cov_loupe/scripts/command_execution.rb +113 -0
- data/lib/cov_loupe/scripts/latest_ci_status.rb +97 -0
- data/lib/cov_loupe/scripts/pre_release_check.rb +164 -0
- data/lib/cov_loupe/scripts/setup_doc_server.rb +23 -0
- data/lib/cov_loupe/scripts/start_doc_server.rb +24 -0
- data/lib/cov_loupe/staleness/stale_status.rb +23 -0
- data/lib/cov_loupe/staleness/staleness_checker.rb +328 -0
- data/lib/cov_loupe/staleness/staleness_message_formatter.rb +91 -0
- data/lib/cov_loupe/tools/coverage_detailed_tool.rb +14 -15
- data/lib/cov_loupe/tools/coverage_raw_tool.rb +14 -14
- data/lib/cov_loupe/tools/coverage_summary_tool.rb +16 -16
- data/lib/cov_loupe/tools/coverage_table_tool.rb +139 -21
- data/lib/cov_loupe/tools/coverage_totals_tool.rb +31 -13
- data/lib/cov_loupe/tools/help_tool.rb +16 -20
- data/lib/cov_loupe/tools/list_tool.rb +65 -0
- data/lib/cov_loupe/tools/uncovered_lines_tool.rb +14 -14
- data/lib/cov_loupe/tools/validate_tool.rb +18 -24
- data/lib/cov_loupe/tools/version_tool.rb +8 -3
- data/lib/cov_loupe/version.rb +1 -1
- data/lib/cov_loupe.rb +83 -55
- metadata +184 -154
- data/docs/dev/BRANCH_ONLY_COVERAGE.md +0 -158
- data/docs/dev/arch-decisions/001-x-arch-decision.md +0 -95
- data/docs/dev/arch-decisions/002-x-arch-decision.md +0 -159
- data/docs/dev/arch-decisions/003-x-arch-decision.md +0 -165
- data/lib/cov_loupe/app_context.rb +0 -26
- data/lib/cov_loupe/constants.rb +0 -22
- data/lib/cov_loupe/coverage_reporter.rb +0 -31
- data/lib/cov_loupe/formatters.rb +0 -51
- data/lib/cov_loupe/mode_detector.rb +0 -56
- data/lib/cov_loupe/model.rb +0 -339
- data/lib/cov_loupe/presenters/coverage_detailed_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_raw_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_summary_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_uncovered_presenter.rb +0 -14
- data/lib/cov_loupe/resultset_loader.rb +0 -131
- data/lib/cov_loupe/staleness_checker.rb +0 -247
- data/lib/cov_loupe/table_formatter.rb +0 -64
- data/lib/cov_loupe/tools/all_files_coverage_tool.rb +0 -51
- data/lib/cov_loupe/util.rb +0 -88
- data/spec/MCP_INTEGRATION_TESTS_README.md +0 -111
- data/spec/TIMESTAMPS.md +0 -48
- data/spec/all_files_coverage_tool_spec.rb +0 -53
- data/spec/app_config_spec.rb +0 -142
- data/spec/base_tool_spec.rb +0 -62
- data/spec/cli/show_default_report_spec.rb +0 -33
- data/spec/cli_enumerated_options_spec.rb +0 -90
- data/spec/cli_error_spec.rb +0 -184
- data/spec/cli_format_spec.rb +0 -123
- data/spec/cli_json_options_spec.rb +0 -50
- data/spec/cli_source_spec.rb +0 -44
- data/spec/cli_spec.rb +0 -192
- data/spec/cli_table_spec.rb +0 -28
- data/spec/cli_usage_spec.rb +0 -42
- data/spec/commands/base_command_spec.rb +0 -107
- data/spec/commands/command_factory_spec.rb +0 -76
- data/spec/commands/detailed_command_spec.rb +0 -34
- data/spec/commands/list_command_spec.rb +0 -28
- data/spec/commands/raw_command_spec.rb +0 -69
- data/spec/commands/summary_command_spec.rb +0 -34
- data/spec/commands/totals_command_spec.rb +0 -34
- data/spec/commands/uncovered_command_spec.rb +0 -55
- data/spec/commands/validate_command_spec.rb +0 -213
- data/spec/commands/version_command_spec.rb +0 -38
- data/spec/constants_spec.rb +0 -61
- data/spec/cov_loupe/formatters/source_formatter_spec.rb +0 -267
- data/spec/cov_loupe/formatters_spec.rb +0 -76
- data/spec/cov_loupe/presenters/base_coverage_presenter_spec.rb +0 -79
- data/spec/cov_loupe_model_spec.rb +0 -454
- data/spec/cov_loupe_module_spec.rb +0 -37
- data/spec/cov_loupe_opts_spec.rb +0 -185
- data/spec/coverage_reporter_spec.rb +0 -102
- data/spec/coverage_table_tool_spec.rb +0 -59
- data/spec/coverage_totals_tool_spec.rb +0 -37
- data/spec/error_handler_spec.rb +0 -197
- data/spec/error_mode_spec.rb +0 -139
- data/spec/errors_edge_cases_spec.rb +0 -312
- data/spec/errors_stale_spec.rb +0 -83
- data/spec/file_based_mcp_tools_spec.rb +0 -99
- data/spec/help_tool_spec.rb +0 -26
- data/spec/integration_spec.rb +0 -789
- data/spec/logging_fallback_spec.rb +0 -128
- data/spec/mcp_logging_spec.rb +0 -44
- data/spec/mcp_server_integration_spec.rb +0 -23
- data/spec/mcp_server_spec.rb +0 -106
- data/spec/mode_detector_spec.rb +0 -153
- data/spec/model_error_handling_spec.rb +0 -269
- data/spec/model_staleness_spec.rb +0 -79
- data/spec/option_normalizers_spec.rb +0 -203
- data/spec/option_parsers/env_options_parser_spec.rb +0 -221
- data/spec/option_parsers/error_helper_spec.rb +0 -222
- data/spec/path_relativizer_spec.rb +0 -98
- data/spec/presenters/coverage_detailed_presenter_spec.rb +0 -19
- data/spec/presenters/coverage_raw_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_summary_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_uncovered_presenter_spec.rb +0 -16
- data/spec/presenters/project_coverage_presenter_spec.rb +0 -87
- data/spec/presenters/project_totals_presenter_spec.rb +0 -144
- data/spec/resolvers/coverage_line_resolver_spec.rb +0 -282
- data/spec/resolvers/resolver_factory_spec.rb +0 -61
- data/spec/resolvers/resultset_path_resolver_spec.rb +0 -60
- data/spec/resultset_loader_spec.rb +0 -167
- data/spec/shared_examples/README.md +0 -115
- data/spec/shared_examples/coverage_presenter_examples.rb +0 -66
- data/spec/shared_examples/file_based_mcp_tools.rb +0 -179
- data/spec/shared_examples/formatted_command_examples.rb +0 -64
- data/spec/shared_examples/mcp_tool_text_json_response.rb +0 -16
- data/spec/spec_helper.rb +0 -127
- data/spec/staleness_checker_spec.rb +0 -374
- data/spec/staleness_more_spec.rb +0 -42
- data/spec/support/cli_helpers.rb +0 -22
- data/spec/support/control_flow_helpers.rb +0 -20
- data/spec/support/fake_mcp.rb +0 -40
- data/spec/support/io_helpers.rb +0 -29
- data/spec/support/mcp_helpers.rb +0 -35
- data/spec/support/mcp_runner.rb +0 -66
- data/spec/support/mocking_helpers.rb +0 -30
- data/spec/table_format_spec.rb +0 -70
- data/spec/tools/validate_tool_spec.rb +0 -132
- data/spec/tools_error_handling_spec.rb +0 -130
- data/spec/util_spec.rb +0 -154
- data/spec/version_spec.rb +0 -123
- data/spec/version_tool_spec.rb +0 -141
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/bar.rb +0 -0
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/foo.rb +0 -0
- /data/lib/cov_loupe/{config_parser.rb → config/config_parser.rb} +0 -0
- /data/lib/cov_loupe/{predicate_evaluator.rb → config/predicate_evaluator.rb} +0 -0
- /data/lib/cov_loupe/{error_handler_factory.rb → errors/error_handler_factory.rb} +0 -0
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
RSpec.describe CovLoupe::Presenters::ProjectCoveragePresenter do
|
|
6
|
-
subject(:presenter) do
|
|
7
|
-
described_class.new(
|
|
8
|
-
model: model,
|
|
9
|
-
sort_order: sort_order,
|
|
10
|
-
check_stale: check_stale,
|
|
11
|
-
tracked_globs: tracked_globs
|
|
12
|
-
)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
let(:model) { instance_double(CovLoupe::CoverageModel) }
|
|
16
|
-
let(:sort_order) { :ascending }
|
|
17
|
-
let(:check_stale) { true }
|
|
18
|
-
let(:tracked_globs) { ['lib/**/*.rb'] }
|
|
19
|
-
let(:files) do
|
|
20
|
-
[
|
|
21
|
-
{
|
|
22
|
-
'file' => '/abs/path/lib/foo.rb',
|
|
23
|
-
'covered' => 5,
|
|
24
|
-
'total' => 6,
|
|
25
|
-
'percentage' => 83.33,
|
|
26
|
-
'stale' => false
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
'file' => '/abs/path/lib/bar.rb',
|
|
30
|
-
'covered' => 1,
|
|
31
|
-
'total' => 6,
|
|
32
|
-
'percentage' => 16.67,
|
|
33
|
-
'stale' => 'L'
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
before do
|
|
40
|
-
allow(model).to receive(:all_files).with(sort_order: sort_order, check_stale: check_stale,
|
|
41
|
-
tracked_globs: tracked_globs).and_return(files)
|
|
42
|
-
allow(model).to receive(:relativize) do |payload|
|
|
43
|
-
relativizer = CovLoupe::PathRelativizer.new(
|
|
44
|
-
root: '/abs/path',
|
|
45
|
-
scalar_keys: %w[file file_path],
|
|
46
|
-
array_keys: %w[newer_files missing_files deleted_files]
|
|
47
|
-
)
|
|
48
|
-
relativizer.relativize(payload)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
describe '#absolute_payload' do
|
|
53
|
-
it 'returns files and counts with stale metadata' do
|
|
54
|
-
payload = presenter.absolute_payload
|
|
55
|
-
|
|
56
|
-
expect(payload['files']).to eq(files)
|
|
57
|
-
expect(payload['counts']).to eq({ 'total' => 2, 'ok' => 1, 'stale' => 1 })
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it 'memoizes the computed payload' do
|
|
61
|
-
presenter.absolute_payload
|
|
62
|
-
presenter.absolute_payload
|
|
63
|
-
|
|
64
|
-
expect(model).to have_received(:all_files).once
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
describe '#relativized_payload' do
|
|
69
|
-
it 'relativizes the files list' do
|
|
70
|
-
relativized = presenter.relativized_payload
|
|
71
|
-
|
|
72
|
-
expect(relativized['files'].map { |f| f['file'] }).to eq(['lib/foo.rb', 'lib/bar.rb'])
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe '#relative_files' do
|
|
77
|
-
it 'returns the relativized file list' do
|
|
78
|
-
expect(presenter.relative_files.map { |f| f['file'] }).to eq(['lib/foo.rb', 'lib/bar.rb'])
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
describe '#relative_counts' do
|
|
83
|
-
it 'returns the relativized counts hash' do
|
|
84
|
-
expect(presenter.relative_counts).to eq({ 'total' => 2, 'ok' => 1, 'stale' => 1 })
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
RSpec.describe CovLoupe::Presenters::ProjectTotalsPresenter do
|
|
6
|
-
subject(:presenter) do
|
|
7
|
-
described_class.new(
|
|
8
|
-
model: model,
|
|
9
|
-
check_stale: true,
|
|
10
|
-
tracked_globs: ['lib/**/*.rb']
|
|
11
|
-
)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
let(:model) { instance_double(CovLoupe::CoverageModel) }
|
|
15
|
-
let(:raw_totals) do
|
|
16
|
-
{
|
|
17
|
-
'lines' => { 'total' => 100, 'covered' => 80, 'uncovered' => 20 },
|
|
18
|
-
'percentage' => 80.0,
|
|
19
|
-
'files' => { 'total' => 10, 'ok' => 9, 'stale' => 1 }
|
|
20
|
-
}
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
before do
|
|
24
|
-
allow(model).to receive(:project_totals)
|
|
25
|
-
.with(tracked_globs: ['lib/**/*.rb'], check_stale: true)
|
|
26
|
-
.and_return(raw_totals)
|
|
27
|
-
allow(model).to receive(:relativize) { |payload| payload }
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
describe '#initialize' do
|
|
31
|
-
it 'stores the model, check_stale, and tracked_globs options' do
|
|
32
|
-
expect(presenter.model).to eq(model)
|
|
33
|
-
expect(presenter.check_stale).to be(true)
|
|
34
|
-
expect(presenter.tracked_globs).to eq(['lib/**/*.rb'])
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
describe '#absolute_payload' do
|
|
39
|
-
it 'returns project totals from the model' do
|
|
40
|
-
result = presenter.absolute_payload
|
|
41
|
-
|
|
42
|
-
expect(result).to include('lines', 'percentage', 'files')
|
|
43
|
-
expect(result['lines']).to include('total' => 100, 'covered' => 80, 'uncovered' => 20)
|
|
44
|
-
expect(result['percentage']).to eq(80.0)
|
|
45
|
-
expect(result['files']).to include('total' => 10, 'ok' => 9, 'stale' => 1)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it 'caches the result on subsequent calls' do
|
|
49
|
-
presenter.absolute_payload
|
|
50
|
-
presenter.absolute_payload
|
|
51
|
-
|
|
52
|
-
expect(model).to have_received(:project_totals).once
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it 'passes tracked_globs to the model' do
|
|
56
|
-
presenter.absolute_payload
|
|
57
|
-
|
|
58
|
-
expect(model).to have_received(:project_totals)
|
|
59
|
-
.with(tracked_globs: ['lib/**/*.rb'], check_stale: true)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
describe '#relativized_payload' do
|
|
64
|
-
it 'returns the relativized payload from the model' do
|
|
65
|
-
result = presenter.relativized_payload
|
|
66
|
-
|
|
67
|
-
expect(result).to eq(raw_totals)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it 'calls relativize on the model' do
|
|
71
|
-
presenter.relativized_payload
|
|
72
|
-
|
|
73
|
-
expect(model).to have_received(:relativize).with(raw_totals)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it 'caches the result on subsequent calls' do
|
|
77
|
-
presenter.relativized_payload
|
|
78
|
-
presenter.relativized_payload
|
|
79
|
-
|
|
80
|
-
expect(model).to have_received(:relativize).once
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
context 'with check_stale: false' do
|
|
85
|
-
subject(:presenter) do
|
|
86
|
-
described_class.new(
|
|
87
|
-
model: model,
|
|
88
|
-
check_stale: false,
|
|
89
|
-
tracked_globs: nil
|
|
90
|
-
)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
before do
|
|
94
|
-
allow(model).to receive(:project_totals)
|
|
95
|
-
.with(tracked_globs: nil, check_stale: false)
|
|
96
|
-
.and_return(raw_totals)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
it 'passes check_stale: false to the model' do
|
|
100
|
-
presenter.absolute_payload
|
|
101
|
-
|
|
102
|
-
expect(model).to have_received(:project_totals)
|
|
103
|
-
.with(tracked_globs: nil, check_stale: false)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
context 'with empty tracked_globs' do
|
|
108
|
-
subject(:presenter) do
|
|
109
|
-
described_class.new(
|
|
110
|
-
model: model,
|
|
111
|
-
check_stale: true,
|
|
112
|
-
tracked_globs: []
|
|
113
|
-
)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
before do
|
|
117
|
-
allow(model).to receive(:project_totals)
|
|
118
|
-
.with(tracked_globs: [], check_stale: true)
|
|
119
|
-
.and_return(raw_totals)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it 'passes empty tracked_globs to the model' do
|
|
123
|
-
presenter.absolute_payload
|
|
124
|
-
|
|
125
|
-
expect(model).to have_received(:project_totals)
|
|
126
|
-
.with(tracked_globs: [], check_stale: true)
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
context 'with relativization that transforms data' do
|
|
131
|
-
before do
|
|
132
|
-
allow(model).to receive(:relativize) do |payload|
|
|
133
|
-
# Simulate relativization that might transform file paths in nested data
|
|
134
|
-
payload.merge('transformed' => true)
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it 'applies the transformation from relativize' do
|
|
139
|
-
result = presenter.relativized_payload
|
|
140
|
-
|
|
141
|
-
expect(result['transformed']).to be(true)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
RSpec.describe CovLoupe::Resolvers::CoverageLineResolver do
|
|
6
|
-
describe '#lookup_lines' do
|
|
7
|
-
context 'with direct path matching' do
|
|
8
|
-
it 'returns lines array for exact path match' do
|
|
9
|
-
abs_path = '/project/lib/foo.rb'
|
|
10
|
-
cov_data = {
|
|
11
|
-
abs_path => { 'lines' => [1, 0, nil, 2] }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
resolver = described_class.new(cov_data)
|
|
15
|
-
lines = resolver.lookup_lines(abs_path)
|
|
16
|
-
|
|
17
|
-
expect(lines).to eq([1, 0, nil, 2])
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it 'returns lines array when entry has lines directly' do
|
|
21
|
-
path = '/tmp/test.rb'
|
|
22
|
-
cov_data = {
|
|
23
|
-
path => { 'lines' => [1, 1, 1] }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
resolver = described_class.new(cov_data)
|
|
27
|
-
expect(resolver.lookup_lines(path)).to eq([1, 1, 1])
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context 'with CWD stripping fallback' do
|
|
32
|
-
it 'finds relative path when absolute path includes CWD' do
|
|
33
|
-
cwd = Dir.pwd
|
|
34
|
-
relative_path = 'lib/bar.rb'
|
|
35
|
-
abs_path = File.join(cwd, relative_path)
|
|
36
|
-
cov_data = {
|
|
37
|
-
relative_path => { 'lines' => [1, 0, 1] }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
resolver = described_class.new(cov_data)
|
|
41
|
-
lines = resolver.lookup_lines(abs_path)
|
|
42
|
-
|
|
43
|
-
expect(lines).to eq([1, 0, 1])
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'does not match when absolute path does not start with CWD' do
|
|
47
|
-
cov_data = {
|
|
48
|
-
'lib/baz.rb' => { 'lines' => [1, 1] }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
resolver = described_class.new(cov_data)
|
|
52
|
-
|
|
53
|
-
expect do
|
|
54
|
-
resolver.lookup_lines('/other/directory/lib/baz.rb')
|
|
55
|
-
end.to raise_error(CovLoupe::FileError, /No coverage entry found/)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
context 'when handling errors' do
|
|
60
|
-
it 'raises FileError when file is not found in coverage data' do
|
|
61
|
-
cov_data = {
|
|
62
|
-
'/project/lib/foo.rb' => { 'lines' => [1, 0] }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
resolver = described_class.new(cov_data)
|
|
66
|
-
|
|
67
|
-
expect do
|
|
68
|
-
resolver.lookup_lines('/project/lib/missing.rb')
|
|
69
|
-
end.to raise_error(CovLoupe::FileError, /No coverage entry found/)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
it 'raises FileError when coverage data is empty' do
|
|
73
|
-
resolver = described_class.new({})
|
|
74
|
-
|
|
75
|
-
expect do
|
|
76
|
-
resolver.lookup_lines('/any/path.rb')
|
|
77
|
-
end.to raise_error(CovLoupe::FileError, /No coverage entry found/)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'raises FileError when entry exists but has no lines or branches' do
|
|
81
|
-
cov_data = {
|
|
82
|
-
'/project/lib/foo.rb' => { 'other_key' => 'value' }
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
resolver = described_class.new(cov_data)
|
|
86
|
-
|
|
87
|
-
expect do
|
|
88
|
-
resolver.lookup_lines('/project/lib/foo.rb')
|
|
89
|
-
end.to raise_error(CovLoupe::FileError, /No coverage entry found/)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
context 'with branch-only coverage synthesis' do
|
|
94
|
-
it 'synthesizes line hits when only branch coverage exists' do
|
|
95
|
-
abs_path = '/tmp/branch_only.rb'
|
|
96
|
-
branch_cov = {
|
|
97
|
-
abs_path => {
|
|
98
|
-
'lines' => nil,
|
|
99
|
-
'branches' => {
|
|
100
|
-
'[:if, 0, 5, 2, 8, 5]' => {
|
|
101
|
-
'[:then, 1, 6, 4, 6, 15]' => 3,
|
|
102
|
-
'[:else, 2, 7, 4, 7, 15]' => 0
|
|
103
|
-
},
|
|
104
|
-
'[:case, 3, 12, 2, 17, 5]' => {
|
|
105
|
-
'[:when, 4, 13, 4, 13, 14]' => 0,
|
|
106
|
-
'[:when, 5, 14, 4, 14, 14]' => 2,
|
|
107
|
-
'[:else, 6, 16, 4, 16, 12]' => 2
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
resolver = described_class.new(branch_cov)
|
|
114
|
-
lines = resolver.lookup_lines(abs_path)
|
|
115
|
-
|
|
116
|
-
expect(lines[5]).to eq(3) # line 6
|
|
117
|
-
expect(lines[6]).to eq(0) # line 7
|
|
118
|
-
expect(lines[12]).to eq(0) # line 13
|
|
119
|
-
expect(lines[13]).to eq(2) # line 14
|
|
120
|
-
expect(lines[15]).to eq(2) # line 16
|
|
121
|
-
expect(lines.count { |v| !v.nil? }).to eq(5)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it 'aggregates hits for multiple branches on the same line' do
|
|
125
|
-
path = '/tmp/duplicated.rb'
|
|
126
|
-
branch_cov = {
|
|
127
|
-
path => {
|
|
128
|
-
'lines' => nil,
|
|
129
|
-
'branches' => {
|
|
130
|
-
'[:if, 0, 3, 2, 3, 12]' => {
|
|
131
|
-
'[:then, 1, 3, 2, 3, 12]' => 2,
|
|
132
|
-
'[:else, 2, 3, 2, 3, 12]' => 3
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
resolver = described_class.new(branch_cov)
|
|
139
|
-
lines = resolver.lookup_lines(path)
|
|
140
|
-
|
|
141
|
-
expect(lines[2]).to eq(5) # line 3 with summed hits
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
it 'handles array-style branch metadata' do
|
|
145
|
-
path = '/tmp/array_style.rb'
|
|
146
|
-
cov_data = {
|
|
147
|
-
path => {
|
|
148
|
-
'lines' => nil,
|
|
149
|
-
'branches' => {
|
|
150
|
-
[:if, 0, 5, 2, 8, 5] => {
|
|
151
|
-
[:then, 1, 6, 4, 6, 15] => 2
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
resolver = described_class.new(cov_data)
|
|
158
|
-
lines = resolver.lookup_lines(path)
|
|
159
|
-
|
|
160
|
-
expect(lines[5]).to eq(2) # line 6
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
it 'returns nil for entries with empty branches' do
|
|
164
|
-
path = '/tmp/empty_branches.rb'
|
|
165
|
-
cov_data = {
|
|
166
|
-
path => {
|
|
167
|
-
'lines' => nil,
|
|
168
|
-
'branches' => {}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
resolver = described_class.new(cov_data)
|
|
173
|
-
|
|
174
|
-
expect do
|
|
175
|
-
resolver.lookup_lines(path)
|
|
176
|
-
end.to raise_error(CovLoupe::FileError)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
it 'skips malformed branch entries' do
|
|
180
|
-
path = '/tmp/malformed.rb'
|
|
181
|
-
cov_data = {
|
|
182
|
-
path => {
|
|
183
|
-
'lines' => nil,
|
|
184
|
-
'branches' => {
|
|
185
|
-
'[:if, 0, 5, 2, 8, 5]' => {
|
|
186
|
-
'[:then, 1, 6, 4, 6, 15]' => 2
|
|
187
|
-
},
|
|
188
|
-
'malformed_key' => 'not_a_hash',
|
|
189
|
-
'[:if, 1, 10]' => { # missing elements in tuple
|
|
190
|
-
'[:then]' => 1 # also malformed
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
resolver = described_class.new(cov_data)
|
|
197
|
-
lines = resolver.lookup_lines(path)
|
|
198
|
-
|
|
199
|
-
# Should still get line 6 from the valid branch
|
|
200
|
-
expect(lines[5]).to eq(2)
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
context 'with extract_line_number edge cases' do
|
|
205
|
-
let(:resolver) { described_class.new({}) }
|
|
206
|
-
|
|
207
|
-
it 'extracts line number from array metadata' do
|
|
208
|
-
result = resolver.send(:extract_line_number, [:if, 0, 10, 2, 15, 5])
|
|
209
|
-
expect(result).to eq(10)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
it 'extracts line number from stringified array metadata' do
|
|
213
|
-
result = resolver.send(:extract_line_number, '[:if, 0, 15, 2, 20, 5]')
|
|
214
|
-
expect(result).to eq(15)
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
it 'returns nil for short array' do
|
|
218
|
-
result = resolver.send(:extract_line_number, [:if, 0])
|
|
219
|
-
expect(result).to be_nil
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
it 'returns nil for short string' do
|
|
223
|
-
result = resolver.send(:extract_line_number, '[:if, 0]')
|
|
224
|
-
expect(result).to be_nil
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
it 'returns nil for non-numeric line element in array' do
|
|
228
|
-
result = resolver.send(:extract_line_number, [:if, 0, 'not_a_number', 2])
|
|
229
|
-
expect(result).to be_nil
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
it 'returns nil for non-numeric line element in string' do
|
|
233
|
-
result = resolver.send(:extract_line_number, '[:if, 0, abc, 2]')
|
|
234
|
-
expect(result).to be_nil
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
it 'handles empty string' do
|
|
238
|
-
result = resolver.send(:extract_line_number, '')
|
|
239
|
-
expect(result).to be_nil
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
it 'handles nil input' do
|
|
243
|
-
result = resolver.send(:extract_line_number, nil)
|
|
244
|
-
expect(result).to be_nil
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# The rescue block catches ArgumentError/TypeError from malformed metadata
|
|
248
|
-
# that can't be converted to line numbers.
|
|
249
|
-
[ArgumentError, TypeError].each do |error_class|
|
|
250
|
-
it "returns nil when string operations raise #{error_class}" do
|
|
251
|
-
weird_object = Object.new
|
|
252
|
-
allow(weird_object).to receive(:to_s).and_raise(error_class, 'test error')
|
|
253
|
-
|
|
254
|
-
result = resolver.send(:extract_line_number, weird_object)
|
|
255
|
-
expect(result).to be_nil
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
context 'with preference for lines over branches' do
|
|
261
|
-
it 'prefers lines array when both lines and branches exist' do
|
|
262
|
-
path = '/tmp/both.rb'
|
|
263
|
-
cov_data = {
|
|
264
|
-
path => {
|
|
265
|
-
'lines' => [1, 2, 3],
|
|
266
|
-
'branches' => {
|
|
267
|
-
'[:if, 0, 100, 2, 105, 5]' => {
|
|
268
|
-
'[:then, 1, 101, 4, 101, 15]' => 99
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
resolver = described_class.new(cov_data)
|
|
275
|
-
lines = resolver.lookup_lines(path)
|
|
276
|
-
|
|
277
|
-
# Should return the lines array, not synthesized branch data
|
|
278
|
-
expect(lines).to eq([1, 2, 3])
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
end
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
require 'tmpdir'
|
|
5
|
-
|
|
6
|
-
RSpec.describe CovLoupe::Resolvers::ResolverFactory do
|
|
7
|
-
describe '.create_resultset_resolver' do
|
|
8
|
-
it 'uses provided candidates when present' do
|
|
9
|
-
custom_candidates = ['alt/.resultset.json']
|
|
10
|
-
resolver = described_class.create_resultset_resolver(
|
|
11
|
-
root: '/tmp/sample',
|
|
12
|
-
candidates: custom_candidates
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
expect(resolver).to be_a(CovLoupe::Resolvers::ResultsetPathResolver)
|
|
16
|
-
expect(resolver.instance_variable_get(:@root)).to eq('/tmp/sample')
|
|
17
|
-
expect(resolver.instance_variable_get(:@candidates)).to eq(custom_candidates)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it 'falls back to default candidates when none provided' do
|
|
21
|
-
resolver = described_class.create_resultset_resolver(root: '/tmp/sample')
|
|
22
|
-
|
|
23
|
-
expect(resolver.instance_variable_get(:@candidates)).to eq(
|
|
24
|
-
CovLoupe::Resolvers::ResultsetPathResolver::DEFAULT_CANDIDATES
|
|
25
|
-
)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
describe '.create_coverage_resolver' do
|
|
30
|
-
it 'wraps coverage data in a CoverageLineResolver' do
|
|
31
|
-
cov = { '/tmp/foo.rb' => { 'lines' => [1, 0] } }
|
|
32
|
-
resolver = described_class.create_coverage_resolver(cov)
|
|
33
|
-
|
|
34
|
-
expect(resolver).to be_a(CovLoupe::Resolvers::CoverageLineResolver)
|
|
35
|
-
expect(resolver.lookup_lines('/tmp/foo.rb')).to eq([1, 0])
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
describe '.find_resultset' do
|
|
40
|
-
it 'locates default resultset within the provided root' do
|
|
41
|
-
Dir.mktmpdir do |dir|
|
|
42
|
-
resultset_path = File.join(dir, '.resultset.json')
|
|
43
|
-
File.write(resultset_path, '{}')
|
|
44
|
-
|
|
45
|
-
resolved = described_class.find_resultset(dir)
|
|
46
|
-
|
|
47
|
-
expect(resolved).to eq(resultset_path)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
describe '.lookup_lines' do
|
|
53
|
-
it 'delegates to CoverageLineResolver for lookups' do
|
|
54
|
-
cov = { '/tmp/bar.rb' => { 'lines' => [0, 1] } }
|
|
55
|
-
|
|
56
|
-
expect(
|
|
57
|
-
described_class.lookup_lines(cov, '/tmp/bar.rb')
|
|
58
|
-
).to eq([0, 1])
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
require 'tmpdir'
|
|
5
|
-
require 'fileutils'
|
|
6
|
-
|
|
7
|
-
RSpec.describe CovLoupe::Resolvers::ResultsetPathResolver do
|
|
8
|
-
describe '#find_resultset' do
|
|
9
|
-
let(:root) { Dir.mktmpdir }
|
|
10
|
-
let(:resolver) { described_class.new(root: root) }
|
|
11
|
-
|
|
12
|
-
after do
|
|
13
|
-
FileUtils.remove_entry(root) if root && Dir.exist?(root)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
it 'raises when a specified resultset file cannot be found' do
|
|
17
|
-
expect do
|
|
18
|
-
resolver.find_resultset(resultset: 'missing.json')
|
|
19
|
-
end.to raise_error(RuntimeError, /Specified resultset not found/)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'raises when a specified directory does not contain .resultset.json' do
|
|
23
|
-
nested_dir = File.join(root, 'coverage')
|
|
24
|
-
Dir.mkdir(nested_dir)
|
|
25
|
-
|
|
26
|
-
expect do
|
|
27
|
-
resolver.find_resultset(resultset: nested_dir)
|
|
28
|
-
end.to raise_error(RuntimeError, /No .resultset.json found in directory/)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'returns the resolved path when a valid resultset file is provided' do
|
|
32
|
-
file = File.join(root, 'custom.json')
|
|
33
|
-
File.write(file, '{}')
|
|
34
|
-
|
|
35
|
-
expect(resolver.find_resultset(resultset: file)).to eq(file)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it 'raises a helpful error when no fallback candidates are found' do
|
|
39
|
-
expect do
|
|
40
|
-
resolver.find_resultset
|
|
41
|
-
end.to raise_error(RuntimeError, /Could not find .resultset.json/)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'accepts a resultset path already nested under the provided root without double-prefixing' do
|
|
45
|
-
project_root = (FIXTURES_DIR / 'project1').to_s
|
|
46
|
-
resolver = described_class.new(root: project_root)
|
|
47
|
-
|
|
48
|
-
resolved = resolver.find_resultset(resultset: 'spec/fixtures/project1/coverage')
|
|
49
|
-
|
|
50
|
-
expect(resolved).to eq(File.join(project_root, 'coverage', '.resultset.json'))
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# In non-strict mode, resolve_candidate returns nil instead of raising
|
|
54
|
-
# when the path doesn't exist, allowing fallback resolution to continue.
|
|
55
|
-
it 'returns nil for non-existent path in non-strict mode' do
|
|
56
|
-
result = resolver.send(:resolve_candidate, '/nonexistent/path.json', strict: false)
|
|
57
|
-
expect(result).to be_nil
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|