I’ve been in a meetup recently and it was about architecture validation with the help of our good friend jqAssistant. Although I didn’t learn much new in the first part, the second part was highly interesting:
For a starter, we need to add asciidoc support to the ADR tooling, since they normally are written in markdown. There is a pending PR that does exactly that:
Or if you need a complete set along with other nifty things like indexing a database (sqlite3) handling and basic atom/rss support, just use my version of it:
Once we’ve picked our version, we need to set this up accordingly:
adr-tools/adr init -t adoc jqassistant/decisions adr-tools/adr new "Assertion Library"
I also will not go into detail how to set up jgAssistant or how to create a bootstrap project and focus on the funny parts. If you want to dive head first just checkout my demo project:
Back to our new ADR, let us just fill in a bit of magic:
= 1. Assertion library |=== | Proposed Date: | 2021-03-15 | Decision Date: | ? | Proposer: | Christoph Kappel | Deciders: | ? | Status: | drafted | Issues: | ? | References: | | Priority: | high |=== NOTE: *Status types:* drafted | proposed | rejected | accepted | deprecated | superseded + *Priority:* low | medium | high == Context There are dozen of assertion library available, we want to settle one for our purpose and rely just on this one. == Decision After some consideration we agreed on using https://assertj.github.io/doc/[AssertJ]. == Consequences [[adr:AssertionLibrary]] [source,cypher,role=constraint,severity=minor] .All calls to `assertThat` must be from `AssertJ`! ---- MATCH (assertType:Type)-[:DECLARES]->(assertMethod) // (1) WHERE NOT assertType.fqn =~ "org.assertj.core.api.*" // (2) AND assertMethod.signature =~ ".*assertThat.*" WITH assertMethod MATCH (testType:Type)-[:DECLARES]->(testMethod:Method), (testMethod)-[invocation:INVOKES]->(assertMethod) RETURN testType.fqn + "#" + testMethod.name as TestMethod, // (3) invocation.lineNumber as LineNumber ORDER BY TestMethod, LineNumber ---- include::jQA:Rules[concepts="adr:AssertionLibrary*", constraints="adr:AssertionLibrary*"] // (4)
So this is basically the complete ADR and I assume most of the things beside the weird code block is pretty self explanatory. The code block - glad you’ve asked - is cypher.
It creates a simple query (1) for all methods, that have assertThat in its method signature and then checks, if the full-qualified name of the package is NOT org.assertj.core.api. (2) For every match (3), it returns the name of the method, along with its line number.
When this list is empty the test passes, if not you’ll get a neat nice table below the definition in the output.
NOTE: The part around (4) is required to see the actual results in the rendered document, took me quite a whole to figure this out.
So we have to jump right back to jqAssistant, since we are done with the ADR.
Next best thing to do is to create an index.adoc document for jqAssistant to render:
:description: This is a demo project for the combination of ADR and jqAssistant. :doctype: book :toc: left :toc-title: Table of Contents :toclevels: 2 :sectnums: :icons: font :nofooter: [[default]] // (1) [role=group,includesGroups="adr:default"] == Architecture decisions, tests and metrics This document contains example https://jqassistant.org/[jqAssistant] rules along with simple https://adr.github.io/[Architecture Decision Records]. [[adr:default]] [role=group,includesConcepts="adr:*",includesConstraints="adr:*"] // (2) == List of Architecture Decision Records include::decisions/0001-assertion-library.adoc[leveloffset=2] // (3)
The default group (1) is required, otherwise jqAssistant will not pick up this document and render nothing at all.
The remaining points, namely (2) and (3), include the ADR into our main document.