specr 0.0.1.beta1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cde21d78fee811f0a9f53137f5adaddcb82199be
4
+ data.tar.gz: 88adee928d0cd54c716ce620bc3f9b515b822cfa
5
+ SHA512:
6
+ metadata.gz: 3292f93ba0c20241876b31656253f85ad8dc21fb95dee2cb4c06022b6d6b39b5a5e6dd0dff4cf0553b59c3a87130cafa6e6aae0e6c8d125b490868a6e9403469
7
+ data.tar.gz: 0d3e876118e04c83a9c87a91a4ad076613bd0736b0fb9648f71bf3ba79f42f131d0f4637c660b17faa9c92d5ab9378bcdef7801f0b21b9e78f8514e446b9226f
@@ -0,0 +1,4 @@
1
+ scenarios.json
2
+ *.iml
3
+ api_spec.json
4
+ pkg/
@@ -0,0 +1,43 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+ Exclude:
6
+ - !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/
7
+ DisplayCopNames: true
8
+ DisplayStyleGuide: true
9
+
10
+ Rails:
11
+ Enabled: true
12
+
13
+ Style/WordArray:
14
+ Exclude:
15
+ - 'app/forms/*_form.rb'
16
+ - 'test/fabricators/extension_package_fabricator.rb'
17
+
18
+ Style/RegexpLiteral:
19
+ AllowInnerSlashes: true
20
+
21
+ Lint/UnusedMethodArgument:
22
+ Exclude:
23
+ - 'lib/tasks/populate.rake'
24
+
25
+ Metrics/LineLength:
26
+ Exclude:
27
+ - 'test/**/*.rb'
28
+ - 'Gemfile*'
29
+
30
+ Style/IndentArray:
31
+ EnforcedStyle: consistent
32
+
33
+ Style/MultilineMethodCallIndentation:
34
+ EnforcedStyle: indented
35
+
36
+ Style/MultilineOperationIndentation:
37
+ EnforcedStyle: indented
38
+
39
+ Documentation:
40
+ Enabled: false
41
+
42
+ Style/ClassAndModuleChildren:
43
+ Enabled: false
@@ -0,0 +1,65 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-06-21 14:42:30 -0600 using RuboCop version 0.40.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Lint/AmbiguousRegexpLiteral:
11
+ Exclude:
12
+ - 'lib/specr/step_definitions/http_steps.rb'
13
+
14
+ # Offense count: 1
15
+ # Cop supports --auto-correct.
16
+ # Configuration parameters: AlignWith, SupportedStyles, AutoCorrect.
17
+ # SupportedStyles: keyword, variable, start_of_line
18
+ Lint/EndAlignment:
19
+ Enabled: false
20
+
21
+ # Offense count: 1
22
+ Lint/Loop:
23
+ Exclude:
24
+ - 'lib/specr/step_definitions/async_steps.rb'
25
+
26
+ # Offense count: 3
27
+ Lint/UselessAssignment:
28
+ Exclude:
29
+ - 'lib/specr/tiny_client.rb'
30
+
31
+ # Offense count: 5
32
+ Metrics/AbcSize:
33
+ Max: 25
34
+
35
+ # Offense count: 1
36
+ # Configuration parameters: CountComments.
37
+ Metrics/ClassLength:
38
+ Max: 109
39
+
40
+ # Offense count: 2
41
+ Metrics/CyclomaticComplexity:
42
+ Max: 10
43
+
44
+ # Offense count: 18
45
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
46
+ # URISchemes: http, https
47
+ Metrics/LineLength:
48
+ Max: 131
49
+
50
+ # Offense count: 6
51
+ # Configuration parameters: CountComments.
52
+ Metrics/MethodLength:
53
+ Max: 28
54
+
55
+ # Offense count: 2
56
+ Metrics/PerceivedComplexity:
57
+ Max: 11
58
+
59
+ # Offense count: 2
60
+ # Cop supports --auto-correct.
61
+ # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
62
+ # SupportedStyles: slashes, percent_r, mixed
63
+ Style/RegexpLiteral:
64
+ Exclude:
65
+ - 'compliance.gemspec'
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ specr (0.0.1.beta1)
5
+ cucumber (~> 2.4.0)
6
+ httmultiparty (~> 0.3.16)
7
+ json-schema (~> 2.6)
8
+ rake (~> 10.0)
9
+ test-unit
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.5.1)
15
+ public_suffix (~> 2.0, >= 2.0.2)
16
+ ast (2.3.0)
17
+ builder (3.2.3)
18
+ byebug (9.0.5)
19
+ coderay (1.1.1)
20
+ cucumber (2.4.0)
21
+ builder (>= 2.1.2)
22
+ cucumber-core (~> 1.5.0)
23
+ cucumber-wire (~> 0.0.1)
24
+ diff-lcs (>= 1.1.3)
25
+ gherkin (~> 4.0)
26
+ multi_json (>= 1.7.5, < 2.0)
27
+ multi_test (>= 0.1.2)
28
+ cucumber-core (1.5.0)
29
+ gherkin (~> 4.0)
30
+ cucumber-wire (0.0.1)
31
+ diff-lcs (1.3)
32
+ gherkin (4.1.3)
33
+ httmultiparty (0.3.16)
34
+ httparty (>= 0.7.3)
35
+ mimemagic
36
+ multipart-post
37
+ httparty (0.15.3)
38
+ multi_xml (>= 0.5.2)
39
+ json-schema (2.8.0)
40
+ addressable (>= 2.4)
41
+ method_source (0.8.2)
42
+ mimemagic (0.3.2)
43
+ multi_json (1.12.1)
44
+ multi_test (0.1.2)
45
+ multi_xml (0.6.0)
46
+ multipart-post (2.0.0)
47
+ parser (2.3.1.2)
48
+ ast (~> 2.2)
49
+ power_assert (1.0.2)
50
+ powerpack (0.1.1)
51
+ pry (0.10.3)
52
+ coderay (~> 1.1.0)
53
+ method_source (~> 0.8.1)
54
+ slop (~> 3.4)
55
+ pry-byebug (3.4.0)
56
+ byebug (~> 9.0)
57
+ pry (~> 0.10)
58
+ public_suffix (2.0.5)
59
+ rainbow (2.1.0)
60
+ rake (10.5.0)
61
+ rubocop (0.40.0)
62
+ parser (>= 2.3.1.0, < 3.0)
63
+ powerpack (~> 0.1)
64
+ rainbow (>= 1.99.1, < 3.0)
65
+ ruby-progressbar (~> 1.7)
66
+ unicode-display_width (~> 1.0, >= 1.0.1)
67
+ ruby-progressbar (1.8.1)
68
+ slop (3.6.0)
69
+ test-unit (3.2.3)
70
+ power_assert
71
+ unicode-display_width (1.0.5)
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ bundler (~> 1.10)
78
+ pry-byebug
79
+ rubocop (~> 0.40.0)
80
+ specr!
81
+
82
+ BUNDLED WITH
83
+ 1.14.4
@@ -0,0 +1,28 @@
1
+ # Specr
2
+
3
+ A toolkit to assist in validating APIs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'specr'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install specr
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ Bug reports and pull requests are welcome on GitHub at https://github.com/remear/specr.
28
+
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "specr"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ require 'httmultiparty'
3
+ require 'json'
4
+ require 'json-schema'
5
+
6
+ require 'specr/version'
7
+ require 'specr/extracer'
8
+ require 'specr/tiny_client'
9
+ require 'specr/step_definitions/async_steps'
10
+ require 'specr/step_definitions/debugging_steps'
11
+ require 'specr/step_definitions/http_steps'
12
+ require 'specr/step_definitions/jsonapi_steps'
13
+ require 'specr/step_definitions/validation_steps'
14
+ require 'specr/step_definitions/jsonapi_validation_steps'
15
+
16
+ require 'specr/features/support/initial_setup'
17
+
18
+ module Specr
19
+ class << self
20
+ attr_accessor :configuration, :client, :logger
21
+ end
22
+
23
+ def self.configure
24
+ self.configuration ||= Configuration.new
25
+ yield configuration
26
+ end
27
+
28
+ class Configuration
29
+ attr_accessor :root_url
30
+ attr_accessor :default_headers
31
+ attr_accessor :max_request_attempts
32
+ attr_accessor :request_attempt_delay
33
+
34
+ def initialize
35
+ @root_url = 'http://localhost:3000'
36
+ @default_headers = {
37
+ 'Accept' => 'application/json',
38
+ 'Content-Type' => 'application/json'
39
+ }
40
+ @max_request_attempts = 5
41
+ @request_attempt_delay = 2
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+ module Specr
3
+ class Extracer
4
+ def initialize
5
+ @scenarios = Array.new
6
+ @resources_create = Hash.new([])
7
+ @resources_update = Hash.new([])
8
+ end
9
+
10
+ def log_request(method, endpoint, request, response, response_code,
11
+ response_message)
12
+ if request.is_a? String
13
+ begin
14
+ request = JSON.parse(request)
15
+ rescue
16
+ # WHAT ARE WE EVEN GETTING
17
+ return
18
+ end
19
+ end
20
+ scenario = {
21
+ name: scenario_name,
22
+ endpoint: endpoint,
23
+ method: method,
24
+ request: request,
25
+ response: response,
26
+ response_code: response_code,
27
+ response_message: response_message
28
+ }
29
+ @scenarios << scenario
30
+ return unless response
31
+ # TODO: make these extract from arrays when those are being used
32
+ if endpoint.start_with?(Specr.configuration.root_url)
33
+ endpoint = endpoint[Specr.configuration.root_url.length..-1]
34
+ end
35
+ if method == 'POST'
36
+ # the path is either /[resource_name] or /[some_other_resource]/[guid]/[resource_name]
37
+ # and the resource is getting created
38
+ resource = endpoint.split('/').last
39
+ @resources_create[resource] += [scenario]
40
+ elsif method == 'PATCH'
41
+ # the path will be /[resource_name]/[guid]
42
+ resource = endpoint.split('/')[1]
43
+ @resources_update[resource] += [scenario]
44
+ end
45
+ # 'GET' & 'DELETE' requests are ignored, and 'PUT' can be similar to a 'PATCH' in updating
46
+ end
47
+
48
+ def save
49
+ file = File.join('specification.json')
50
+ json = {
51
+ endpoints: load_endpoints,
52
+ scenarios: @scenarios,
53
+ resources_create: @resources_create,
54
+ resources_update: @resources_update,
55
+ forms: load_forms,
56
+ schemas: load_schemas
57
+ }
58
+ File.open(file, 'w') { |f| f.write(JSON.pretty_generate(json)) }
59
+ end
60
+
61
+ private
62
+
63
+ def scenario_name
64
+ scenario = Specr.client.current_scenario
65
+ [scenario.feature.name.parameterize.underscore,
66
+ scenario.name.parameterize.underscore].join('.')
67
+ end
68
+
69
+ def resolve_refs(json, path)
70
+ if json.is_a? Hash
71
+ if json['$ref']
72
+ path = File.join(File.dirname(path), json['$ref'])
73
+ json.delete('$ref')
74
+ Hash[resolve_refs(JSON.parse(File.read(path)), path).to_a + json.to_a]
75
+ else
76
+ Hash[json.map do |key, value|
77
+ [key, resolve_refs(value, path)]
78
+ end]
79
+ end
80
+ elsif json.is_a? Array
81
+ json.map do |j|
82
+ resolve_refs j, path
83
+ end
84
+ else
85
+ json
86
+ end
87
+ end
88
+
89
+ def load_schemas
90
+ ret = {}
91
+ path = File.join('fixtures')
92
+ Dir.glob("#{path}/**/*.json") do |f|
93
+ json = resolve_refs(JSON.parse(File.read(f)), f)
94
+ ret[f] = json
95
+ end
96
+ ret
97
+ end
98
+
99
+ def load_endpoints
100
+ JSON.parse(File.read('endpoints.json'))
101
+ end
102
+
103
+ def load_forms
104
+ JSON.parse(File.read('forms.json'))
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require 'logger'
3
+ require 'test/unit/assertions'
4
+
5
+ World(Test::Unit::Assertions)
6
+
7
+ extracer = Specr::Extracer.new
8
+
9
+ AfterConfiguration do
10
+ Specr.logger = Logger.new(STDOUT)
11
+ Specr.logger.level = Logger::FATAL
12
+ Specr.client = Specr::TinyClient.new(extracer)
13
+ end
14
+
15
+ Before do |scenario|
16
+ Specr.client.clear_storage
17
+ Specr.client.current_scenario = scenario
18
+ end
19
+
20
+ Before('@cleanroom') do
21
+ Specr.client.clear_storage
22
+ end
23
+
24
+ at_exit do
25
+ Specr.client.extracer.save
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ And(/^I await processing for ((?:https?:\/)?\/\S*) with the condition "(\S*?)" "(\S*)" "(.*?)"$/) do |url, keys, comperator, value|
3
+ await_resource_processing(url, comperator, keys, value)
4
+ end
5
+
6
+ def await_resource_processing(href, comperator, watched_attribute, desired_value)
7
+ attempts = 0
8
+ max_attempts = 5
9
+ pending = true
10
+ response = nil
11
+ begin
12
+ response = JSON.parse(Specr.client.get(href))
13
+ current_value = Specr.client.get_link(watched_attribute)
14
+ if current_value.method(comperator).call(desired_value)
15
+ pending = false
16
+ else
17
+ sleep 5
18
+ end
19
+ attempts += 1
20
+ end while pending && (attempts < max_attempts)
21
+ raise ArgumentError, 'Timed out waiting for resource processing!' if pending == true
22
+ response
23
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ Then(/^debug$/) do
3
+ Specr.logger.debug "HTTP status code: #{@client.last_code}"
4
+ Specr.logger.debug "HTTP body: #{@client.last_body}"
5
+ Specr.logger.debug "Storage: #{@client.storage}"
6
+ end
7
+
8
+ When(/^debugging is enabled$/) do
9
+ Specr.logger.level = Logger::DEBUG
10
+ end
11
+
12
+ When(/^debugging is disabled$/) do
13
+ Specr.logger.level = Logger::FATAL
14
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ When /^I set headers:$/ do |request_headers|
3
+ Specr.client.headers = Specr.configuration.default_headers.merge(request_headers.rows_hash)
4
+ end
5
+
6
+ When(/^I (\w+) to ((?:https?:\/)?\/\S*)$/) do |verb, url|
7
+ Specr.client.send(verb.downcase, url)
8
+ end
9
+
10
+ When(/^I (\w+) to ((?:https?:\/)?\/\S*) with the body:$/) do |verb, url, body|
11
+ Specr.client.send(verb.downcase.to_sym, url, body)
12
+ end
13
+
14
+ When(/^I (POST|PATCH) to (\/\S*?) with the file "(.*?)" as "(.*?)"$/) do |verb, url, file, file_field|
15
+ Specr.client.send("#{verb.downcase}_multipart",
16
+ Specr.client.hydrater(url), file, file_field, nil)
17
+ end
18
+
19
+ When(/^I (POST|PATCH) to (\/\S*?) with the "(.*?)" file as "(.*?)" and the body:$/) do |verb, url, file, file_field, body|
20
+ Specr.client.send("#{verb.downcase}_multipart",
21
+ Specr.client.hydrater(url), file, file_field, body)
22
+ end
23
+
24
+ When(/^I (\w+) to the "(.*?)" link with the body:$/) do |verb, keys, body|
25
+ step "I #{verb} to #{Specr.client.get_link(keys)} with the body:", body
26
+ end
27
+
28
+ When(/^I (\w+) to the "(.*?)" link$/) do |verb, keys|
29
+ step "I #{verb} to #{Specr.client.get_link(keys)}"
30
+ end
31
+
32
+ Then(/^the response has this schema:$/) do |schema|
33
+ Specr.client.validate(schema)
34
+ end
35
+
36
+ Then(/^the response is valid according to the "(.*?)" schema$/) do |filename|
37
+ Specr.client.validate(filename)
38
+ end
39
+
40
+ Then(/^I should get a (.+) status code$/) do |code|
41
+ message = Specr.client.last_body.fetch('description', '') if Specr.client.last_body
42
+ assert_equal code.to_i, Specr.client.last_code, message
43
+ end
44
+
45
+ Then(/^there should be no response body$/) do
46
+ assert_nil Specr.client.last_body
47
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ When(/^I (\w+) to the (top|resource)\-level (\w+) link$/) do |verb, level, link|
3
+ step "I #{verb} to the #{level}-level \"#{link}\" link with the body:", nil
4
+ end
5
+
6
+ When(/^I (\w+) to the (top|resource)\-level (\w+) link with the body:$/) do |verb, level, link, body|
7
+ keys = case level
8
+ when 'resource' then "data.links.#{link}"
9
+ when 'top' then "links.#{link}"
10
+ end
11
+ step "I #{verb} to the \"#{keys}\" link with the body:", body
12
+ end
@@ -0,0 +1,10 @@
1
+ Then(/^the attributes match:$/) do |against|
2
+ checker JSON.parse(Specr.client.hydrater(against)), Specr.client['data']['attributes'], ''
3
+ end
4
+
5
+ Then(/^the errors match:$/) do |against|
6
+ against = JSON.parse(Specr.client.hydrater(against))
7
+ Specr.client['errors'].each do |error|
8
+ checker against, error, ''
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # TODO: move to a validator
3
+ def checker(from, of, nesting)
4
+ assert_not_nil from, nesting
5
+ from.each_pair do |key, val|
6
+ if val.is_a?(String) || val.is_a?(Integer) || val.is_a?(TrueClass) || val.is_a?(FalseClass)
7
+ assert_equal val, of[key], "#{nesting}>#{key}"
8
+ elsif val.nil?
9
+ assert_nil of[key]
10
+ elsif of.is_a?(Array)
11
+ checker val, of[0][key], "#{nesting}>#{key}"
12
+ else
13
+ checker val, of[key], "#{nesting}>#{key}"
14
+ end
15
+ end
16
+ end
17
+
18
+ Then(/^the fields match:$/) do |against|
19
+ checker JSON.parse(Specr.client.hydrater(against)), Specr.client['data'], ''
20
+ end
21
+
22
+ Then(/^the fields match:$/) do |against|
23
+ against = JSON.parse(Specr.client.hydrater(against))
24
+ Specr.client.last_body[resource].each do |body|
25
+ checker against, body, ''
26
+ end
27
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+ module Specr
3
+ class TinyClient
4
+ attr_accessor :headers, :current_scenario
5
+ attr_reader :extracer, :responses, :storage
6
+
7
+ def initialize(extracer)
8
+ @extracer = extracer
9
+ @root_url = Specr.configuration.root_url
10
+ @headers = Specr.configuration.default_headers
11
+ @responses = []
12
+ @storage = {}
13
+ end
14
+
15
+ def clear_storage
16
+ @storage = {}
17
+ end
18
+
19
+ def build_options(opts)
20
+ multipart = opts.delete(:multipart)
21
+ body = opts.delete(:body)
22
+ body = JSON.parse(body) unless [Hash, NilClass].include? body.class # TODO: is this necessary?
23
+ body = hydrater(JSON.dump(body)) if body
24
+ options = {
25
+ headers: headers
26
+ }.tap do |o|
27
+ o[:body] = body if body
28
+ if multipart
29
+ file = opts.delete(:file)
30
+ field = opts.delete(:field)
31
+ o[:body] = {} unless o[:body]
32
+ o[:body][field] = file
33
+ end
34
+ end
35
+ end
36
+
37
+ def build_url(endpoint)
38
+ endpoint = hydrater(endpoint)
39
+ url = endpoint =~ /https?:\/\// ? endpoint : "#{@root_url}#{endpoint}"
40
+ end
41
+
42
+ def refine_endpoint(ep)
43
+ ep =~ /^\/\S*\/:\S*_id$/ ? ep.sub(/:(\S*_)id/, ':id') : ep
44
+ end
45
+
46
+ def request(verb, endpoint, opts = {})
47
+ raise 'HTTP Verb must be a symbol' unless verb.is_a? Symbol
48
+ multipart = opts[:multipart]
49
+ url = build_url(endpoint)
50
+ options = build_options(opts)
51
+ Specr.logger.debug(options)
52
+ response = if multipart
53
+ HTTMultiParty.post(url, options)
54
+ else
55
+ HTTParty.send(verb, url, options)
56
+ end
57
+ Specr.logger.debug(response)
58
+ responses << response
59
+ extracer.log_request(
60
+ verb.to_s.upcase,
61
+ refine_endpoint(endpoint),
62
+ options.fetch(:body, nil),
63
+ last_body,
64
+ response.code,
65
+ response.message
66
+ ) if last_code < 400
67
+ response
68
+ end
69
+
70
+ def post(endpoint, body = nil, opts = {})
71
+ request(:post, endpoint, opts.merge!(body: body))
72
+ end
73
+
74
+ def post_multipart(endpoint, file, field, _body, opts = {})
75
+ file_name = File.join('fixtures', 'files', file)
76
+ options = opts.merge!(multipart: true,
77
+ file: File.new(file_name),
78
+ field: field)
79
+ request(:post, endpoint, options)
80
+ end
81
+
82
+ def put(endpoint, body, opts = {})
83
+ request(:put, endpoint, opts.merge!(body: body))
84
+ end
85
+
86
+ def patch(endpoint, body, opts = {})
87
+ request(:patch, endpoint, opts.merge!(body: body))
88
+ end
89
+
90
+ def get(endpoint)
91
+ request(:get, endpoint)
92
+ end
93
+
94
+ def delete(endpoint, body = nil, opts = {})
95
+ request(:delete, endpoint, body ? opts.merge!(body: body) : opts)
96
+ end
97
+
98
+ def add_response(response)
99
+ responses << response
100
+ end
101
+
102
+ def last_code
103
+ responses.last.code
104
+ end
105
+
106
+ def last_body
107
+ JSON.parse(responses.last.body) if responses.last.body
108
+ end
109
+
110
+ def get_link(keys)
111
+ last_body.dig(*keys.split('.'))
112
+ end
113
+
114
+ def hydrater(what)
115
+ return unless what
116
+ what.gsub(/:\w+/) do |match|
117
+ key = match.tr(':', '')
118
+ hydrated_value = storage[key] ? storage[key] : storage[key.to_sym]
119
+ hydrated_value ? hydrated_value : match
120
+ end
121
+ end
122
+
123
+ def validate(against)
124
+ file_name = File.join('fixtures', "#{against}.json")
125
+ if File.exist?(file_name) && (!against.is_a? Hash)
126
+ JSON::Validator.validate!(file_name, last_body)
127
+ else
128
+ JSON::Validator.validate!(against, last_body)
129
+ end
130
+ end
131
+
132
+ def [](name)
133
+ if last_body.key? name
134
+ last_body[name]
135
+ else
136
+ last_body.select { |x| x != 'links' && x != 'meta' }.values.first[name]
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Specr
3
+ VERSION = '0.0.1.beta1'
4
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'specr/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'specr'
9
+ spec.version = Specr::VERSION
10
+ spec.authors = ['Ben Mills']
11
+ spec.email = ['ben@unfiniti.com']
12
+
13
+ spec.summary = 'A toolkit for building API specifications.'
14
+ spec.description = 'A toolkit for building API specifications.'
15
+ spec.homepage = 'http://github.com/remear/specr'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'cucumber', '~> 2.4.0'
23
+ spec.add_dependency 'httmultiparty', '~> 0.3.16'
24
+ spec.add_dependency 'json-schema', '~> 2.6'
25
+ spec.add_dependency 'test-unit'
26
+ spec.add_dependency 'rake', '~> 10.0'
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.10'
29
+ spec.add_development_dependency 'pry-byebug'
30
+ spec.add_development_dependency 'rubocop', '~> 0.40.0'
31
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: specr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Mills
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-07-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cucumber
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: httmultiparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.16
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.16
41
+ - !ruby/object:Gem::Dependency
42
+ name: json-schema
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.10'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.40.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.40.0
125
+ description: A toolkit for building API specifications.
126
+ email:
127
+ - ben@unfiniti.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rubocop.yml"
134
+ - ".rubocop_todo.yml"
135
+ - ".travis.yml"
136
+ - Gemfile
137
+ - Gemfile.lock
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - lib/specr.rb
143
+ - lib/specr/extracer.rb
144
+ - lib/specr/features/support/initial_setup.rb
145
+ - lib/specr/step_definitions/async_steps.rb
146
+ - lib/specr/step_definitions/debugging_steps.rb
147
+ - lib/specr/step_definitions/http_steps.rb
148
+ - lib/specr/step_definitions/jsonapi_steps.rb
149
+ - lib/specr/step_definitions/jsonapi_validation_steps.rb
150
+ - lib/specr/step_definitions/validation_steps.rb
151
+ - lib/specr/tiny_client.rb
152
+ - lib/specr/version.rb
153
+ - specr.gemspec
154
+ homepage: http://github.com/remear/specr
155
+ licenses: []
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">"
169
+ - !ruby/object:Gem::Version
170
+ version: 1.3.1
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 2.6.8
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: A toolkit for building API specifications.
177
+ test_files: []