jsontest 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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +21 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +343 -0
  8. data/Rakefile +18 -0
  9. data/features/equivalence.feature +286 -0
  10. data/features/files.feature +89 -0
  11. data/features/inclusion.feature +154 -0
  12. data/features/memory.feature +221 -0
  13. data/features/paths.feature +74 -0
  14. data/features/sizes.feature +38 -0
  15. data/features/step_definitions/steps.rb +7 -0
  16. data/features/support/env.rb +9 -0
  17. data/features/types.feature +24 -0
  18. data/gemfiles/rspec2.gemfile +10 -0
  19. data/gemfiles/rspec3.gemfile +10 -0
  20. data/jsontest.gemspec +22 -0
  21. data/lib/jsontest.rb +14 -0
  22. data/lib/jsontest/configuration.rb +30 -0
  23. data/lib/jsontest/cucumber.rb +95 -0
  24. data/lib/jsontest/errors.rb +46 -0
  25. data/lib/jsontest/exclusion.rb +26 -0
  26. data/lib/jsontest/helpers.rb +62 -0
  27. data/lib/jsontest/matchers.rb +33 -0
  28. data/lib/jsontest/matchers/be_json_eql.rb +66 -0
  29. data/lib/jsontest/matchers/have_json_path.rb +32 -0
  30. data/lib/jsontest/matchers/have_json_size.rb +39 -0
  31. data/lib/jsontest/matchers/have_json_type.rb +52 -0
  32. data/lib/jsontest/matchers/include_json.rb +63 -0
  33. data/lib/jsontest/memory.rb +19 -0
  34. data/lib/jsontest/messages.rb +8 -0
  35. data/spec/jsontest/configuration_spec.rb +62 -0
  36. data/spec/jsontest/helpers_spec.rb +111 -0
  37. data/spec/jsontest/matchers/be_json_eql_spec.rb +111 -0
  38. data/spec/jsontest/matchers/have_json_path_spec.rb +29 -0
  39. data/spec/jsontest/matchers/have_json_size_spec.rb +61 -0
  40. data/spec/jsontest/matchers/have_json_type_spec.rb +92 -0
  41. data/spec/jsontest/matchers/include_json_spec.rb +96 -0
  42. data/spec/jsontest/matchers_spec.rb +77 -0
  43. data/spec/jsontest/memory_spec.rb +32 -0
  44. data/spec/spec_helper.rb +23 -0
  45. data/spec/support/files/one.json +1 -0
  46. data/spec/support/files/project/one.json +1 -0
  47. data/spec/support/files/project/two.json +18 -0
  48. data/spec/support/files/project/version/one.json +1 -0
  49. data/spec/support/files/project/version/two.json +3 -0
  50. data/spec/support/files/two.json +24 -0
  51. metadata +182 -0
@@ -0,0 +1,33 @@
1
+ require "jsontest/matchers/be_json_eql"
2
+ require "jsontest/matchers/include_json"
3
+ require "jsontest/matchers/have_json_path"
4
+ require "jsontest/matchers/have_json_type"
5
+ require "jsontest/matchers/have_json_size"
6
+
7
+ module JsonTest
8
+ module Matchers
9
+ def be_json_eql(json = nil)
10
+ JsonTest::Matchers::BeJsonEql.new(json)
11
+ end
12
+
13
+ def include_json(json = nil)
14
+ JsonTest::Matchers::IncludeJson.new(json)
15
+ end
16
+
17
+ def have_json_path(path)
18
+ JsonTest::Matchers::HaveJsonPath.new(path)
19
+ end
20
+
21
+ def have_json_type(type)
22
+ JsonTest::Matchers::HaveJsonType.new(type)
23
+ end
24
+
25
+ def have_json_size(size)
26
+ JsonTest::Matchers::HaveJsonSize.new(size)
27
+ end
28
+ end
29
+ end
30
+
31
+ RSpec.configure do |config|
32
+ config.include JsonTest::Matchers
33
+ end
@@ -0,0 +1,66 @@
1
+ module JsonTest
2
+ module Matchers
3
+ class BeJsonEql
4
+ include JsonTest::Helpers
5
+ include JsonTest::Exclusion
6
+ include JsonTest::Messages
7
+
8
+ attr_reader :expected, :actual
9
+
10
+ def diffable?
11
+ true
12
+ end
13
+
14
+ def initialize(expected_json = nil)
15
+ @expected_json = expected_json
16
+ @path = nil
17
+ end
18
+
19
+ def matches?(actual_json)
20
+ raise "Expected equivalent JSON not provided" if @expected_json.nil?
21
+
22
+ @actual, @expected = scrub(actual_json, @path), scrub(@expected_json)
23
+ @actual == @expected
24
+ end
25
+
26
+ def at_path(path)
27
+ @path = path
28
+ self
29
+ end
30
+
31
+ def to_file(path)
32
+ @expected_json = load_json(path)
33
+ self
34
+ end
35
+
36
+ def excluding(*keys)
37
+ excluded_keys.merge(keys.map(&:to_s))
38
+ self
39
+ end
40
+
41
+ def including(*keys)
42
+ excluded_keys.subtract(keys.map(&:to_s))
43
+ self
44
+ end
45
+
46
+ def failure_message
47
+ message_with_path("Expected equivalent JSON")
48
+ end
49
+ alias :failure_message_for_should :failure_message
50
+
51
+ def failure_message_when_negated
52
+ message_with_path("Expected inequivalent JSON")
53
+ end
54
+ alias :failure_message_for_should_not :failure_message_when_negated
55
+
56
+ def description
57
+ message_with_path("equal JSON")
58
+ end
59
+
60
+ private
61
+ def scrub(json, path = nil)
62
+ generate_normalized_json(exclude_keys(parse_json(json, path))).chomp + "\n"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ module JsonTest
2
+ module Matchers
3
+ class HaveJsonPath
4
+ include JsonTest::Helpers
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def matches?(json)
11
+ parse_json(json, @path)
12
+ true
13
+ rescue JsonTest::MissingPath
14
+ false
15
+ end
16
+
17
+ def failure_message
18
+ %(Expected JSON path "#{@path}")
19
+ end
20
+ alias :failure_message_for_should :failure_message
21
+
22
+ def failure_message_when_negated
23
+ %(Expected no JSON path "#{@path}")
24
+ end
25
+ alias :failure_message_for_should_not :failure_message_when_negated
26
+
27
+ def description
28
+ %(have JSON path "#{@path}")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ module JsonTest
2
+ module Matchers
3
+ class HaveJsonSize
4
+ include JsonTest::Helpers
5
+ include JsonTest::Messages
6
+
7
+ def initialize(size)
8
+ @expected = size
9
+ @path = nil
10
+ end
11
+
12
+ def matches?(json)
13
+ ruby = parse_json(json, @path)
14
+ raise EnumerableExpected.new(ruby) unless Enumerable === ruby
15
+ @actual = ruby.size
16
+ @actual == @expected
17
+ end
18
+
19
+ def at_path(path)
20
+ @path = path
21
+ self
22
+ end
23
+
24
+ def failure_message
25
+ message_with_path("Expected JSON value size to be #{@expected}, got #{@actual}")
26
+ end
27
+ alias :failure_message_for_should :failure_message
28
+
29
+ def failure_message_when_negated
30
+ message_with_path("Expected JSON value size to not be #{@expected}, got #{@actual}")
31
+ end
32
+ alias :failure_message_for_should_not :failure_message_when_negated
33
+
34
+ def description
35
+ message_with_path(%(have JSON size "#{@expected}"))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ module JsonTest
2
+ module Matchers
3
+ class HaveJsonType
4
+ include JsonTest::Helpers
5
+ include JsonTest::Messages
6
+
7
+ def initialize(type)
8
+ @classes = type_to_classes(type)
9
+ @path = nil
10
+ end
11
+
12
+ def matches?(json)
13
+ @ruby = parse_json(json, @path)
14
+ @classes.any? { |c| c === @ruby }
15
+ end
16
+
17
+ def at_path(path)
18
+ @path = path
19
+ self
20
+ end
21
+
22
+ def failure_message
23
+ message_with_path("Expected JSON value type to be #{@classes.join(", ")}, got #{@ruby.class}")
24
+ end
25
+ alias :failure_message_for_should :failure_message
26
+
27
+ def failure_message_when_negated
28
+ message_with_path("Expected JSON value type to not be #{@classes.join(", ")}, got #{@ruby.class}")
29
+ end
30
+ alias :failure_message_for_should_not :failure_message_when_negated
31
+
32
+ def description
33
+ message_with_path(%(have JSON type "#{@classes.join(", ")}"))
34
+ end
35
+
36
+ private
37
+ def type_to_classes(type)
38
+ case type
39
+ when Class then [type]
40
+ when Array then type.map { |t| type_to_classes(t) }.flatten
41
+ else
42
+ case type.to_s.downcase
43
+ when "boolean" then [TrueClass, FalseClass]
44
+ when "object" then [Hash]
45
+ when "nil", "null" then [NilClass]
46
+ else [Module.const_get(type.to_s.capitalize)]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,63 @@
1
+ module JsonTest
2
+ module Matchers
3
+ class IncludeJson
4
+ include JsonTest::Helpers
5
+ include JsonTest::Exclusion
6
+ include JsonTest::Messages
7
+
8
+ def initialize(expected_json = nil)
9
+ @expected_json = expected_json
10
+ @path = nil
11
+ end
12
+
13
+ def matches?(actual_json)
14
+ raise "Expected included JSON not provided" if @expected_json.nil?
15
+
16
+ @actual_json = actual_json
17
+
18
+ actual = parse_json(actual_json, @path)
19
+ expected = exclude_keys(parse_json(@expected_json))
20
+ case actual
21
+ when Hash then actual.values.map { |v| exclude_keys(v) }.include?(expected)
22
+ when Array then actual.map { |e| exclude_keys(e) }.include?(expected)
23
+ when String then actual.include?(expected)
24
+ else false
25
+ end
26
+ end
27
+
28
+ def at_path(path)
29
+ @path = path
30
+ self
31
+ end
32
+
33
+ def from_file(path)
34
+ @expected_json = load_json(path)
35
+ self
36
+ end
37
+
38
+ def excluding(*keys)
39
+ excluded_keys.merge(keys.map(&:to_s))
40
+ self
41
+ end
42
+
43
+ def including(*keys)
44
+ excluded_keys.subtract(keys.map(&:to_s))
45
+ self
46
+ end
47
+
48
+ def failure_message
49
+ message_with_path("Expected #{@actual_json} to include #{@expected_json}")
50
+ end
51
+ alias :failure_message_for_should :failure_message
52
+
53
+ def failure_message_when_negated
54
+ message_with_path("Expected #{@actual_json} to not include #{@expected_json}")
55
+ end
56
+ alias :failure_message_for_should_not :failure_message_when_negated
57
+
58
+ def description
59
+ message_with_path("include JSON")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ module JsonTest
2
+ module Memory
3
+ def memory
4
+ @memory ||= {}
5
+ end
6
+
7
+ def memorize(key, value)
8
+ memory[key.to_sym] = value
9
+ end
10
+
11
+ def remember(json)
12
+ memory.empty? ? json : json % memory
13
+ end
14
+
15
+ def forget
16
+ memory.clear
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module JsonTest
2
+ module Messages
3
+ def message_with_path(message)
4
+ message << %( at path "#{@path}") if @path
5
+ message
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,62 @@
1
+ require "spec_helper"
2
+
3
+ describe JsonTest::Configuration do
4
+ it "excludes id and timestamps by default" do
5
+ JsonTest.excluded_keys.should eq ["id", "created_at", "updated_at"]
6
+ end
7
+
8
+ it "excludes custom keys" do
9
+ JsonTest.exclude_keys("token")
10
+ JsonTest.excluded_keys.should eq ["token"]
11
+ end
12
+
13
+ it "excludes custom keys via setter" do
14
+ JsonTest.excluded_keys = ["token"]
15
+ JsonTest.excluded_keys.should eq ["token"]
16
+ end
17
+
18
+ it "excludes custom keys via block" do
19
+ JsonTest.configure { |c| c.exclude_keys("token") }
20
+ JsonTest.excluded_keys.should eq ["token"]
21
+ end
22
+
23
+ it "excludes custom keys via block setter" do
24
+ JsonTest.configure { |c| c.excluded_keys = ["token"] }
25
+ JsonTest.excluded_keys.should eq ["token"]
26
+ end
27
+
28
+ it "excludes custom keys via instance-evaluated block" do
29
+ JsonTest.configure{ exclude_keys("token") }
30
+ JsonTest.excluded_keys.should eq ["token"]
31
+ end
32
+
33
+ it "ensures its excluded keys are strings" do
34
+ JsonTest.exclude_keys(:token)
35
+ JsonTest.excluded_keys.should eq ["token"]
36
+ end
37
+
38
+ it "ensures its excluded keys are unique" do
39
+ JsonTest.exclude_keys("token", :token)
40
+ JsonTest.excluded_keys.should eq ["token"]
41
+ end
42
+
43
+ it "resets its excluded keys" do
44
+ original = JsonTest.excluded_keys
45
+
46
+ JsonTest.exclude_keys("token")
47
+ JsonTest.excluded_keys.should_not eq original
48
+
49
+ JsonTest.reset
50
+ JsonTest.excluded_keys.should eq original
51
+ end
52
+
53
+ it "resets its directory" do
54
+ JsonTest.directory.should be_nil
55
+
56
+ JsonTest.directory = "/"
57
+ JsonTest.directory.should_not be_nil
58
+
59
+ JsonTest.reset
60
+ JsonTest.directory.should be_nil
61
+ end
62
+ end
@@ -0,0 +1,111 @@
1
+ require "spec_helper"
2
+
3
+ describe JsonTest::Helpers do
4
+ include described_class
5
+
6
+ context "parse_json" do
7
+ it "parses JSON documents" do
8
+ parse_json(%({"json":["spec"]})).should eq({"json" => ["spec"]})
9
+ end
10
+
11
+ it "parses JSON values" do
12
+ parse_json(%("jsontest")).should eq "jsontest"
13
+ end
14
+
15
+ it "raises a parser error for invalid JSON" do
16
+ expect{ parse_json("jsontest") }.to raise_error(MultiJson::DecodeError)
17
+ end
18
+
19
+ it "parses at a path if given" do
20
+ json = %({"json":["spec"]})
21
+ parse_json(json, "json").should eq ["spec"]
22
+ parse_json(json, "json/0").should eq "spec"
23
+ end
24
+
25
+ it "raises an error for a missing path" do
26
+ json = %({"json":["spec"]})
27
+ %w(spec json/1).each do |path|
28
+ expect{ parse_json(json, path) }.to raise_error(JsonTest::MissingPath)
29
+ end
30
+ end
31
+
32
+ it "parses at a numeric string path" do
33
+ json = %({"1":"two"})
34
+ parse_json(json, "1").should eq "two"
35
+ end
36
+ end
37
+
38
+ context "normalize_json" do
39
+ it "normalizes a JSON document" do
40
+ normalized = <<-JSON
41
+ {
42
+ "json": [
43
+ "spec"
44
+ ]
45
+ }
46
+ JSON
47
+ normalize_json(%({"json":["spec"]})).should eq normalized.chomp
48
+ end
49
+
50
+ it "normalizes at a path" do
51
+ normalize_json(%({"json":["spec"]}), "json/0").should eq %("spec")
52
+ end
53
+
54
+ it "accepts a JSON value" do
55
+ normalize_json(%("jsontest")).should eq %("jsontest")
56
+ end
57
+
58
+ it "normalizes JSON values" do
59
+ normalize_json(%(1e+1)).should eq %(10.0)
60
+ end
61
+ end
62
+
63
+ context "generate_normalized_json" do
64
+ it "generates a normalized JSON document" do
65
+ normalized = <<-JSON
66
+ {
67
+ "json": [
68
+ "spec"
69
+ ]
70
+ }
71
+ JSON
72
+ generate_normalized_json({"json" => ["spec"]}).should eq normalized.chomp
73
+ end
74
+
75
+ it "generates a normalized JSON value" do
76
+ generate_normalized_json(nil).should eq %(null)
77
+ end
78
+ end
79
+
80
+ context "load_json_file" do
81
+ it "raises an error when no directory is set" do
82
+ expect{ load_json("one.json") }.to raise_error(JsonTest::MissingDirectory)
83
+ end
84
+
85
+ it "returns JSON when the file exists" do
86
+ JsonTest.directory = files_path
87
+ load_json("one.json").should eq %({"value":"from_file"})
88
+ end
89
+
90
+ it "ignores extra slashes" do
91
+ JsonTest.directory = "/#{files_path}/"
92
+ load_json("one.json").should eq %({"value":"from_file"})
93
+ end
94
+
95
+ it "raises an error when the file doesn't exist" do
96
+ JsonTest.directory = files_path
97
+ expect{ load_json("bogus.json") }.to raise_error(JsonTest::MissingFile)
98
+ end
99
+
100
+ it "raises an error when the directory doesn't exist" do
101
+ JsonTest.directory = "#{files_path}_bogus"
102
+ expect{ load_json("one.json") }.to raise_error(JsonTest::MissingFile)
103
+ end
104
+
105
+ it "finds nested files" do
106
+ JsonTest.directory = files_path
107
+ load_json("project/one.json").should eq %({"nested":"inside_folder"})
108
+ load_json("project/version/one.json").should eq %({"nested":"deeply"})
109
+ end
110
+ end
111
+ end