ADR and jqAssistant
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:
Combine ADR with the asciidoc processing of jqAssistant. I’ve never thought about it before, but once the speaker presented this idea I was totally into it.
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"
First steps with jqAssistant
Messing with jqAssistant is always funny, when you manage to make your mind about Cypher, you are busy with lots of flaky tests and varying output errors, but we’ll come to that later I guess.
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:
Revisit our ADR
= 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)
|1||Simple query in Cypher for all methods that have
|2||Check, if if the full-qualified name of the package is NOT
|3||Return the name of the method, along with its line number for every match.|
|4||This is required to see the actual results in the rendered document, took me quite a whole to figure this out.|
So this is basically the complete ADR and I assume most of the things beside the weird code block is pretty self explanatory. When this list is empty the test passes, if not you’ll get a neat nice table below the definition in the output.
Put it to the test in jqAssistant
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]
|1||The default group is required, otherwise jqAssistant will not pick up this document and render nothing at all.|
|2||Include the ADR into our main document.|
Both tools can be used to write tests for architecture. The addition of a graph database in jqAssistant along with the query language Cypher and the different input plugins allow more flexibility, but also set a steep learning curve.
My showcase can be found here: