-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bidirectional converter specs to test namespaces
Previously, we might have duplicated namespaces when converting the same string back and forth. With the latest change in #52, we're avoiding this error and are now adding respective test coverage for this phenomena.
- Loading branch information
Showing
3 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe 'equal_namespace_definitions matcher' do | ||
let(:xml) { Examples::Example2.xml } | ||
let(:xml2) { Examples::Example2.xml } | ||
|
||
it 'successfully compares two similar xml strings' do | ||
expect(xml).to have_equal_namespace_definitions_as xml2 | ||
end | ||
|
||
context 'when one of the classes is not a string' do | ||
let(:string) { 'hello' } | ||
let(:integer) { 1234 } | ||
|
||
it 'fails' do | ||
expect(string).not_to have_equal_namespace_definitions_as integer | ||
end | ||
|
||
it 'provides a useful error message' do | ||
expect { expect(string).to have_equal_namespace_definitions_as integer } | ||
.to raise_error(RSpec::Expectations::ExpectationNotMetError, /does not have equal namespaces as/) | ||
end | ||
end | ||
|
||
context 'when one of the strings does not contain valid xml' do | ||
let(:xml2) { 'foobar' } | ||
|
||
it 'fails' do | ||
expect(xml).not_to have_equal_namespace_definitions_as xml2 | ||
end | ||
|
||
it 'provides a useful error message' do | ||
expect { expect(xml).to have_equal_namespace_definitions_as xml2 } | ||
.to raise_error(RSpec::Expectations::ExpectationNotMetError, /does not have equal namespaces as/) | ||
end | ||
end | ||
|
||
context 'when one xml string is different' do | ||
let(:xml2) { Examples::Example3.json } | ||
|
||
it 'fails the comparison' do | ||
expect(xml).not_to have_equal_namespace_definitions_as xml2 | ||
end | ||
|
||
it 'provides a useful error message' do | ||
expect { expect(xml).to have_equal_namespace_definitions_as xml2 } | ||
.to raise_error(RSpec::Expectations::ExpectationNotMetError, /does not have equal namespaces as/) | ||
end | ||
end | ||
|
||
context 'with namespaces' do | ||
let(:xml) do | ||
<<-XML | ||
<alice xmlns="http://some-namespace" xmlns:charlie="http://some-other-namespace"> | ||
<bob>david</bob> | ||
<charlie:edgar>frank</charlie:edgar> | ||
</alice> | ||
XML | ||
end | ||
|
||
context 'with unequal namespace definitions' do | ||
let(:xml2) do | ||
<<~XML | ||
<alice xmlns:charlie="http://some-other-namespace" xmlns="http://some-namespace"> | ||
<bob>david</bob> | ||
<charlie:edgar xmlns:charlie="http://some-other-namespace" xmlns="http://some-namespace"> | ||
frank | ||
</charlie:edgar> | ||
</alice> | ||
XML | ||
end | ||
|
||
it 'fails the comparison' do | ||
expect(xml).not_to have_equal_namespace_definitions_as xml2 | ||
end | ||
|
||
it 'provides a useful error message' do | ||
expect { expect(xml).to have_equal_namespace_definitions_as xml2 } | ||
.to raise_error(RSpec::Expectations::ExpectationNotMetError, /does not have equal namespaces as/) | ||
end | ||
end | ||
|
||
context 'with similar namespace definitions' do | ||
let(:xml2) do | ||
<<~XML | ||
<alice xmlns:charlie="http://some-other-namespace" xmlns="http://some-namespace"> | ||
<bob>david</bob> | ||
<charlie:edgar>frank</charlie:edgar> | ||
</alice> | ||
XML | ||
end | ||
|
||
it 'successfully compares two similar xml strings' do | ||
expect(xml).to have_equal_namespace_definitions_as xml2 | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe 'BidirectionalConverter' do | ||
let(:xml2_json) { Dachsfisch::XML2JSONConverter } | ||
let(:json2_xml) { Dachsfisch::JSON2XMLConverter } | ||
|
||
context 'with valid XML' do | ||
describe '#perform' do | ||
subject { json2_xml.perform json: } | ||
|
||
let(:json) { xml2_json.perform xml: } | ||
|
||
Examples.each :json2xml do |example| | ||
context "with #{example.name}" do | ||
let(:xml) { example.xml } | ||
|
||
it { is_expected.to be_equivalent_to(xml) } | ||
it { is_expected.to have_equal_namespace_definitions_as(xml) } | ||
end | ||
end | ||
end | ||
end | ||
|
||
context 'with valid JSON' do | ||
describe '#perform' do | ||
subject { xml2_json.perform xml: } | ||
|
||
let(:xml) { json2_xml.perform json: } | ||
|
||
Examples.each :xml2json do |example| | ||
context "with #{example.name}" do | ||
let(:json) { example.json } | ||
|
||
it { is_expected.to be_an_equal_json_as(json) } | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rspec/expectations' | ||
|
||
RSpec::Matchers.define :have_equal_namespace_definitions_as do |expected| | ||
attr_reader :actual, :expected | ||
|
||
match do |actual| | ||
return false unless actual.is_a?(String) && expected.is_a?(String) | ||
|
||
expected_xml = parse_fragment(expected) | ||
@expected = expected_xml.to_xml | ||
actual_xml = parse_fragment(actual) | ||
@actual = actual_xml.to_xml | ||
|
||
return false if expected_xml.errors.length.positive? || actual_xml.errors.length.positive? | ||
return false unless EquivalentXml.equivalent?(expected_xml, actual_xml) | ||
|
||
return compare_namespaces(actual_xml.children.first, expected_xml.children.first) | ||
end | ||
|
||
failure_message do |actual| | ||
"#{@actual || actual} does not have equal namespaces as \n#{@expected || expected}." | ||
end | ||
|
||
diffable | ||
|
||
private | ||
|
||
def compare_namespaces(actual, expected) | ||
return false unless same_namespace_definitions?(actual, expected) | ||
|
||
actual.children.each_with_index.all? do |actual_child, index| | ||
expected_child = expected.children[index] | ||
compare_namespaces(actual_child, expected_child) | ||
end | ||
end | ||
|
||
def same_namespace_definitions?(actual, expected) | ||
return true if actual.nil? && expected.nil? | ||
return false if actual.nil? || expected.nil? | ||
|
||
actual_namespaces = namespaces(actual) | ||
expected_namespaces = namespaces(expected) | ||
actual_namespaces == expected_namespaces | ||
end | ||
|
||
def namespaces(node) | ||
node.namespace_definitions.map {|namespace| namespace.deconstruct_keys(%i[prefix href]) }.sort_by {|ns| ns[:prefix].to_s } | ||
end | ||
|
||
def parse_fragment(xml) | ||
# This is a workaround for an unintended behavior in Nokogiri's XML::DocumentFragment.parse method. | ||
# Originally, the method de-duplicates the namespace definitions of all nodes in the fragment, if possible. | ||
# However, for this test, we want to compare the namespace definitions of the actual and expected XML. | ||
# Therefore, we parse the XML with a root node, and compare the resulting document. | ||
Nokogiri::XML::Document.parse("<root>#{xml}</root>", &:noblanks) | ||
end | ||
end |