usagi 1.0.0
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 +7 -0
- data/.gitignore +35 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +22 -0
- data/README.md +2 -0
- data/bin/usagi +27 -0
- data/lib/usagi.rb +86 -0
- data/lib/usagi/api_response.rb +80 -0
- data/lib/usagi/matcher.rb +44 -0
- data/lib/usagi/matcher_container.rb +32 -0
- data/lib/usagi/rspec.rb +43 -0
- data/lib/usagi/usagi_matchers.rb +27 -0
- data/usagi.gemspec +15 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 05d0450e25d612318e48d92c0fe7e3d4aaaa58c9
|
4
|
+
data.tar.gz: f5f666caec60871805e4f829cb599c4a4b3939e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 37c026e882eb31c803c7344e7239683356886b81d4f498a925aef73d60b7d04b27dfa44883d07079b9fda1a1476fe51c171deffe107fce166fd4856206029471
|
7
|
+
data.tar.gz: c1ff4e485ac2c5e215ec7d0133b340c96abf595810a5737af66b31c00ab32180fbb7c08cd10e3bd10013d59a74db0b0833325cc3ce8e59f7ffb5daab87da93c6
|
data/.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
*.rbc
|
2
|
+
capybara-*.html
|
3
|
+
.rspec
|
4
|
+
/log
|
5
|
+
/tmp
|
6
|
+
/db/*.sqlite3
|
7
|
+
/db/*.sqlite3-journal
|
8
|
+
/public/system
|
9
|
+
/coverage/
|
10
|
+
/spec/tmp
|
11
|
+
**.orig
|
12
|
+
rerun.txt
|
13
|
+
pickle-email-*.html
|
14
|
+
|
15
|
+
# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
|
16
|
+
config/initializers/secret_token.rb
|
17
|
+
config/secrets.yml
|
18
|
+
|
19
|
+
## Environment normalisation:
|
20
|
+
/.bundle
|
21
|
+
/vendor/bundle
|
22
|
+
|
23
|
+
# these should all be checked in to normalise the environment:
|
24
|
+
# Gemfile.lock, .ruby-version, .ruby-gemset
|
25
|
+
|
26
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
27
|
+
.rvmrc
|
28
|
+
|
29
|
+
# if using bower-rails ignore default bower_components path bower.json files
|
30
|
+
/vendor/assets/bower_components
|
31
|
+
*.bowerrc
|
32
|
+
bower.json
|
33
|
+
|
34
|
+
# Ignore pow environment settings
|
35
|
+
.powenv
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :test do
|
4
|
+
gem 'rspec', '3.2.0'
|
5
|
+
gem 'rspec-core', '3.2.3'
|
6
|
+
gem 'rspec-expectations', '3.2.1'
|
7
|
+
gem 'rspec-mocks', '3.2.1'
|
8
|
+
gem 'rspec-support', '3.2.2'
|
9
|
+
gem 'rspec-rails', '3.2.3'
|
10
|
+
gem 'database_cleaner'
|
11
|
+
gem 'nyan-cat-formatter'
|
12
|
+
gem 'simplecov', require: false
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionpack (4.1.8)
|
5
|
+
actionview (= 4.1.8)
|
6
|
+
activesupport (= 4.1.8)
|
7
|
+
rack (~> 1.5.2)
|
8
|
+
rack-test (~> 0.6.2)
|
9
|
+
actionview (4.1.8)
|
10
|
+
activesupport (= 4.1.8)
|
11
|
+
builder (~> 3.1)
|
12
|
+
erubis (~> 2.7.0)
|
13
|
+
activesupport (4.1.8)
|
14
|
+
i18n (~> 0.6, >= 0.6.9)
|
15
|
+
json (~> 1.7, >= 1.7.7)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
thread_safe (~> 0.1)
|
18
|
+
tzinfo (~> 1.1)
|
19
|
+
builder (3.2.2)
|
20
|
+
database_cleaner (1.4.1)
|
21
|
+
diff-lcs (1.2.5)
|
22
|
+
docile (1.1.5)
|
23
|
+
erubis (2.7.0)
|
24
|
+
i18n (0.7.0)
|
25
|
+
json (1.8.3)
|
26
|
+
minitest (5.7.0)
|
27
|
+
nyan-cat-formatter (0.11)
|
28
|
+
rspec (>= 2.99, >= 2.14.2, < 4)
|
29
|
+
rack (1.5.5)
|
30
|
+
rack-test (0.6.3)
|
31
|
+
rack (>= 1.0)
|
32
|
+
railties (4.1.8)
|
33
|
+
actionpack (= 4.1.8)
|
34
|
+
activesupport (= 4.1.8)
|
35
|
+
rake (>= 0.8.7)
|
36
|
+
thor (>= 0.18.1, < 2.0)
|
37
|
+
rake (10.4.2)
|
38
|
+
rspec (3.2.0)
|
39
|
+
rspec-core (~> 3.2.0)
|
40
|
+
rspec-expectations (~> 3.2.0)
|
41
|
+
rspec-mocks (~> 3.2.0)
|
42
|
+
rspec-core (3.2.3)
|
43
|
+
rspec-support (~> 3.2.0)
|
44
|
+
rspec-expectations (3.2.1)
|
45
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
46
|
+
rspec-support (~> 3.2.0)
|
47
|
+
rspec-mocks (3.2.1)
|
48
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
+
rspec-support (~> 3.2.0)
|
50
|
+
rspec-rails (3.2.3)
|
51
|
+
actionpack (>= 3.0, < 4.3)
|
52
|
+
activesupport (>= 3.0, < 4.3)
|
53
|
+
railties (>= 3.0, < 4.3)
|
54
|
+
rspec-core (~> 3.2.0)
|
55
|
+
rspec-expectations (~> 3.2.0)
|
56
|
+
rspec-mocks (~> 3.2.0)
|
57
|
+
rspec-support (~> 3.2.0)
|
58
|
+
rspec-support (3.2.2)
|
59
|
+
simplecov (0.10.0)
|
60
|
+
docile (~> 1.1.0)
|
61
|
+
json (~> 1.8)
|
62
|
+
simplecov-html (~> 0.10.0)
|
63
|
+
simplecov-html (0.10.0)
|
64
|
+
thor (0.19.1)
|
65
|
+
thread_safe (0.3.5)
|
66
|
+
tzinfo (1.2.2)
|
67
|
+
thread_safe (~> 0.1)
|
68
|
+
|
69
|
+
PLATFORMS
|
70
|
+
ruby
|
71
|
+
|
72
|
+
DEPENDENCIES
|
73
|
+
database_cleaner
|
74
|
+
nyan-cat-formatter
|
75
|
+
rspec (= 3.2.0)
|
76
|
+
rspec-core (= 3.2.3)
|
77
|
+
rspec-expectations (= 3.2.1)
|
78
|
+
rspec-mocks (= 3.2.1)
|
79
|
+
rspec-rails (= 3.2.3)
|
80
|
+
rspec-support (= 3.2.2)
|
81
|
+
simplecov
|
82
|
+
|
83
|
+
BUNDLED WITH
|
84
|
+
1.10.5
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
data/bin/usagi
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rspec'
|
3
|
+
require 'usagi'
|
4
|
+
|
5
|
+
# Available startup arguments:
|
6
|
+
# > --usagi-debug-requests: Outputs all curl commands used and the results
|
7
|
+
# > --usagi-rails-output: Outputs all rails IO
|
8
|
+
# > --usagi-allow-store-key-reuse: Allows to use STORE_VALUE multiple times with same key name
|
9
|
+
# > --usagi-allow_nil_store_values: Allows to store nil value for later use with REUSE_VALUE
|
10
|
+
|
11
|
+
usagi_args = ARGV.grep(/--usagi-/)
|
12
|
+
Usagi.start(*usagi_args)
|
13
|
+
|
14
|
+
ENV['RAILS_ENV'] = 'test'
|
15
|
+
|
16
|
+
begin
|
17
|
+
argv_list = ARGV - usagi_args
|
18
|
+
argv_list += Dir['./spec/usagi/**/*.rb'] unless ARGV.any?{|argv| argv =~ /\.rb$/ }
|
19
|
+
Usagi.rspec = RSpec::Core::Runner.run(argv_list.flatten)
|
20
|
+
rescue
|
21
|
+
puts "[usagi][#{Usagi.pid}] RSpec encountered an exception"
|
22
|
+
Usagi.stop
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
|
26
|
+
Usagi.stop
|
27
|
+
Process.exit(Usagi.rspec) if Usagi.rspec != 0
|
data/lib/usagi.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Usagi
|
4
|
+
class << self
|
5
|
+
attr_accessor :pid, :port, :rspec, :suite_options, :options
|
6
|
+
|
7
|
+
def start(*opts)
|
8
|
+
@options = {}
|
9
|
+
opts.each{|opt| @options[opt.gsub(/^--usagi-/,'').gsub('-','_').to_sym] = true }
|
10
|
+
@port = (rand * 65535).to_i until defined?(@port) && @port > 1024
|
11
|
+
@io = IO.popen([{'RAILS_ENV' => 'test'},['rails', 'bundle'], 'server', '-p', @port.to_s])
|
12
|
+
Thread.new(@io) do |rails_io|
|
13
|
+
buffer = ''
|
14
|
+
until rails_io.eof?
|
15
|
+
buffer += rails_io.readpartial(1024)
|
16
|
+
while buffer["\n"]
|
17
|
+
minibuf = buffer.split("\n").first
|
18
|
+
buffer = buffer[(minibuf.length + 1)..-1]
|
19
|
+
puts "[rails]>> #{minibuf}" if Usagi.options[:rails_output]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
puts "[rails]>> #{buffer}" if buffer.length > 0 && Usagi.options[:rails_output]
|
23
|
+
end
|
24
|
+
@pid = @io.pid
|
25
|
+
puts "[usagi][#{@pid}] Running rails server on port #{@port}"
|
26
|
+
|
27
|
+
Signal.trap('INT') do
|
28
|
+
stop
|
29
|
+
Process.exit
|
30
|
+
end
|
31
|
+
|
32
|
+
sleep 1 until `curl --silent "localhost:#{@port}"`.length > 0
|
33
|
+
|
34
|
+
puts "[usagi][#{@pid}] Rails server listening on port #{@port}"
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
begin
|
40
|
+
Process.kill('INT', @pid)
|
41
|
+
puts "[usagi][#{@pid}] SIGINT sent to rails server"
|
42
|
+
puts "[usagi][#{@pid}] Waiting for rails server to ack kill command..."
|
43
|
+
Process.wait(Usagi.pid)
|
44
|
+
rescue
|
45
|
+
end
|
46
|
+
puts "[usagi][#{@pid}] Killed rails server"
|
47
|
+
end
|
48
|
+
|
49
|
+
def options
|
50
|
+
@options ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def suite_options
|
54
|
+
@suite_options ||= {}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Matchers methods
|
58
|
+
def define_matcher(name, &block)
|
59
|
+
if matchers[name.to_s.upcase]
|
60
|
+
raise ArgumentError("already defined matcher #{name.to_s.upcase}")
|
61
|
+
end
|
62
|
+
matchers[name.to_s.upcase] = Matcher.new(name.to_s.upcase, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove_matcher(name)
|
66
|
+
unless matchers[name.to_s.upcase]
|
67
|
+
raise ArgumentError("undefined matcher #{name.to_s.upcase}")
|
68
|
+
end
|
69
|
+
matchers.delete(name.to_s.upcase)
|
70
|
+
end
|
71
|
+
|
72
|
+
def rename_matcher(old_name, new_name)
|
73
|
+
unless matchers[old_name.to_s.upcase]
|
74
|
+
raise ArgumentError("undefined matcher #{old_name.to_s.upcase}")
|
75
|
+
end
|
76
|
+
matchers[new_name.to_s.upcase] = matchers.delete(old_name.to_s.upcase)
|
77
|
+
end
|
78
|
+
|
79
|
+
def matchers
|
80
|
+
@matchers ||= MatcherContainer.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
require 'usagi/api_response'
|
86
|
+
require 'usagi/rspec' if defined? RSpec
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative 'matcher_container'
|
2
|
+
require_relative 'usagi_matchers'
|
3
|
+
|
4
|
+
module Usagi
|
5
|
+
class ApiResponse
|
6
|
+
def initialize(data)
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other_data)
|
11
|
+
## matchers
|
12
|
+
if @data =~ /^STORE_VALUE\((:.*)\)$/
|
13
|
+
return Usagi::ApiResponse.store_value($1, other_data)
|
14
|
+
end
|
15
|
+
if @data =~ /^REUSE_VALUE\((:.*)\)$/
|
16
|
+
@data = Usagi::ApiResponse.reuse_value($1)
|
17
|
+
unless @data == other_data
|
18
|
+
raise "non-matching stored data: #{@data}__#{other_data}__"
|
19
|
+
end
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
# usagi_matchers
|
24
|
+
if matcher_and_args = Usagi.matchers.find_from_scenario(@data)
|
25
|
+
matcher, args = *matcher_and_args
|
26
|
+
return true if matcher.matches?(other_data, args)
|
27
|
+
else
|
28
|
+
raise "non-matching data: (expected: #{@data}, got: #{other_data})" unless @data.class == other_data.class
|
29
|
+
case @data.class.to_s
|
30
|
+
when 'Array'
|
31
|
+
raise "non-matching array length (expected: #{@data.length}, got #{other_data.length})" unless @data.length == other_data.length
|
32
|
+
raise "non-matching arrays (expected: #{@data}, got: #{other_data})" unless @data.each_with_index.all? do |value, i|
|
33
|
+
Usagi::ApiResponse.new(value) == other_data[i]
|
34
|
+
end
|
35
|
+
when 'Hash'
|
36
|
+
exp_keys = @data.keys.map(&:to_sym) - Usagi::ApiResponse.unmatchable_keys
|
37
|
+
got_keys = other_data.keys.map(&:to_sym) - Usagi::ApiResponse.unmatchable_keys
|
38
|
+
raise "non-matching hash keys (expected: #{exp_keys}, got: #{got_keys}" unless exp_keys.length == got_keys.length
|
39
|
+
raise "non-matching hashes (expected #{@data}, got: #{other_data})" unless @data.all? do |key, value|
|
40
|
+
next true if Usagi::ApiResponse.unmatchable_keys.include?(key)
|
41
|
+
Usagi::ApiResponse.new(value) == other_data[key]
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise "non-matching data (expected: #{@data}, got: #{other_data})" unless @data == other_data
|
45
|
+
end
|
46
|
+
end
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
attr_accessor :unmatchable_keys
|
52
|
+
attr_accessor :stored_values
|
53
|
+
|
54
|
+
def unmatchable_keys
|
55
|
+
Usagi.suite_options[:unmatchable_keys] ||%i(created_at updated_at)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reuse_value(name)
|
59
|
+
@stored_values ||= {}
|
60
|
+
unless @stored_values.keys.include?(name) || Usagi.options[:allow_nil_store_values]
|
61
|
+
raise ArgumentError, "stored value name #{name} was never used"
|
62
|
+
end
|
63
|
+
value = @stored_values[name]
|
64
|
+
puts "[usagi][#{Usagi.pid}] REUSE _#{value}_"
|
65
|
+
value
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_value(name, value)
|
69
|
+
@stored_values ||= {}
|
70
|
+
if @stored_values.keys.include?(name) && !Usagi.options[:allow_store_key_reuse]
|
71
|
+
raise ArgumentError, "stored value name #{name} already used"
|
72
|
+
end
|
73
|
+
@stored_values[name] = value
|
74
|
+
puts "[usagi][#{Usagi.pid}] STORE #{name} => #{value}"
|
75
|
+
return true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Matcher
|
2
|
+
attr_accessor :name
|
3
|
+
|
4
|
+
def initialize(name, &block)
|
5
|
+
@name = name
|
6
|
+
@matcher = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(api_value, args)
|
10
|
+
mi = MatcherInstance.new(@matcher, api_value, args)
|
11
|
+
raise mi.error_message unless mi.matches?
|
12
|
+
end
|
13
|
+
|
14
|
+
class MatcherInstance
|
15
|
+
attr_accessor :api_value, :args
|
16
|
+
|
17
|
+
def initialize(matcher_block, api_value, args)
|
18
|
+
@api_value = api_value
|
19
|
+
instance_exec(*args, &matcher_block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def error_block
|
23
|
+
@error || lambda{|api_value| "non-matching value (got: #{api_value})" }
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_message
|
27
|
+
error_block.call(api_value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def matches?
|
31
|
+
@match.call(api_value)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def error(&block)
|
36
|
+
@error = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def match(&block)
|
40
|
+
@match = block
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'matcher'
|
2
|
+
|
3
|
+
class MatcherContainer
|
4
|
+
extend Enumerable
|
5
|
+
|
6
|
+
def each
|
7
|
+
matchers.each do |matcher|
|
8
|
+
yield matcher
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_from_scenario(scenario_value)
|
13
|
+
return nil unless scenario_value =~ /^([A-Z_]+)(\(.*\))?$/
|
14
|
+
matcher, args = matchers[$1], $2
|
15
|
+
args = eval("[#{args[1..-2]}]") if args
|
16
|
+
return nil unless matcher
|
17
|
+
[matcher, args]
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](name)
|
21
|
+
matchers[name.to_s.upcase]
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(name, value)
|
25
|
+
matchers[name.to_s.upcase] = value
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def matchers
|
30
|
+
@matchers ||= {}
|
31
|
+
end
|
32
|
+
end
|
data/lib/usagi/rspec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
RSpec::Matchers.define :usagi_scenario do |*api_declaration|
|
2
|
+
description do
|
3
|
+
api_declaration.first
|
4
|
+
end
|
5
|
+
|
6
|
+
match do |data|
|
7
|
+
api_scenario, api_opts = *api_declaration
|
8
|
+
api_opts = Usagi.suite_options.deep_merge(api_opts || {})
|
9
|
+
current_context = @matcher_execution_context.class.metadata[:description]
|
10
|
+
|
11
|
+
file_path = @matcher_execution_context.class.metadata[:file_path].gsub(/\.rb$/, '.yml')
|
12
|
+
raise "missing scenario file: #{file_path}" unless File.exist?(file_path)
|
13
|
+
scenario = YAML.load_file(file_path)
|
14
|
+
raise "missing context: #{current_context}" unless scenario[current_context]
|
15
|
+
raise "missing scenario: #{current_context}:#{api_scenario}" unless scenario[current_context][api_scenario]
|
16
|
+
scenario = JSON.parse(scenario[current_context][api_scenario].to_json)
|
17
|
+
protocol, path = scenario['query'].split(' ')
|
18
|
+
query = URI.parse(path).query
|
19
|
+
path = URI.parse(path).path
|
20
|
+
query = Rack::Utils.parse_nested_query(query).merge(api_opts[:query] || {}).to_query
|
21
|
+
post_data = api_opts[:body]
|
22
|
+
headers = api_opts[:headers]
|
23
|
+
|
24
|
+
full_uri = URI::HTTP.build([
|
25
|
+
nil,
|
26
|
+
"localhost",
|
27
|
+
Usagi.port,
|
28
|
+
path,
|
29
|
+
query,
|
30
|
+
nil
|
31
|
+
]).to_s
|
32
|
+
data = %[curl --silent -X #{protocol} "#{full_uri}"]
|
33
|
+
data += %[ --data "#{post_data.to_query}"] if post_data && post_data.keys.length > 0
|
34
|
+
headers.each do |key, value|
|
35
|
+
data += %[ -H "#{key}: #{value}"]
|
36
|
+
end if headers
|
37
|
+
puts "REQ__#{data}__" if Usagi.options[:debug_requests]
|
38
|
+
data = `#{data}`
|
39
|
+
puts "RES__#{data}__" if Usagi.options[:debug_requests]
|
40
|
+
data = JSON.parse(data)
|
41
|
+
Usagi::ApiResponse.new(scenario['reply']) == data
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'matcher_container'
|
2
|
+
|
3
|
+
Usagi.define_matcher :any_value do
|
4
|
+
match do
|
5
|
+
true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Usagi.define_matcher :any_value_not_nil do
|
10
|
+
error do
|
11
|
+
"expected to be a non-nil value"
|
12
|
+
end
|
13
|
+
|
14
|
+
match do |api_value|
|
15
|
+
api_value != nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Usagi.define_matcher :range do |from, to|
|
20
|
+
error do |api_value|
|
21
|
+
"expected to be in range #{from}..#{to} (got: #{api_value})"
|
22
|
+
end
|
23
|
+
|
24
|
+
match do |api_value|
|
25
|
+
api_value >= from && api_value <= to
|
26
|
+
end
|
27
|
+
end
|
data/usagi.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'usagi'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.licenses = ['MIT']
|
6
|
+
s.summary = '兎 - Usagi is a functional test suite for rails APIs'
|
7
|
+
s.homepage = 'https://github.com/savemysmartphone/usagi'
|
8
|
+
s.description = 'Functional test suite for rails APIs'
|
9
|
+
s.authors = ["Arnaud 'red' Rouyer", "Alice Clavel"]
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.executables << 'usagi'
|
13
|
+
s.require_path = 'lib'
|
14
|
+
s.required_ruby_version = '>= 2.0.0'
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: usagi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arnaud 'red' Rouyer
|
8
|
+
- Alice Clavel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-09-03 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Functional test suite for rails APIs
|
15
|
+
email:
|
16
|
+
executables:
|
17
|
+
- usagi
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".gitignore"
|
22
|
+
- Gemfile
|
23
|
+
- Gemfile.lock
|
24
|
+
- LICENSE
|
25
|
+
- README.md
|
26
|
+
- bin/usagi
|
27
|
+
- lib/usagi.rb
|
28
|
+
- lib/usagi/api_response.rb
|
29
|
+
- lib/usagi/matcher.rb
|
30
|
+
- lib/usagi/matcher_container.rb
|
31
|
+
- lib/usagi/rspec.rb
|
32
|
+
- lib/usagi/usagi_matchers.rb
|
33
|
+
- usagi.gemspec
|
34
|
+
homepage: https://github.com/savemysmartphone/usagi
|
35
|
+
licenses:
|
36
|
+
- MIT
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.0.0
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.4.6
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: "兎 - Usagi is a functional test suite for rails APIs"
|
58
|
+
test_files: []
|