From 442e21197c8ad76c67e61e38701f1fbc6d1b7795 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Tue, 14 Jan 2025 14:44:53 +0100 Subject: [PATCH] add curl_command as serverspec extension --- README.md | 23 +++++++- .../acceptance/serverspec_extensions.rb | 12 ++++ .../serverspec_extensions/curl_command.rb | 57 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 lib/voxpupuli/acceptance/serverspec_extensions.rb create mode 100644 lib/voxpupuli/acceptance/serverspec_extensions/curl_command.rb diff --git a/README.md b/README.md index e0fe453..5a46dd1 100644 --- a/README.md +++ b/README.md @@ -246,4 +246,25 @@ describe 'my example' do end ``` -For this `examples/my_example.pp' must exist and contain valid Puppet code. It then uses the idempotent resource shared example. +For this `examples/my_example.pp` must exist and contain valid Puppet code. It then uses the idempotent resource shared example. + +## Serverspec extensions + +Some [Serverspec](https://serverspec.org/) extensions are shipped and enabled by default. These make it easier to write tests but were not accepted by Serverspec upstream. + +### `curl_command` + +Often you want to test some service that exposes things over HTTP. +Instead of using [`command("curl …")`](https://serverspec.org/resource_types.html#command) you can use `curl_command(…)` which behaves like a Serverspec `command` but adds matchers for the HTTP response code and the response body. + +```ruby +describe curl_command("http://localhost:8080/api/ping") do + its(:response_code) { is_expected.to eq(200) } + its(:exit_status) { is_expected.to eq 0 } +end + +describe curl_command('http://localhost:8080/api/status', headers: { 'Accept' => 'application/json' }) do + its(:response_code) { is_expected.to eq(200) } + its(:body_as_json) { is_expected.to eq({'status': 'ok'}) } +end +``` diff --git a/lib/voxpupuli/acceptance/serverspec_extensions.rb b/lib/voxpupuli/acceptance/serverspec_extensions.rb new file mode 100644 index 0000000..1bda8ab --- /dev/null +++ b/lib/voxpupuli/acceptance/serverspec_extensions.rb @@ -0,0 +1,12 @@ +require 'serverspec' +require_relative 'serverspec_extensions/curl_command' + +module Serverspec + module Helper + module Type + def curl_command(*args) + Voxpupuli::Acceptance::ServerspecExtensions::CurlCommand.new(*args) + end + end + end +end diff --git a/lib/voxpupuli/acceptance/serverspec_extensions/curl_command.rb b/lib/voxpupuli/acceptance/serverspec_extensions/curl_command.rb new file mode 100644 index 0000000..6fd833a --- /dev/null +++ b/lib/voxpupuli/acceptance/serverspec_extensions/curl_command.rb @@ -0,0 +1,57 @@ +# written by https://github.com/ekohl +# https://github.com/mizzy/serverspec/pull/611 was rejected so adding it here. + +require 'serverspec' + +module Voxpupuli + module Acceptance + module ServerspecExtensions + class CurlCommand < Serverspec::Type::Command + def response_code + m = %r{Response-Code: (?\d+)}.match(stderr) + return 0 unless m + + m[:code].to_i + end + + def body + command_result.stdout + end + + def body_as_json + MultiJson.load(body) + end + + private + + def curl_command + # curl on EL8 is too old to support --write-out %stderr + command = "curl --silent --write-out '%{stderr}Response-Code: %{response_code}\\n' '#{@name}'" + + @options.each do |option, value| + case option + when :cacert, :cert, :key + command += " --#{option} '#{value}'" + when :headers + value.each do |header, header_value| + command += if header_value + " --header '#{header}: #{header_value}'" + else + " --header '#{header};'" + end + end + else + raise "Unknown option #{option} (value: #{value})" + end + end + + command + end + + def command_result + @command_result ||= @runner.run_command(curl_command) + end + end + end + end +end