Skip to content

Commit

Permalink
Merge pull request #1 from alqh/proto-1
Browse files Browse the repository at this point in the history
Proto Stage 1 - Go test output parser
  • Loading branch information
alqh authored Mar 24, 2024
2 parents a88cb49 + 3cceb67 commit 5d1f3e6
Show file tree
Hide file tree
Showing 23 changed files with 1,072 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
# @global-owner1 and @global-owner2 will be requested for
# review when someone opens a pull request.
@alqh
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
.idea
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Fitogether

Inspired by the workflow of [_Fit_](http://fit.c2.com/), Fitogether enables collaboration and communication in software development.

## Concept

<img src="./docs/assets/images/Fit.png" width="650"/>

Multiple stakeholders (technical and non-technical) collaborate together to produce a cFit Document that describes the software behaviour in a language that is understandable and clear to all. This document should be easy enough to understand that it can serve as a manual to ordinary users, a promise to your users of what the software does.

The cFit Document feeds into the process of developing the running software. Each part of the document sets an expectation of how the software should behave, like an acceptance criteria, which is then translated into one or more tests written as part of the UI or service's test. Typically a UI or a service has different granularity of tests (testing pyramid), therefore, the one expectation could encompass a few unit tests, a few component tests and an integration test.

All the tests results then feeds back up and collated to indicate if the expectation in the Fit Document has been met or not.

A cFit report should show:

* the expectations of the software
* if the expectations of the software has been met. This is measured if all tests related to that expectation pass.
* a breakdown of where the expectation is verified. This is the list of where the tests are.

75 changes: 75 additions & 0 deletions docs/USE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Use Fitogether

## Creating cFit Documents

### Unit
Each cFit document should represent a unit of software.

Each unit has expectations of how it behave, and can include interactions with other units in order to deliver the functionality that it owns.

This unit is documented in a single markdown file.

The fitogether tool will parse the markdown to determine the expectations to match.

Defining expectations:

* Each header is an expectation. This can be `h2`, `h3`, `h4` and so forth.
* The expectation is referenced by its coded value, which is the first non-spaced word on the header.

```markdown
# Tagging Blog Post

// Format <heading level> <codedValue> <text describing the expectation>
## T1.v2 Add a tag to a blog post

### A1 The tag must have been deleted

### A2 The user must have permission to add a tag to blog post
```

### Categories

Multiple cFit documents can be grouped under a category of functionality, which in turn, can be grouped under a larger category of functionality.

Each category is a directory.

An example could be:

```
blogs
|-- post
|-- tagging-blog-post.md
|-- feeds
|-- subscribe-new-feeds.md
|-- unsubscribe-via-email.md
```

## Creating Tests to Validate cFit Expectations

The testing library used for running tests in for a language will produce a test report. This test report will be parsed by fitogether tool to match the test to the expectation.

Each expectation can be referenced by its full URI:

```
// Format: [<dir_path>]-<filename>-[<codedValue>]
blogs/post/tagging-blog-post/t1.v2/a2
```

Depending on the language support, here is how each test are tagged:

### Go

Add the URI of the expecation in the end of the the test name.

```go
// Format: t.Run("..description of test... @<URI of expectation>")

func TestAssignBlogPostHandler(t *testing.T) {
t.Run("Should remove tags to blog post @blogs/post/tagging-blog-post/t1.v2/a1", ....)
}

func TestBlogPostRepository(t *testing.T) {
t.Run("Soft delete tag on blog post @blogs/post/tagging-blog-post/t1.v2/a1", ....)
}
```

158 changes: 158 additions & 0 deletions docs/assets/drawio/Fit.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<mxfile host="app.diagrams.net" modified="2024-03-16T03:03:55.285Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" etag="4GVYUySkvPvQ5tCx2QE3" version="24.0.7" type="device">
<diagram name="Page-1" id="53M_2jYgaUl9bHfNDvYw">
<mxGraphModel dx="2069" dy="819" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="P_TPIv7538cp4Sw5QN1q-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-1" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-1" value="Developers" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="130" y="380" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="295" y="470" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-2" value="Designers" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="280" y="520" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-5" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-5" value="Product Managers" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="429" y="380" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-6" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-6" value="Support" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="150" y="280" width="20" height="40" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-7" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-7" value="Marketing" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="429" y="280" width="21" height="40" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-8" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-8" value="Users" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="390" y="510" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeColor=#00994D;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-14" target="P_TPIv7538cp4Sw5QN1q-28">
<mxGeometry relative="1" as="geometry">
<mxPoint x="295" y="190" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-14" value="Collaborate and agree on software behaviour" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;textShadow=0;fillColor=#CCFFCC;" vertex="1" parent="1">
<mxGeometry x="240" y="355" width="110" height="110" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-15" target="P_TPIv7538cp4Sw5QN1q-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-15" value="Testers" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="170" y="510" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;strokeColor=#00994D;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-28" target="P_TPIv7538cp4Sw5QN1q-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-34" value="sets expectations" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-33">
<mxGeometry x="-0.1444" y="1" relative="1" as="geometry">
<mxPoint x="1" y="1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-28" value="cFit Document" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="207.5" y="120" width="175" height="90" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-30" value="- Clear business requirements&lt;br&gt;- Describe software behaviour&lt;br&gt;(functionally, not the UI)&lt;br&gt;- Language used is understandable and aligned among stakeholders&amp;nbsp;&lt;br&gt;(ubiquitous language)" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;labelBackgroundColor=#FFF6A6;spacing=5;spacingTop=5;spacingBottom=5;" vertex="1" parent="1">
<mxGeometry x="-60" y="115" width="240" height="100" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.75;entryDx=0;entryDy=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;dashed=1;strokeColor=#FF6666;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-32" target="P_TPIv7538cp4Sw5QN1q-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-36" value="pass / fail" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#FF3333;" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-35">
<mxGeometry x="0.1709" y="3" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;strokeColor=#00994D;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-32" target="P_TPIv7538cp4Sw5QN1q-37">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-54" value="Help form&lt;br&gt;test cases" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-45">
<mxGeometry x="0.5924" y="-1" relative="1" as="geometry">
<mxPoint x="-41" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-46" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeColor=#00994D;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-32" target="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry relative="1" as="geometry">
<mxPoint x="995" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-58" value="Help form&lt;br&gt;test cases" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-46">
<mxGeometry x="0.7333" y="2" relative="1" as="geometry">
<mxPoint x="-37" y="-4" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-32" value="Running Software" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="800" y="120" width="150" height="90" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-37" value="UI" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="727" y="360" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-52" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.347;entryY=1.011;entryDx=0;entryDy=0;entryPerimeter=0;dashed=1;fillColor=#f8cecc;strokeColor=#FF3333;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-37" target="P_TPIv7538cp4Sw5QN1q-32">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="777" y="310" />
<mxPoint x="852" y="310" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-55" value="pass / fail" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#FF3333;" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-52">
<mxGeometry x="-0.643" y="-1" relative="1" as="geometry">
<mxPoint x="32" y="10" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-59" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.acute_triangle;dx=0.5;" vertex="1" parent="1">
<mxGeometry x="795" y="365" width="50" height="35" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-60" value="Tests" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="790" y="375" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-63" value="BE / Services" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="920" y="375" width="245" height="200" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-39" value="Data&lt;br&gt;Storage" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry x="10" y="40" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-40" value="Events" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry x="120" y="70" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-41" value="3rd Party&lt;br&gt;(External)" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry x="10" y="110" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-61" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.acute_triangle;dx=0.5;" vertex="1" parent="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry x="190" y="-10" width="50" height="35" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-62" value="Tests" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="P_TPIv7538cp4Sw5QN1q-63">
<mxGeometry x="185" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-65" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.813;entryY=1.033;entryDx=0;entryDy=0;entryPerimeter=0;dashed=1;strokeColor=#FF3333;" edge="1" parent="1" source="P_TPIv7538cp4Sw5QN1q-63" target="P_TPIv7538cp4Sw5QN1q-32">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1043" y="270" />
<mxPoint x="922" y="270" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="P_TPIv7538cp4Sw5QN1q-66" value="pass / fail" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#FF3333;" vertex="1" connectable="0" parent="P_TPIv7538cp4Sw5QN1q-65">
<mxGeometry x="-0.5116" y="1" relative="1" as="geometry">
<mxPoint x="29" y="4" as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Binary file added docs/assets/images/Fit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output-results
Loading

0 comments on commit 5d1f3e6

Please sign in to comment.