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

LG-14972 | Fix XML fixture; implement match_xml matcher #11599

Merged
merged 6 commits into from
Dec 6, 2024

Conversation

n1zyy
Copy link
Member

@n1zyy n1zyy commented Dec 5, 2024

🎫 Ticket

Link to the relevant ticket:
LG-14972

🛠 Summary of changes

Implements a match_xml matcher for rspec.

This is intended to work around an issue where the XML fixture had to be mis-indented in a weird way to match what was actually being generated, because we were comparing them with string equality.

Curiously, REXML::Documents never match:

[11] pry(main)> x = REXML::Document.new('<xml/>')
=> <UNDEFINED> ... </>
[12] pry(main)> y = x.dup
=> <UNDEFINED> ... </>
[13] pry(main)> x == y
=> false

So, run the actual and expected XML documents through a REXML::Formatter to normalize them. We still have to do a string comparison, but stuff like indentation go away.

"Shouldn't we just fix the XML we generate?"

No, we should switch to their REST API and never have to deal with XML again. But that's a bigger story.

@n1zyy
Copy link
Member Author

n1zyy commented Dec 5, 2024

There does not seem to be a norm around writing tests for matchers, but here's what happen if I add an extra character to the fixture:

screenshot showing an unexpected extra character added to the transaction ID, causing the test to fail and show a diff of only that line

doc = REXML::Document.new(document)
formatter = REXML::Formatters::Pretty.new
formatter.compact = true
formatter.write(doc, output)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this pattern, but I'm not the REXML maintainer.

output = String.new(encoding: 'UTF-8')
doc = REXML::Document.new(document)
formatter = REXML::Formatters::Pretty.new
formatter.compact = true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not super significant which formatter we use, but Pretty generates nicely-formatted XML that's actually fairly close to what we have.

compact = true keeps <foo>bar</foo> from becoming:

<foo>
  bar
</foo>

# This matcher considers the documents the same if their string outputs
# are equal after both going through the REXML::Formatters::Pretty.
match do |document|
# We have to override these for the diff to use these, rather than input strings
Copy link
Member Author

@n1zyy n1zyy Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diffable line would otherwise print the diff between document and comparison, which is the original XML before normalization. This caused some pulling out of hair, because it's not what we're actually comparing, so the diff shown was not correlated to the failure!

@n1zyy n1zyy marked this pull request as ready for review December 5, 2024 17:14
changelog: Internal, RSpec Matchers, Adds match_xml matcher and cleans up gross fixture
@n1zyy n1zyy requested a review from a team December 5, 2024 17:26
@n1zyy n1zyy changed the title Cleanup LG-14972 | Fix XML fixture; implement match_xml matcher Dec 5, 2024
Copy link
Member

@matthinz matthinz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that we don't consistently have specs for matchers, but we do have some. I think it would be good to have a quick spec for this, just to document what we expect the behavior to be:

  • i give you two xml docs that differ in whitespace only, and we can still tell whether they match
  • (bonus points, not required if it is a whole thing) i give you two xml docs that are the same, but reference namespaces slightly differently, and we can still tell whether they match

@@ -51,3 +51,17 @@
require 'zonebie/rspec'

RSpec::Expectations.configuration.on_potential_false_positives = :nothing

# Shared helper methods used in multiple files
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced this is the right place for these, and it boggles my mind that I am the first person on the project to want to reuse a method between multiple tests, but I cannot find any example of putting these somewhere else?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are various flavors of spec helpers in spec/support, but I'd consider creating a new rspec matcher for something like this too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some, but not all, are unintuitively included in all tests here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new matcher to test the test for my new matcher feels like the turtles are starting to stack up a little too much.

I'm going to push an update moving this out of SpecHelper and into a new DiffHelper. Let me know if that seems like a reasonable enough approach.

@n1zyy
Copy link
Member Author

n1zyy commented Dec 5, 2024

@matthinz Thanks for the (offline) pointer to

describe '#have_logged_event' do

I have implemented a test with a handful of examples. I ended up not doing anything with namespaces, because the matcher as written is still very literal and doesn't give them any special treatment. I.e., declaring an unused namespace will still produce a document with it through the Pretty Formatter, and even just changing the order namespaces are mentioned will also result in a non-match.

I started to add a test case showing this, but I think the existing tests showing that changing capitalization or ordering of elements will result in a non-match is more expressive: there is nothing special about namespaces.

@n1zyy n1zyy merged commit 5e8b9e8 into main Dec 6, 2024
2 checks passed
@n1zyy n1zyy deleted the mattw/LG-14972_xml_matcher branch December 6, 2024 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants