mirror of
https://github.com/inspec/inspec
synced 2024-11-26 14:40:26 +00:00
Basic template, not parameterized
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
677542de02
commit
4b9fd1df79
20 changed files with 709 additions and 3 deletions
|
@ -8,7 +8,9 @@ module InspecPlugins
|
|||
class CLI < Inspec.plugin(2, :cli_command)
|
||||
subcommand_desc 'init SUBCOMMAND', 'Generate InSpec code'
|
||||
|
||||
# require_relative 'cli_profile'
|
||||
TEMPLATES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'templates'))
|
||||
|
||||
require_relative 'cli_profile'
|
||||
require_relative 'cli_plugin'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'pathname'
|
||||
require_relative 'renderer'
|
||||
require 'byebug'
|
||||
|
||||
module InspecPlugins
|
||||
module Init
|
||||
|
@ -17,6 +17,27 @@ module InspecPlugins
|
|||
ui.error("Plugin names must begin with either " + ui.emphasis('inspec') + ' or ' + ui.emphasis('train') + ' - saw ' + ui.emphasis(plugin_name))
|
||||
ui.exit(:usage_error)
|
||||
end
|
||||
|
||||
plugin_type = plugin_type[1]
|
||||
unless plugin_type == 'inspec'
|
||||
ui.error('Sorry, only inspec plugins are supported at this time: train support is not implemented yet.')
|
||||
ui.exit(:usage_error)
|
||||
end
|
||||
|
||||
template_path = File.join('plugins', plugin_type + '-plugin-template')
|
||||
|
||||
render_opts = {
|
||||
templates_path: TEMPLATES_PATH,
|
||||
overwrite: options[:overwrite],
|
||||
}
|
||||
renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
|
||||
|
||||
vars = {
|
||||
name: plugin_name,
|
||||
camel_case: plugin_name.sub(/^(inspec|train)\-/, '').split('-').map(&:capitalize).join(''),
|
||||
snake_case: plugin_name.tr('-', '_')
|
||||
}
|
||||
renderer.render_with_values(template_path, plugin_type + ' plugin', vars)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,6 @@ module InspecPlugins
|
|||
module Init
|
||||
class CLI < Inspec.plugin(2, :cli_command)
|
||||
|
||||
TEMPLATES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'templates'))
|
||||
|
||||
#-------------------------------------------------------------------#
|
||||
# inspec init profile
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# encoding: utf-8
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
group :development do
|
||||
gem 'bundler'
|
||||
gem 'byebug'
|
||||
gem 'minitest'
|
||||
gem 'rake'
|
||||
gem 'rubocop', '= 0.49.1' # Need to keep in sync with main InSpec project, so config files will work
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (c) 2018 Chef Software Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,62 @@
|
|||
# InSpec Plugin Example - Resource Lister
|
||||
|
||||
This plugin provides an example of building a plugin for use with [InSpec](https://inspec.io). Its functionality is simple, but useful: list resources included with InSpec.
|
||||
|
||||
## To Install this as a User
|
||||
|
||||
You will need InSpec v2.3 or later.
|
||||
|
||||
If you want to just use this (not learn how to write a plugin), you can do so by simply running:
|
||||
|
||||
```
|
||||
you@machine $ inspec plugin install inspec-resource-lister
|
||||
```
|
||||
|
||||
You can then run:
|
||||
|
||||
```
|
||||
you@machine $ inspec plugin help listresources
|
||||
# ... Usage info
|
||||
|
||||
you@machine $ inspec plugin listresources core
|
||||
aide_conf
|
||||
apache
|
||||
apache_conf
|
||||
... snip ...
|
||||
yumrepo
|
||||
zfs_dataset
|
||||
zfs_pool
|
||||
------------------------------
|
||||
160 resources total
|
||||
```
|
||||
|
||||
## Features of This Example Kit
|
||||
|
||||
This example plugin is a full-fledged plugin example, with everything a real-world, industrial grade plugin would have, including:
|
||||
|
||||
* an implementation of an InSpec CLI Command, using the InSpec PluginV2 API
|
||||
* documentation (you are reading it now)
|
||||
* tests, at the unit and functional level
|
||||
* a .gemspec, for packaging and publishing it as a gem
|
||||
* a Gemfile, for managing its dependencies
|
||||
* a Rakefile, for running development tasks
|
||||
* Rubocop linting support for using the base InSpec project rubocop.yml (See Rakefile)
|
||||
|
||||
You are encouraged to use this plugin as a starting point for real plugins.
|
||||
|
||||
## Development of a Plugin
|
||||
|
||||
[Plugin Development](https://github.com/inspec/inspec/blob/master/docs/dev/plugins.md) is documented on the `inspec` project on GitHub. Additionally, this example
|
||||
plugin has extensive comments explaining what is happening, and why.
|
||||
|
||||
### A Tour of the Plugin
|
||||
|
||||
One nice circuit of the plugin might be:
|
||||
* look at the gemspec, to see what the plugin thinks it does
|
||||
* look at the functional tests, to see the plugin proving it does what it says
|
||||
* look at the unit tests, to see how the plugin claims it is internally structured
|
||||
* look at the Rakefile, to see how to interact with the project
|
||||
* look at lib/inspec-resource-lister.rb, the entry point which InSpec will always load if the plugin is installed
|
||||
* look at lib/inspec-resource-lister/plugin.rb, the plugin definition which InSpec uses to understand what the plugin _can_ do.
|
||||
* look at lib/inspec-resource-lister/cli_command.rb, the CLI Command implementation itself.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# A Rakefile defines tasks to help maintain your project.
|
||||
# Rake provides several task templates that are useful.
|
||||
|
||||
#------------------------------------------------------------------#
|
||||
# Test Runner Tasks
|
||||
#------------------------------------------------------------------#
|
||||
|
||||
# This task template will make a task named 'test', and run
|
||||
# the tests that it finds.
|
||||
require 'rake/testtask'
|
||||
|
||||
Rake::TestTask.new do |t|
|
||||
t.libs.push 'lib'
|
||||
t.test_files = FileList[
|
||||
'test/unit/*_test.rb',
|
||||
'test/functional/*_test.rb',
|
||||
]
|
||||
t.verbose = true
|
||||
# Ideally, we'd run tests with warnings enabled,
|
||||
# but the dependent gems have many warnings. As this
|
||||
# is an example, let's disable them so the testing
|
||||
# experience is cleaner.
|
||||
t.warning = false
|
||||
end
|
||||
|
||||
#------------------------------------------------------------------#
|
||||
# Code Style Tasks
|
||||
#------------------------------------------------------------------#
|
||||
require 'rubocop/rake_task'
|
||||
|
||||
RuboCop::RakeTask.new(:lint) do |t|
|
||||
# Choices of rubocop rules to enforce are deeply personal.
|
||||
# Here, we set things up so that your plugin will use the Bundler-installed
|
||||
# inspec gem's copy of the InSpec project's rubocop.yml file (which
|
||||
# is indeed packaged with the inspec gem).
|
||||
require 'inspec/globals'
|
||||
inspec_rubocop_yml = File.join(Inspec.src_root, '.rubocop.yml')
|
||||
|
||||
t.options = ['--display-cop-names', '--config', inspec_rubocop_yml]
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
# coding: utf-8
|
||||
|
||||
# As plugins are usually packaged and distributed as a RubyGem,
|
||||
# we have to provide a .gemspec file, which controls the gembuild
|
||||
# and publish process. This is a fairly generic gemspec.
|
||||
|
||||
# It is traditional in a gemspec to dynamically load the current version
|
||||
# from a file in the source tree. The next three lines make that happen.
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'inspec-resource-lister/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
# Importantly, all InSpec plugins must be prefixed with `inspec-` (most
|
||||
# plugins) or `train-` (plugins which add new connectivity features).
|
||||
spec.name = 'inspec-resource-lister'
|
||||
|
||||
# It is polite to namespace your plugin under InspecPlugins::YourPluginInCamelCase
|
||||
spec.version = InspecPlugins::ResourceLister::VERSION
|
||||
spec.authors = ['Clinton Wolfe']
|
||||
spec.email = ['cwolfe@chef.io']
|
||||
spec.summary = 'InSpec Plugin example, lists available resources'
|
||||
spec.description = 'Example for implementing an InSpec Plugin. This simply lists available resources.'
|
||||
spec.homepage = 'https://github.com/inspec/inspec/tree/master/examples/plugin'
|
||||
spec.license = 'Apache-2.0'
|
||||
|
||||
# Though complicated-looking, this is pretty standard for a gemspec.
|
||||
# It just filters what will actually be packaged in the gem (leaving
|
||||
# out tests, etc)
|
||||
spec.files = %w{
|
||||
README.md inspec-resource-lister.gemspec Gemfile
|
||||
} + Dir.glob(
|
||||
'lib/**/*', File::FNM_DOTMATCH
|
||||
).reject { |f| File.directory?(f) }
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
# If you rely on any other gems, list them here with any constraints.
|
||||
# This is how `inspec plugin install` is able to manage your dependencies.
|
||||
# For example, perhaps you are writing a thing that talks to AWS, and you
|
||||
# want to ensure you have `aws-sdk` in a certain version.
|
||||
|
||||
# All plugins should mention inspec, > 2.2.78
|
||||
# 2.2.78 included the v2 Plugin API
|
||||
spec.add_dependency 'inspec', '>=2.2.78', '<4.0.0'
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# encoding: utf-8
|
||||
|
||||
# This file is known as the "entry point."
|
||||
# This is the file InSpec will try to load if it
|
||||
# thinks your plugin is installed.
|
||||
|
||||
# The *only* thing this file should do is setup the
|
||||
# load path, then load the plugin definition file.
|
||||
|
||||
# Next two lines simply add the path of the gem to the load path.
|
||||
# This is not needed when being loaded as a gem; but when doing
|
||||
# plugin development, you may need it. Either way, it's harmless.
|
||||
libdir = File.dirname(__FILE__)
|
||||
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
||||
|
||||
require 'inspec-resource-lister/plugin'
|
|
@ -0,0 +1,70 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'inspec/resource'
|
||||
|
||||
module InspecPlugins::ResourceLister
|
||||
# This class will provide the actual CLI implementation.
|
||||
# Its superclass is provided by another call to Inspec.plugin,
|
||||
# this time with two args. The first arg specifies we are requesting
|
||||
# version 2 of the Plugins API. The second says we are making a CLI
|
||||
# Command plugin component, so please make available any DSL needed
|
||||
# for that.
|
||||
# In fact, aside from a some housekeeping DSL methods, most of the
|
||||
# DSL provided is that of Thor. Inspec.plugin(2, :cli_command)
|
||||
# promises to return a class that is a subclass of Thor. So, to add
|
||||
# commands, usage information, and options, use the Thor documentation.
|
||||
class CliCommand < Inspec.plugin(2, :cli_command)
|
||||
# This isn't provided by Thor, but is needed by InSpec so that it can
|
||||
# register the subcommand. Args are a usage message, and a short decription.
|
||||
# These will appear when someone has installed the plugin, and then they
|
||||
# run `inspec help`.
|
||||
subcommand_desc 'list-resources [COMMAND]', 'List resources that InSpec finds.'
|
||||
|
||||
# The usual rhythm for a Thor CLI file is description, options, command method.
|
||||
# Thor just has you call DSL methods in sequence prior to each command.
|
||||
# Let's make a command, 'core', that lists all of the resources included with InSpec.
|
||||
|
||||
# First, provide a usage / description. This will appear in `inspec help list-resources`.
|
||||
desc 'core [OPTIONS]', 'List resources that are included with InSpec.'
|
||||
|
||||
# Let's include an option, -s, to summarize the list.
|
||||
# Refer to the Thors docs; there is a lot you can do here.
|
||||
option :summary, desc: 'Include a total at the bottom', \
|
||||
type: :boolean, default: true, aliases: [:s]
|
||||
|
||||
# OK, now the actual method itself. If you provide params, you're telling Thor that
|
||||
# you accept CLI arguments after all options have been consumed. Let's accept a
|
||||
# pattern, assumed to be a wildcard substring. If we provide a default, the CLI arg becomes optional.
|
||||
def core(pattern = /.+/)
|
||||
# The code here will *only* be executed if someone actually runs
|
||||
# `inspec list-resources core`. So, we can lazily wait to load
|
||||
# expensive things here. However, InSpec has in fact already
|
||||
# loaded the Resources, so we don't have anything to load.
|
||||
|
||||
# If we were passed a CLI arg, wrap the arg in Regexp matchers so
|
||||
# we will match anywhere in the name.
|
||||
unless pattern == /.+/
|
||||
pattern = Regexp.new('.*' + pattern + '.*')
|
||||
end
|
||||
|
||||
# This gets a bit into InSpec innards; but this is simply a Hash.
|
||||
registry = Inspec::Resource.default_registry
|
||||
resource_names = registry.keys.grep(pattern).sort
|
||||
|
||||
# One day we'll have nice UI support.
|
||||
resource_names.each { |name| puts name }
|
||||
|
||||
if options[:summary]
|
||||
puts '-' * 30
|
||||
puts "#{resource_names.count} resources total"
|
||||
end
|
||||
end
|
||||
|
||||
# A neat idea for future work would be to add another command, perhaps
|
||||
# 'resource-pack', which examines a possibly remote resource pack and
|
||||
# enumerates the resources it defines.
|
||||
|
||||
# Another idea might be to fetch a profile, and list the resources actually
|
||||
# used in the controls of the profile, along with counts.
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
# Plugin Definition file
|
||||
# The purpose of this file is to declare to InSpec what plugin_types (capabilities)
|
||||
# are included in this plugin, and provide hooks that will load them as needed.
|
||||
|
||||
# It is important that this file load successfully and *quickly*.
|
||||
# Your plugin's functionality may never be used on this InSpec run; so we keep things
|
||||
# fast and light by only loading heavy things when they are needed.
|
||||
|
||||
# Presumably this is light
|
||||
require 'inspec-resource-lister/version'
|
||||
|
||||
# The InspecPlugins namespace is where all plugins should declare themselves.
|
||||
# The 'Inspec' capitalization is used throughout the InSpec source code; yes, it's
|
||||
# strange.
|
||||
module InspecPlugins
|
||||
# Pick a reasonable namespace here for your plugin. A reasonable choice
|
||||
# would be the CamelCase version of your plugin gem name.
|
||||
# inspec-resource-lister => ResourceLister
|
||||
module ResourceLister
|
||||
# This simple class handles the plugin definition, so calling it simply Plugin is OK.
|
||||
# Inspec.plugin returns various Classes, intended to be superclasses for various
|
||||
# plugin components. Here, the one-arg form gives you the Plugin Definition superclass,
|
||||
# which mainly gives you access to the hook / plugin_type DSL.
|
||||
# The number '2' says you are asking for version 2 of the plugin API. If there are
|
||||
# future versions, InSpec promises plugin API v2 will work for at least two more InSpec
|
||||
# major versions.
|
||||
class Plugin < ::Inspec.plugin(2)
|
||||
# Internal machine name of the plugin. InSpec will use this in errors, etc.
|
||||
plugin_name :'inspec-resource-lister'
|
||||
|
||||
# Define a new CLI subcommand.
|
||||
# The argument here will be used to match against the command line args,
|
||||
# and if the user said `inspec list-resources`, this hook will get called.
|
||||
# Notice that you can define multiple hooks with different names, and they
|
||||
# don't have to match the plugin name.
|
||||
|
||||
# We'd like this to be list-resources, but Thor does not support hyphens
|
||||
# see https://github.com/erikhuda/thor/pull/613
|
||||
cli_command :listresources do
|
||||
# Calling this hook doesn't mean list-resources is being executed - just
|
||||
# that we should be ready to do so. So, load the file that defines the
|
||||
# functionality.
|
||||
# For example, InSpec will activate this hook when `inspec help` is
|
||||
# executed, so that this plugin's usage message will be included in the help.
|
||||
require 'inspec-resource-lister/cli_command'
|
||||
|
||||
# Having loaded our functionality, return a class that will let the
|
||||
# CLI engine tap into it.
|
||||
InspecPlugins::ResourceLister::CliCommand
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
# This file simply makes it easier for CI engines to update
|
||||
# the version stamp, and provide a clean way for the gemspec
|
||||
# to learn the current version.
|
||||
module InspecPlugins
|
||||
module ResourceLister
|
||||
VERSION = '0.1.0'.freeze
|
||||
end
|
||||
end
|
24
lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/fixtures/README.md
vendored
Normal file
24
lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/fixtures/README.md
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Test Fixtures Area
|
||||
|
||||
In this directory, you would place things that you need during testing. For example, if you were making a plugin that counts the number of controls in a profile, you might have a directory tree like this:
|
||||
|
||||
```
|
||||
fixtures/
|
||||
profiles/
|
||||
zero-controls/
|
||||
inspec.yml
|
||||
controls/
|
||||
twelve-controls/
|
||||
inspec.yml
|
||||
controls/
|
||||
nine.rb
|
||||
three.rb
|
||||
```
|
||||
|
||||
When writing your functional tests, you can point InSpec at the various test fixture profiles, and know that when it points at the zero-controls profile, it should find no controls; and when pointed at the twelve-controls profile, it should find 12.
|
||||
|
||||
## Using test fixtures provided with core inspec
|
||||
|
||||
InSpec itself ships with many test fixtures - not just profiles, but attribute files, configuration directories, and more. Examine them at [the fixtures directory](https://github.com/inspec/inspec/tree/master/test/unit/mock)
|
||||
|
||||
To use them, see the helper.rb file included in the example at test/helper.rb .
|
|
@ -0,0 +1,18 @@
|
|||
# Functional Testing Area for Example Plugins
|
||||
|
||||
## What example tests are provided?
|
||||
|
||||
Here, since this is a CliCommand plugin, we provide one set of functional tests:
|
||||
|
||||
* inspec_resource_lister_test.rb - Runs `inspec resource-lister` in several circumstances, and verifies the output from the process.
|
||||
|
||||
## What are functional tests?
|
||||
|
||||
Functional tests are tests that verify that your plugin works _as would be seen by a user_. Functional tests generally do not have inside knowledge about the inner workings of the plugin. However a functional test is very interested in changes that you plugin make to the outside world: exit codes, command output, changes to files on the filesystem, etc.
|
||||
|
||||
To be picked up by the Rake tasks as tests, each test file should end in `_test.rb`.
|
||||
|
||||
## Unit vs Functional Tests
|
||||
|
||||
A practical difference between unit tests and functional tests is that unit tests all run within one process, while functional tests might exercise a CLI plugin by shelling out to an inspec command in a subprocess, and examining the results.
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
# Functional Tests for Example Plugin, Resource Lister
|
||||
|
||||
# Functional tests are used to verify the behavior of the plugin are as expected, to a user.
|
||||
# Functional tests generally do not have inside knowledge of how the plugin works.
|
||||
|
||||
# Include our test harness
|
||||
require_relative '../helper'
|
||||
|
||||
# Because InSpec is a Spec-style test suite, we're going to use MiniTest::Spec
|
||||
# here, for familiar look and feel. However, this isn't InSpec (or RSpec) code.
|
||||
describe 'inspec list-resources core' do
|
||||
# Our helper.rb locates this library from the InSpec install that
|
||||
# Bundler installed for us. If we want its methods, we still must
|
||||
# import it. Including it here will make it available in all child
|
||||
# 'describe' blocks.
|
||||
include CorePluginFunctionalHelper
|
||||
|
||||
# When thinking up scenarios to test, start with the simplest.
|
||||
# Then think of each major feature, and exercise them.
|
||||
# Running combinations of features makes sense if it is very likely,
|
||||
# or a difficult / dangerous case. You can always add more tests
|
||||
# here as users find subtle problems. In fact, having a user submit
|
||||
# a PR that creates a failing functional test is a great way to
|
||||
# capture the reproduction case.
|
||||
# The simplest case:
|
||||
describe "when run without an argument" do
|
||||
|
||||
# run_inspec_process_with_this_plugin is a helper provided by
|
||||
# CoreFunctionalHelper. It makes the InSpec that Bundler installed
|
||||
# think that this plugin we are currently testing is installed as a
|
||||
# user plugin, by writing a plugin config file in a temp dir.
|
||||
# To use it, just provide a command line, minus the word `inspec`.
|
||||
let (:outcome) { run_inspec_process_with_this_plugin('listresources core') }
|
||||
|
||||
# Some tests through here use minitest Expectations, which attach to all
|
||||
# Objects, and begin with 'must' (positive) or 'wont' (negative)
|
||||
# See https://ruby-doc.org/stdlib-2.1.0/libdoc/minitest/rdoc/MiniTest/Expectations.html
|
||||
it("should exit successfully") { outcome.exit_status.must_equal(0) }
|
||||
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
||||
|
||||
# A selection of core resources, just spot checking.
|
||||
# This is an example of using Ruby to define sets of tests.
|
||||
['process', 'service', 'user', 'file'].each do |resource_name|
|
||||
it "should mention the '#{resource_name}' resource" do
|
||||
outcome.stdout.must_include(resource_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Check for the summary
|
||||
it "should mention the summary" do
|
||||
outcome.stdout.must_include('resources total')
|
||||
end
|
||||
end
|
||||
|
||||
# Test the search pattern feature, in a couple of ways.
|
||||
describe "when run with a search pattern that matches things" do
|
||||
# Notice that the command line is changed here:
|
||||
# "list all resources that have the word user in them"
|
||||
let (:outcome) { run_inspec_process_with_this_plugin('listresources core user') }
|
||||
|
||||
# Should be well-behaved...
|
||||
it("should exit successfully") { outcome.exit_status.must_equal(0) }
|
||||
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
||||
|
||||
# Here, we want to know it DID match some things, and NOT some others.
|
||||
['user', 'users'].each do |resource_name|
|
||||
it "should mention the '#{resource_name}' resource" do
|
||||
outcome.stdout.must_include(resource_name)
|
||||
end
|
||||
end
|
||||
['process', 'service', 'file'].each do |resource_name|
|
||||
it "should NOT mention the '#{resource_name}' resource" do
|
||||
outcome.stdout.wont_include(resource_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
describe "when run with a search pattern that matches nothing" do
|
||||
# Unlikely we'll have a resource with the string 'autogyro' in it.
|
||||
let (:outcome) { run_inspec_process_with_this_plugin('listresources core autogyro') }
|
||||
|
||||
# Should be well-behaved...
|
||||
it("should exit successfully") { outcome.exit_status.must_equal(0) }
|
||||
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
||||
|
||||
# Output lines should be just two, for the summary.
|
||||
it "should only have two output lines" do
|
||||
outcome.stdout.split("\n").count.must_equal(2)
|
||||
end
|
||||
|
||||
# Check for the summary
|
||||
it "should mention a zero-resource summary" do
|
||||
outcome.stdout.must_include('0 resources total')
|
||||
end
|
||||
end
|
||||
|
||||
# Exercise the summary option, which defaults to 'true'.
|
||||
describe "when run with the no-summary flag" do
|
||||
# Alter the command string to include the no-summary option
|
||||
let(:outcome) { run_inspec_process_with_this_plugin('listresources core --no-summary') }
|
||||
|
||||
# Should be well-behaved...
|
||||
it("should exit successfully") { outcome.exit_status.must_equal(0) }
|
||||
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
||||
|
||||
# Check for the summary
|
||||
it "should NOT mention summary" do
|
||||
outcome.stdout.wont_include('0 resources total')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# Test helper file for example plugins
|
||||
|
||||
# This file's job is to collect any libraries needed for testing, as well as provide
|
||||
# any utilities to make testing a plugin easier.
|
||||
|
||||
# InSpec core provides a number of such libraries and facilities, in the file
|
||||
# lib/pligins/shared/core_plugin_test_helper.rb . So, one job in this file is
|
||||
# to locate and load that file.
|
||||
require 'inspec/../plugins/shared/core_plugin_test_helper'
|
||||
|
||||
# Also load the InSpec plugin system. We need this so we can unit-test the plugin
|
||||
# classes, which will rely on the plugin system.
|
||||
require 'inspec/plugin/v2'
|
||||
|
||||
# Caution: loading all of InSpec (i.e. require 'inspec') may cause interference with
|
||||
# minitest/spec; one symptom would be appearing to have no tests.
|
||||
# See https://github.com/inspec/inspec/issues/3380
|
||||
|
||||
# You can select from a number of test harnesses. Since InSpec uses Spec-style controls
|
||||
# in profile code, you will probably want to use something like minitest/spec, which provides
|
||||
# Spec-style tests.
|
||||
require 'minitest/spec'
|
||||
require 'minitest/autorun'
|
||||
|
||||
# You might want to put some debugging tools here. We run tests to find bugs, after all.
|
||||
require 'byebug'
|
|
@ -0,0 +1,17 @@
|
|||
# Unit Testing Area for Example Plugins
|
||||
|
||||
## What Example Tests are Provided?
|
||||
|
||||
Here, since this is a CliCommand plugin, we provide two sets of unit tests:
|
||||
|
||||
* plugin_def_test.rb - Would be useful in any plugin. Verifies that the plugin is properly detected and registered.
|
||||
* cli_args_test.rb - Verifies that the expected commands are present, and that they have the expected options and args.
|
||||
|
||||
## What are Unit Tests?
|
||||
|
||||
Unit tests are tests that verify that the individual components of your plugin work as intended. To be picked up by the Rake tasks as tests, each test file should end in `_test.rb`.
|
||||
|
||||
## Unit vs Functional Tests
|
||||
|
||||
A practical difference between unit tests and functional tests is that unit tests all run within one process, while functional tests might exercise a CLI plugin by shelling out to an inspec command in a subprocess, and examining the results.
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# This unit test performs some tests to verify that the command line options for
|
||||
# inspec-resource-lister are correct.
|
||||
|
||||
# Include our test harness
|
||||
require_relative '../helper'
|
||||
|
||||
# Load the class under test, the CliCommand definition.
|
||||
require 'inspec-resource-lister/cli_command'
|
||||
|
||||
# Because InSpec is a Spec-style test suite, we're going to use MiniTest::Spec
|
||||
# here, for familiar look and feel. However, this isn't InSpec (or RSpec) code.
|
||||
describe InspecPlugins::ResourceLister::CliCommand do
|
||||
|
||||
# When writing tests, you can use `let` to create variables that you
|
||||
# can reference easily.
|
||||
|
||||
# This is the CLI Command implementation class.
|
||||
# It is a subclass of Thor, which is a CLI framework.
|
||||
# This unit test file is mostly about verifying the Thor settings.
|
||||
let(:cli_class) { InspecPlugins::ResourceLister::CliCommand }
|
||||
|
||||
# This is a Hash of Structs that tells us details of options for the 'core' subcommand.
|
||||
let(:core_options) { cli_class.all_commands['core'].options }
|
||||
|
||||
# To group tests together, you can nest 'describe' in minitest/spec
|
||||
# (that is discouraged in InSpec control code.)
|
||||
describe 'the core command' do
|
||||
|
||||
# Some tests through here use minitest Expectations, which attach to all
|
||||
# Objects, and begin with 'must' (positive) or 'wont' (negative)
|
||||
# See https://ruby-doc.org/stdlib-2.1.0/libdoc/minitest/rdoc/MiniTest/Expectations.html
|
||||
|
||||
# Option count OK?
|
||||
it "should take one option" do
|
||||
core_options.count.must_equal(1)
|
||||
end
|
||||
|
||||
# Summary option
|
||||
describe "the summary option" do
|
||||
it "should be present" do
|
||||
core_options.keys.must_include(:summary)
|
||||
end
|
||||
it "should have a description" do
|
||||
core_options[:summary].description.wont_be_nil
|
||||
end
|
||||
it "should not be required" do
|
||||
core_options[:summary].required.wont_equal(true)
|
||||
end
|
||||
it "should have a single-letter alias" do
|
||||
core_options[:summary].aliases.must_include(:s)
|
||||
end
|
||||
end
|
||||
|
||||
# Argument count
|
||||
# The 'core' command takes one optional argument. According to the
|
||||
# metaprogramming rules of Ruby, the core() method should thus have an
|
||||
# arity of -1. See http://ruby-doc.org/core-2.5.1/Method.html#method-i-arity
|
||||
# for how that number is caclulated.
|
||||
it "should take one optional argument" do
|
||||
cli_class.instance_method(:core).arity.must_equal(-1)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# This unit test performs some tests to verify that
|
||||
# the inspec-resource-lister plugin is configured correctly.
|
||||
|
||||
# Include our test harness
|
||||
require_relative '../helper'
|
||||
|
||||
# Load the class under test, the Plugin definition.
|
||||
require 'inspec-resource-lister/plugin'
|
||||
|
||||
# Because InSpec is a Spec-style test suite, we're going to use MiniTest::Spec
|
||||
# here, for familiar look and feel. However, this isn't InSpec (or RSpec) code.
|
||||
|
||||
describe InspecPlugins::ResourceLister::Plugin do
|
||||
|
||||
# When writing tests, you can use `let` to create variables that you
|
||||
# can reference easily.
|
||||
|
||||
# Internally, plugins are always known by a Symbol name. Convert here.
|
||||
let(:plugin_name) { :'inspec-resource-lister' }
|
||||
|
||||
# The Registry knows about all plugins that ship with InSpec by
|
||||
# default, as well as any that are installed by the user. When a
|
||||
# plugin definition is loaded, it will also self-register.
|
||||
let(:registry) { Inspec::Plugin::V2::Registry.instance }
|
||||
|
||||
# The plugin status record tells us what the Registry knows.
|
||||
# Note that you can use previously-defined 'let's.
|
||||
let(:status) { registry[plugin_name] }
|
||||
|
||||
# OK, actual tests!
|
||||
|
||||
# Does the Registry know about us at all?
|
||||
it "should be registered" do
|
||||
registry.known_plugin?(plugin_name)
|
||||
end
|
||||
|
||||
# Some tests through here use minitest Expectations, which attach to all
|
||||
# Objects, and begin with 'must' (positive) or 'wont' (negative)
|
||||
# See https://ruby-doc.org/stdlib-2.1.0/libdoc/minitest/rdoc/MiniTest/Expectations.html
|
||||
|
||||
# The plugin system had an undocumented v1 API; this should be a v2 example.
|
||||
it "should be an api-v2 plugin" do
|
||||
status.api_generation.must_equal(2)
|
||||
end
|
||||
|
||||
# Plugins can support several different activator hooks, each of which has a type.
|
||||
# Since this is (primarily) a CliCommand plugin, we'd expect to see that among our types.
|
||||
it "should include a cli_command activator hook" do
|
||||
status.plugin_types.must_include(:cli_command)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
require_relative '../../../shared/core_plugin_test_helper.rb'
|
||||
|
||||
class InitPluginCli < MiniTest::Test
|
||||
include CorePluginFunctionalHelper
|
||||
|
||||
def test_generating_inspec_plugin_correct_prefix_required
|
||||
Dir.mktmpdir do |dir|
|
||||
plugin = 'wacky-name'
|
||||
run_result = run_inspec_process("init plugin #{plugin}", prefix: "cd #{dir} &&")
|
||||
assert_equal 1, run_result.exit_status
|
||||
assert_empty run_result.stderr
|
||||
assert_includes run_result.stdout, 'ERROR'
|
||||
assert_includes run_result.stdout, 'Plugin names must begin with'
|
||||
end
|
||||
end
|
||||
|
||||
def test_generating_inspec_plugin_with_default_options
|
||||
Dir.mktmpdir do |dir|
|
||||
plugin = 'inspec-test-generated-plugin'
|
||||
snake_case = plugin.tr('-', '_')
|
||||
camel_case = plugin.sub(/^inspec\-/, '').split('-').map(&:capitalize).join('')
|
||||
|
||||
run_result = run_inspec_process("init plugin #{plugin}", prefix: "cd #{dir} &&")
|
||||
assert_empty run_result.stderr
|
||||
assert_equal 0, run_result.exit_status
|
||||
assert_includes run_result.stdout, 'Creating new inspec plugin at'
|
||||
assert_includes run_result.stdout, plugin
|
||||
|
||||
# Check generated files and contents.
|
||||
# Each file must exist, and its contents must match each of the regexen given.
|
||||
{
|
||||
File.join(plugin, 'README.md') => [],
|
||||
File.join(plugin, 'LICENSE') => [],
|
||||
File.join(plugin, 'Gemfile') => [],
|
||||
File.join(plugin, 'Rakefile') => [],
|
||||
File.join(plugin, plugin + '.gemspec') => [],
|
||||
File.join(plugin, 'lib', plugin + '.rb') => [],
|
||||
File.join(plugin, 'lib', plugin, 'plugin.rb') => [],
|
||||
File.join(plugin, 'lib', plugin, 'version.rb') => [],
|
||||
File.join(plugin, 'lib', plugin, 'cli_command.rb') => [],
|
||||
File.join(plugin, 'test', 'helper.rb') => [],
|
||||
File.join(plugin, 'test', 'functional', snake_case + '_test.rb') => [],
|
||||
File.join(plugin, 'test', 'unit', 'plugin_def_test.rb') => [],
|
||||
File.join(plugin, 'test', 'unit', 'cli_args_test.rb') => [],
|
||||
}.each do |path, regexen|
|
||||
full_path = File.join(dir, path)
|
||||
assert(File.exist?(full_path), "#{path} should have been generated")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue