Skip to content

Success Predicate Examples

Coverage Validation Predicate Examples

Back to main README

This directory contains example coverage validation predicates for use with the validate subcommand.

⚠️ SECURITY WARNING

Success predicates execute as arbitrary Ruby code with full system privileges. They have unrestricted access to: - File system operations (read, write, delete) - Network operations (HTTP requests, sockets) - System commands (via backticks, system(), exec(), etc.) - Environment variables and sensitive data

Only use predicate files from trusted sources. Treat them like any other executable code in your project. - Never use predicates from untrusted or unknown sources - Review predicates before use, especially in CI/CD environments - Store predicates in version control with code review - Be cautious when copying examples from the internet

Usage

File mode:

cov-loupe validate examples/success_predicates/<filename>.rb

String mode:

cov-loupe validate -i '->(model) { model.list["files"].all? { |f| f["percentage"] >= 80 } }'

The predicate receives a CoverageModel instance and returns: - Truthy value → Exit code 0 (success) - Falsy value → Exit code 1 (failure) - Exception → Exit code 2 (error)

Available Examples

all_files_above_threshold_predicate.rb

All files must have >= 80% coverage.

cov-loupe validate examples/success_predicates/all_files_above_threshold_predicate.rb

project_coverage_minimum_predicate.rb

Total project coverage must be >= 85%.

cov-loupe validate examples/success_predicates/project_coverage_minimum_predicate.rb

directory_specific_thresholds_predicate.rb

Different thresholds for different directories: - lib/api/ - 90% required - lib/core/ - 85% required - lib/legacy/ - 60% required

cov-loupe validate examples/success_predicates/directory_specific_thresholds_predicate.rb

Creating Custom Predicates

A predicate must be a callable object (lambda, proc, or class with #call method):

Lambda example:

->(model) do
  model.list['files'].all? { |f| f['percentage'] >= 80 }
end

Class method example:

class MyPolicy
  def self.call(model)
    model.list['files'].all? { |f| f['percentage'] >= @threshold }
  end
end

MyPolicy  # The class itself

Instance method example:

class MyPolicy
  def initialize(threshold = 80)
    @threshold = threshold
  end

  def call(model)
    model.list['files'].all? { |f| f['percentage'] >= @threshold }
  end
end

MyPolicy

CoverageModel API

The model parameter provides:

# Get all files
files = model.list['files']
# => [{ "file" => "...", "covered" => 12, "total" => 14, "percentage" => 85.71, "stale" => false }, ...]

# Filter by globs
api_files = model.list(tracked_globs: ['lib/api/**/*.rb'])['files']

# Get specific file data
summary = model.summary_for('lib/model.rb')
uncovered = model.uncovered_for('lib/model.rb')

See docs/user/LIBRARY_API.md for the complete API.

When to Use Standalone Scripts Instead

For more complex scenarios, you may prefer writing a standalone Ruby script that uses the cov-loupe library directly instead of using the validate subcommand.

Use a standalone script when: - External dependencies needed - Your policy requires other gems or libraries - Complex setup required - You need custom logging, database connections, or API calls - Easier testing desired - Standalone scripts can be tested like any Ruby code - More explicit control - You want full control over model initialization and error handling - Sophisticated logic - Multi-step analysis, data aggregation, or integration with other tools

Predicate approach is better when: - Policy is simple and self-contained (most common use case) - You want the tool to handle option parsing (--resultset, --raise-on-stale, etc.) - Consistent error handling and exit codes are desired - Less boilerplate is preferred

Example standalone script:

#!/usr/bin/env ruby
require 'cov_loupe'
require 'httparty'  # External gem

# Custom initialization
model = CovLoupe::CoverageModel.new(
  resultset: ENV['COVERAGE_PATH'],
  raise_on_stale: true
)

# Complex logic with external API
files = model.list['files']
low_coverage_files = files.select { |f| f['percentage'] < 80 }

# Post to Slack
if low_coverage_files.any?
  HTTParty.post(
    ENV['SLACK_WEBHOOK_URL'],
    body: { text: "#{low_coverage_files.size} files below 80%" }.to_json
  )
  exit 1
else
  exit 0
end

Both approaches execute arbitrary code with full system privileges, so the security considerations are identical. Choose the approach that best fits your use case.

CI/CD Integration

GitHub Actions:

- name: Enforce Coverage Policy
  run: bundle exec cov-loupe validate coverage_policy.rb

GitLab CI:

coverage:enforce:
  script:
    - bundle exec cov-loupe validate coverage_policy.rb

Jenkins:

stage('Coverage Policy') {
    steps {
        sh 'bundle exec cov-loupe validate coverage_policy.rb'
    }
}

Exit Codes

  • 0 - Predicate returned truthy (success)
  • 1 - Predicate returned falsy (failure)
  • 2 - Predicate raised an error

Use exit code 1 to fail CI/CD builds when coverage doesn't meet policy.