Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bidirectional converter specs to test namespaces #53

Merged
merged 1 commit into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions spec/custom_matchers/equal_namespace_definitions_spec.rb
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
kkoehn marked this conversation as resolved.
Show resolved Hide resolved
<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
39 changes: 39 additions & 0 deletions spec/dachsfisch/bidirectional_converter_spec.rb
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
59 changes: 59 additions & 0 deletions spec/support/expectations/equal_namespace_definitions.rb
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)
kkoehn marked this conversation as resolved.
Show resolved Hide resolved

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