webspicy 0.1.0.pre.rc1

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +7 -0
  5. data/Rakefile +11 -0
  6. data/examples/restful/Gemfile +5 -0
  7. data/examples/restful/Gemfile.lock +69 -0
  8. data/examples/restful/Rakefile +21 -0
  9. data/examples/restful/app.rb +32 -0
  10. data/examples/restful/webspicy/schema.fio +4 -0
  11. data/examples/restful/webspicy/todo/getTodo.yml +52 -0
  12. data/examples/restful/webspicy/todo/getTodos.yml +39 -0
  13. data/lib/webspicy/checker.rb +26 -0
  14. data/lib/webspicy/client/http_client.rb +70 -0
  15. data/lib/webspicy/client.rb +20 -0
  16. data/lib/webspicy/configuration.rb +168 -0
  17. data/lib/webspicy/formaldoc.fio +47 -0
  18. data/lib/webspicy/resource/service/invocation.rb +158 -0
  19. data/lib/webspicy/resource/service/test_case.rb +74 -0
  20. data/lib/webspicy/resource/service.rb +49 -0
  21. data/lib/webspicy/resource.rb +43 -0
  22. data/lib/webspicy/scope.rb +113 -0
  23. data/lib/webspicy/tester/asserter.rb +94 -0
  24. data/lib/webspicy/tester/assertions.rb +103 -0
  25. data/lib/webspicy/tester.rb +96 -0
  26. data/lib/webspicy/version.rb +8 -0
  27. data/lib/webspicy.rb +112 -0
  28. data/spec/unit/resource/service/test_dress_params.rb +34 -0
  29. data/spec/unit/resource/test_instantiate_url.rb +20 -0
  30. data/spec/unit/resource/test_url_placeholders.rb +16 -0
  31. data/spec/unit/scope/test_each_resource.rb +59 -0
  32. data/spec/unit/scope/test_each_service.rb +51 -0
  33. data/spec/unit/scope/test_to_real_url.rb +75 -0
  34. data/spec/unit/spec_helper.rb +28 -0
  35. data/spec/unit/test_configuration.rb +84 -0
  36. data/spec/unit/tester/test_assertions.rb +108 -0
  37. data/tasks/test.rake +27 -0
  38. metadata +149 -0
@@ -0,0 +1,96 @@
1
+ module Webspicy
2
+ class Tester
3
+
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+ attr_reader :config
8
+
9
+ def call
10
+ Webspicy.with_scope_for(config) do |scope|
11
+ client = scope.get_client
12
+ scope.each_resource do |resource|
13
+ scope.each_service(resource) do |service|
14
+ RSpec.describe "#{service.method} `#{resource.url}`" do
15
+ scope.each_example(service) do |test_case|
16
+ describe test_case.description do
17
+
18
+ before(:each) do
19
+ client.before(test_case, service, resource)
20
+ end
21
+
22
+ subject do
23
+ client.call(test_case, service, resource)
24
+ end
25
+
26
+ it 'can be invoked successfuly' do
27
+ expect(subject.done?).to eq(true)
28
+ end
29
+
30
+ it 'meets the status and content type specification' do
31
+ expect(subject.expected_status_unmet).to(be_nil)
32
+ expect(subject.expected_content_type_unmet).to(be_nil)
33
+ end
34
+
35
+ it 'has the expected response headers' do
36
+ expect(subject.expected_headers_unmet).to(be_nil)
37
+ end if test_case.has_expected_headers?
38
+
39
+ it 'meets the output data schema' do
40
+ expect(subject.expected_schema_unmet).to(be_nil)
41
+ end
42
+
43
+ it 'meets all assertions' do
44
+ expect(subject.assertions_unmet).to(be_nil)
45
+ end if test_case.has_assertions?
46
+
47
+ end
48
+ end # service.examples
49
+
50
+ scope.each_counterexamples(service) do |test_case|
51
+ describe test_case.description do
52
+
53
+ before(:each) do
54
+ client.before(test_case, service, resource)
55
+ end
56
+
57
+ subject do
58
+ client.call(test_case, service, resource)
59
+ end
60
+
61
+ it 'can be invoked successfuly' do
62
+ expect(subject.done?).to eq(true)
63
+ end
64
+
65
+ it 'meets the status and content type specification' do
66
+ expect(subject.expected_status_unmet).to(be_nil)
67
+ expect(subject.expected_content_type_unmet).to(be_nil)
68
+ end
69
+
70
+ it 'has the expected response headers' do
71
+ expect(subject.expected_headers_unmet).to(be_nil)
72
+ end if test_case.has_expected_headers?
73
+
74
+ it 'meets the output data schema' do
75
+ expect(subject.expected_schema_unmet).to(be_nil)
76
+ end
77
+
78
+ it 'meets all assertions' do
79
+ expect(subject.assertions_unmet).to(be_nil)
80
+ end if test_case.has_assertions?
81
+
82
+ it 'has expected error' do
83
+ expect(subject.expected_error_unmet).to(be_nil)
84
+ end if test_case.has_expected_error?
85
+
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ RSpec::Core::Runner.run config.rspec_options
92
+ end
93
+ end
94
+
95
+ end # class Tester
96
+ end # module Webspicy
@@ -0,0 +1,8 @@
1
+ module Webspicy
2
+ module Version
3
+ TINY = "0-rc1"
4
+ MINOR = 1
5
+ MAJOR = 0
6
+ end
7
+ VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
+ end
data/lib/webspicy.rb ADDED
@@ -0,0 +1,112 @@
1
+ require 'http'
2
+ require 'json'
3
+ require 'path'
4
+ require 'finitio'
5
+ require 'logger'
6
+ require 'ostruct'
7
+ require 'yaml'
8
+ require 'rspec'
9
+ module Webspicy
10
+
11
+ ###
12
+ ### Load library
13
+ ###
14
+
15
+ require 'webspicy/configuration'
16
+ require 'webspicy/scope'
17
+ require 'webspicy/client'
18
+ require 'webspicy/client/http_client'
19
+ require 'webspicy/resource'
20
+ require 'webspicy/checker'
21
+ require 'webspicy/tester'
22
+ require 'webspicy/tester/assertions'
23
+ require 'webspicy/tester/asserter'
24
+
25
+ ###
26
+ ### About folders
27
+ ###
28
+
29
+ ROOT_FOLDER = Path.backfind('.[Gemfile]')
30
+
31
+ EXAMPLES_FOLDER = ROOT_FOLDER/'examples'
32
+
33
+ ###
34
+ ### About formal doc and resources defined there
35
+ ###
36
+
37
+ FORMALDOC = Finitio::DEFAULT_SYSTEM.parse (Path.dir/"webspicy/formaldoc.fio").read
38
+
39
+ def resource(raw, file = nil)
40
+ FORMALDOC["Resource"].dress(raw)
41
+ end
42
+ module_function :resource
43
+
44
+ def service(raw)
45
+ FORMALDOC["Service"].dress(raw)
46
+ end
47
+ module_function :service
48
+
49
+ def test_case(raw)
50
+ FORMALDOC["TestCase"].dress(raw)
51
+ end
52
+ module_function :test_case
53
+
54
+ #
55
+ # Yields a Scope instance for the configuration passed as parameter.
56
+ #
57
+ # This method makes sure that the scope will also be accessible for
58
+ # Finitio world schema parsing/dressing. Given that some global state
59
+ # is required (see "Schema" ADT, the dresser in particular, which calls
60
+ # `schema` later), the scope is put as a thread-local variable...
61
+ #
62
+ def with_scope_for(config)
63
+ scope = set_current_scope(Scope.new(config))
64
+ yield scope
65
+ set_current_scope(nil)
66
+ end
67
+ module_function :with_scope_for
68
+
69
+ #
70
+ # Sets the current scope.
71
+ #
72
+ # See `with_scope_for`
73
+ #
74
+ def set_current_scope(scope)
75
+ Thread.current[:webspicy_scope] = scope
76
+ end
77
+ module_function :set_current_scope
78
+
79
+ #
80
+ # Parses a webservice schema (typically input or output) in the context
81
+ # of the current scope previously installed using `with_scope_for`.
82
+ #
83
+ # If no scope has previously been installed, Finitio's default system
84
+ # is used instead of another schema.
85
+ #
86
+ def schema(fio)
87
+ if scope = Thread.current[:webspicy_scope]
88
+ scope.parse_schema(fio)
89
+ else
90
+ Finitio::DEFAULT_SYSTEM.parse(fio)
91
+ end
92
+ end
93
+ module_function :schema
94
+
95
+ ###
96
+ ### Logging facade
97
+ ###
98
+
99
+ LOGGER = ::Logger.new(STDOUT)
100
+ LOGGER.level = Logger.const_get(ENV['LOG_LEVEL'] || 'WARN')
101
+
102
+ def info(*args, &bl)
103
+ LOGGER && LOGGER.info(*args, &bl)
104
+ end
105
+ module_function :info
106
+
107
+ def debug(*args, &bl)
108
+ LOGGER && LOGGER.debug(*args, &bl)
109
+ end
110
+ module_function :debug
111
+
112
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ module Webspicy
3
+ class Resource
4
+ describe Service, "dress_params" do
5
+
6
+ it 'symbolizes keys' do
7
+ service = Webspicy.service({
8
+ method: "GET",
9
+ description: "Test service",
10
+ preconditions: "Foo",
11
+ input_schema: "{ id: Integer }",
12
+ output_schema: "{}",
13
+ error_schema: "{}"
14
+ })
15
+ params = service.dress_params("id" => 1247)
16
+ expect(params).to eq(id: 1247)
17
+ end
18
+
19
+ it 'supports an array' do
20
+ service = Webspicy.service({
21
+ method: "GET",
22
+ description: "Test service",
23
+ preconditions: "Foo",
24
+ input_schema: "[{ id: Integer }]",
25
+ output_schema: "{}",
26
+ error_schema: "{}"
27
+ })
28
+ params = service.dress_params([{"id" => 1247}])
29
+ expect(params).to eq([{id: 1247}])
30
+ end
31
+
32
+ end
33
+ end # class Resource
34
+ end # module Webspicy
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+ module Webspicy
3
+ describe Resource, "instantiate_url" do
4
+
5
+ it 'does nothing when the url has no placeholder' do
6
+ r = Resource.new(url: "/test/a/url")
7
+ url, params = r.instantiate_url(foo: "bar")
8
+ expect(url).to eq("/test/a/url")
9
+ expect(params).to eq(foo: "bar")
10
+ end
11
+
12
+ it 'instantiates placeholders and strips corresponding params' do
13
+ r = Resource.new(url: "/test/{foo}/url")
14
+ url, params = r.instantiate_url(foo: "bar", baz: "coz")
15
+ expect(url).to eq("/test/bar/url")
16
+ expect(params).to eq(baz: "coz")
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+ module Webspicy
3
+ describe Resource, "url_placeholders" do
4
+
5
+ it 'returns an empty array on none' do
6
+ r = Resource.new(url: "/test/a/url")
7
+ expect(r.url_placeholders).to eq([])
8
+ end
9
+
10
+ it 'returns all placeholders' do
11
+ r = Resource.new(url: "/test/{foo}/url/{bar}")
12
+ expect(r.url_placeholders).to eq([:foo, :bar])
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ module Webspicy
3
+ describe Scope, 'each_resource' do
4
+
5
+ with_scope_management
6
+
7
+ let(:scope) {
8
+ Scope.new(configuration)
9
+ }
10
+
11
+ subject {
12
+ scope.each_resource.to_a
13
+ }
14
+
15
+ context 'without any filter' do
16
+
17
+ let(:configuration) {
18
+ Configuration.new{|c|
19
+ c.add_folder restful_folder
20
+ }
21
+ }
22
+
23
+ it 'returns all files' do
24
+ expect(subject.size).to eql(restful_folder.glob('**/*.yml').size)
25
+ end
26
+ end
27
+
28
+ context 'with a file filter as a proc' do
29
+
30
+ let(:configuration) {
31
+ Configuration.new{|c|
32
+ c.add_folder restful_folder
33
+ c.file_filter = ->(f) {
34
+ f.basename.to_s == "getTodo.yml"
35
+ }
36
+ }
37
+ }
38
+
39
+ it 'returns only that file' do
40
+ expect(subject.size).to eql(1)
41
+ end
42
+ end
43
+
44
+ context 'with a file filter as a Regex' do
45
+
46
+ let(:configuration) {
47
+ Configuration.new{|c|
48
+ c.add_folder restful_folder
49
+ c.file_filter = /getTodo.yml/
50
+ }
51
+ }
52
+
53
+ it 'returns only that file' do
54
+ expect(subject.size).to eql(1)
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ module Webspicy
3
+ describe Scope, 'each_service' do
4
+
5
+ with_scope_management
6
+
7
+ let(:scope) {
8
+ Scope.new(configuration)
9
+ }
10
+
11
+ let(:resource) {
12
+ scope.each_resource.first
13
+ }
14
+
15
+ subject {
16
+ scope.each_service(resource).to_a
17
+ }
18
+
19
+ context 'without any filter' do
20
+
21
+ let(:configuration) {
22
+ Configuration.new{|c|
23
+ c.add_folder restful_folder
24
+ c.file_filter = /getTodo.yml/
25
+ }
26
+ }
27
+
28
+ it 'returns all services' do
29
+ expect(subject.size).to eql(1)
30
+ end
31
+ end
32
+
33
+ context 'with a service filter as a proc' do
34
+
35
+ let(:configuration) {
36
+ Configuration.new{|c|
37
+ c.add_folder restful_folder
38
+ c.file_filter = /getTodo.yml/
39
+ c.service_filter = ->(s) {
40
+ s.method == "POST"
41
+ }
42
+ }
43
+ }
44
+
45
+ it 'returns nothing' do
46
+ expect(subject.size).to eql(0)
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ module Webspicy
3
+ describe Scope, 'to_real_url' do
4
+
5
+ let(:scope) {
6
+ Scope.new(configuration)
7
+ }
8
+
9
+ context 'when no host at all' do
10
+
11
+ let(:configuration) {
12
+ Configuration.new
13
+ }
14
+
15
+ it 'does nothing on absolute URLs already' do
16
+ url = 'http://127.0.0.1:4567/todo'
17
+ got = scope.to_real_url(url)
18
+ expect(got).to eql(url)
19
+ end
20
+
21
+ it 'fails on relative URLs' do
22
+ expect(->(){
23
+ scope.to_real_url("/todo")
24
+ }).to raise_error(/Unable to resolve `\/todo`/)
25
+ end
26
+ end
27
+
28
+ context 'with a static host' do
29
+
30
+ let(:configuration) {
31
+ Configuration.new do |c|
32
+ c.host = "http://127.0.0.1:4568"
33
+ end
34
+ }
35
+
36
+ it 'does nothing on absolute URLs already' do
37
+ url = 'http://127.0.0.1:4567/todo'
38
+ got = scope.to_real_url(url)
39
+ expect(got).to eql(url)
40
+ end
41
+
42
+ it 'resolves relative URLs as expected' do
43
+ url = '/todo'
44
+ got = scope.to_real_url(url)
45
+ expect(got).to eql("http://127.0.0.1:4568/todo")
46
+ end
47
+
48
+ end
49
+
50
+ context 'with a dynamic host resolver' do
51
+
52
+ let(:configuration) {
53
+ Configuration.new do |c|
54
+ c.host = ->(url) {
55
+ "http://127.0.0.1:4568#{url}"
56
+ }
57
+ end
58
+ }
59
+
60
+ it 'resolves absolute URLs' do
61
+ url = 'http://127.0.0.1:4567/todo'
62
+ got = scope.to_real_url(url)
63
+ expect(got).to eql("http://127.0.0.1:4568#{url}")
64
+ end
65
+
66
+ it 'resolves relative URLs as expected' do
67
+ url = '/todo'
68
+ got = scope.to_real_url(url)
69
+ expect(got).to eql("http://127.0.0.1:4568/todo")
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,28 @@
1
+ require 'rspec'
2
+ require 'webspicy'
3
+
4
+ module SpecHelper
5
+
6
+ def restful_folder
7
+ Webspicy::EXAMPLES_FOLDER/'restful/webspicy'
8
+ end
9
+
10
+ end
11
+
12
+ module ScopeManagement
13
+
14
+ def with_scope_management
15
+ before(:each) {
16
+ Webspicy.set_current_scope(scope)
17
+ }
18
+ after(:each) {
19
+ Webspicy.set_current_scope(nil)
20
+ }
21
+ end
22
+
23
+ end
24
+
25
+ RSpec.configure do |c|
26
+ c.include SpecHelper
27
+ c.extend ScopeManagement
28
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ module Webspicy
3
+ describe Configuration do
4
+
5
+ it 'yields itself at construction' do
6
+ seen = nil
7
+ Configuration.new do |c|
8
+ seen = c
9
+ end
10
+ expect(seen).to be_a(Configuration)
11
+ end
12
+
13
+ describe 'run_counterexamples' do
14
+
15
+ it 'is true by default' do
16
+ config = Configuration.new
17
+ expect(config.run_counterexamples?).to eql(true)
18
+ end
19
+
20
+ it 'implements backward compatibility with ROBUST env variable' do
21
+ ENV['ROBUST'] = 'no'
22
+ config = Configuration.new
23
+ expect(config.run_counterexamples?).to eql(false)
24
+
25
+ ENV['ROBUST'] = 'yes'
26
+ config = Configuration.new
27
+ expect(config.run_counterexamples?).to eql(true)
28
+ end
29
+
30
+ it 'ignores the environment is set explicitly' do
31
+ ENV['ROBUST'] = 'yes'
32
+ config = Configuration.new do |c|
33
+ c.run_counterexamples = false
34
+ end
35
+ expect(config.run_counterexamples?).to eql(false)
36
+ end
37
+ end
38
+
39
+ describe 'file_filter' do
40
+
41
+ it 'is nil by default' do
42
+ config = Configuration.new
43
+ expect(config.file_filter).to be_nil
44
+ end
45
+
46
+ it 'is implements backward compatibility with the RESOURCE env variable' do
47
+ ENV['RESOURCE'] = 'getTodo.yml'
48
+ config = Configuration.new
49
+ expect(config.file_filter).to be_a(Regexp)
50
+ end
51
+
52
+ it 'ignores the environment is set explicitly' do
53
+ ENV['RESOURCE'] = 'getTodo.yml'
54
+ config = Configuration.new do |c|
55
+ c.file_filter = nil
56
+ end
57
+ expect(config.file_filter).to be_nil
58
+ end
59
+ end
60
+
61
+ describe 'service_filter' do
62
+
63
+ it 'is nil by default' do
64
+ config = Configuration.new
65
+ expect(config.service_filter).to be_nil
66
+ end
67
+
68
+ it 'is implements backward compatibility with the RESOURCE env variable' do
69
+ ENV['METHOD'] = 'GET'
70
+ config = Configuration.new
71
+ expect(config.service_filter).to be_a(Proc)
72
+ end
73
+
74
+ it 'ignores the environment is set explicitly' do
75
+ ENV['METHOD'] = 'POST'
76
+ config = Configuration.new do |c|
77
+ c.service_filter = nil
78
+ end
79
+ expect(config.service_filter).to be_nil
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ module Webspicy
4
+ class Tester
5
+ describe Assertions do
6
+ include Assertions
7
+
8
+ public :extract_path
9
+
10
+ it 'has an extract_path helper' do
11
+ target = { foo: "Hello", bar: { foo: "Hello" }, baz: [{ foo: "world" }] }
12
+ expect(extract_path(target)).to be(target)
13
+ expect(extract_path(target, nil)).to be(target)
14
+ expect(extract_path(target, '')).to be(target)
15
+ expect(extract_path(target, 'foo')).to eql("Hello")
16
+ expect(extract_path(target, 'bar/foo')).to eql("Hello")
17
+ expect(extract_path(target, 'baz/0')).to eql({ foo: "world" })
18
+ expect(extract_path(target, 'baz/0/foo')).to eql("world")
19
+ end
20
+
21
+ it 'has an exists() assertion' do
22
+ expect(exists nil).to be(false)
23
+ expect(exists []).to be(true)
24
+ expect(exists [1]).to be(true)
25
+ expect(exists({ foo: [] }, 'foo')).to be(true)
26
+ expect(exists({ foo: {} }, 'foo')).to be(true)
27
+ expect(exists({ foo: {} }, 'foo/bar')).to be(false)
28
+ end
29
+
30
+ it 'has an notExists() assertion' do
31
+ expect(notExists nil).to be(true)
32
+ expect(notExists []).to be(false)
33
+ expect(notExists [1]).to be(false)
34
+ expect(notExists({ foo: [] }, 'foo')).to be(false)
35
+ expect(notExists({ foo: {} }, 'foo')).to be(false)
36
+ expect(notExists({ foo: {} }, 'foo/bar')).to be(true)
37
+ end
38
+
39
+ it 'has an empty() assertion' do
40
+ expect(empty []).to be(true)
41
+ expect(empty [1]).to be(false)
42
+ expect(empty({ foo: [] }, 'foo')).to be(true)
43
+ expect(empty({ foo: [1] }, 'foo')).to be(false)
44
+ end
45
+
46
+ it 'has a notEmpty() assertion' do
47
+ expect(notEmpty []).to be(false)
48
+ expect(notEmpty [1]).to be(true)
49
+ expect(notEmpty({ foo: [] }, 'foo')).to be(false)
50
+ expect(notEmpty({ foo: [1] }, 'foo')).to be(true)
51
+ end
52
+
53
+ it 'has an size() assertion' do
54
+ expect(size [], 0).to be(true)
55
+ expect(size [], 1).to be(false)
56
+ expect(size [12], 1).to be(true)
57
+ expect(size({ foo: [] }, 'foo', 0)).to be(true)
58
+ expect(size({ foo: [] }, 'foo', 1)).to be(false)
59
+ expect(size({ foo: ['bar'] }, 'foo', 1)).to be(true)
60
+ end
61
+
62
+ it 'has an idIn assertion' do
63
+ expect(idIn [{id: 1}, {id: 2}], [1, 2]).to be(true)
64
+ expect(idIn [{id: 1}, {id: 2}], [2, 1]).to be(true)
65
+ expect(idIn [{id: 1}, {id: 2}], [1, 3]).to be(false)
66
+ expect(idIn [{id: 1}, {id: 2}], [1]).to be(false)
67
+ expect(idIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1, 2])).to be(true)
68
+
69
+ expect(idIn({id: 1}, [1])).to be(true)
70
+ expect(idIn({id: 1}, [2])).to be(false)
71
+ end
72
+
73
+ it 'has an idNotIn assertion' do
74
+ expect(idNotIn [{id: 1}, {id: 2}], [3]).to be(true)
75
+ expect(idNotIn [{id: 1}, {id: 2}], [3, 4]).to be(true)
76
+ expect(idNotIn [{id: 1}, {id: 2}], [1]).to be(false)
77
+ expect(idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1])).to be(false)
78
+ expect(idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [3])).to be(true)
79
+
80
+ expect(idNotIn({id: 1}, [3])).to be(true)
81
+ expect(idNotIn({id: 1}, [1])).to be(false)
82
+ end
83
+
84
+ it 'has an idFD assertion' do
85
+ target = { foo: [
86
+ { id: 1, bar: "bar" },
87
+ { id: 2, bar: "baz" }
88
+ ] }
89
+ expect(idFD(target, 'foo', 1, bar: "bar")).to be(true)
90
+ expect(idFD(target, 'foo', 1, bar: "baz")).to be(false)
91
+ expect(idFD(target, 'foo', 1, baz: "boz")).to be(false)
92
+
93
+ target = { foo: { id: 1, bar: "bar" } }
94
+ expect(idFD(target, 'foo', 1, bar: "bar")).to be(true)
95
+ expect(idFD(target, 'foo', 1, bar: "baz")).to be(false)
96
+ expect(idFD(target, 'foo', 1, baz: "boz")).to be(false)
97
+ end
98
+
99
+ it 'has an pathFD assertion' do
100
+ target = { foo: { bar: "baz"} }
101
+ expect(pathFD(target, 'foo', bar: "baz")).to be(true)
102
+ expect(pathFD(target, 'foo', bar: "boz")).to be(false)
103
+ expect(pathFD(target, 'foo', boz: "biz")).to be(false)
104
+ end
105
+
106
+ end
107
+ end # class Tester
108
+ end # module Webspicy