summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2019-05-25 20:22:41 +0300
committerLars Wirzenius <liw@liw.fi>2019-05-25 20:22:41 +0300
commit4c0f475beb0f4c445d55f46670a1b6d8dcd9b129 (patch)
treee19659e259a5e77cda5bd813f6197c7f41d43a51
parentcfc37ea567a5024a692ba929fa2338faa7403a92 (diff)
downloadfable-poc-4c0f475beb0f4c445d55f46670a1b6d8dcd9b129.tar.gz
Add: first rough draft of fft-docgen
-rwxr-xr-xfft-docgen124
-rw-r--r--muck.md133
-rw-r--r--muck.yaml32
3 files changed, 289 insertions, 0 deletions
diff --git a/fft-docgen b/fft-docgen
new file mode 100755
index 0000000..6dd7ee9
--- /dev/null
+++ b/fft-docgen
@@ -0,0 +1,124 @@
+#!/bin/env python3
+
+import copy
+import re
+import sys
+
+import CommonMark_bkrs as CommonMark
+import yaml
+
+def format_keyword(line):
+ words = line.split(' ')
+ keyword = words[0]
+ return '**{}** '.format(keyword) + line[len(keyword):]
+
+def format_scenario_step(bind, line):
+ for b in bind:
+ m = re.match(b['pattern'], line, re.I)
+ if m and m.end() == len(line):
+ n = len(m.groups())
+ if n > 0:
+ end = 0
+ parts = []
+ for i in range(1, n+1):
+ parts.append(line[end:m.start(i)])
+ thispart = line[m.start(i) : m.end(i)]
+ parts.append('_{}_'.format(thispart))
+ end = m.end(i)
+ line = ''.join(parts) + line[m.end(n):]
+
+ if not line.strip():
+ return line
+ return format_keyword(line)
+
+def format_fable_snippet(bind, lines):
+ return [format_scenario_step(bind, line) for line in lines]
+
+def is_fable_snippet(o):
+ prefix = "```fable\n"
+ return o.t == 'FencedCode' and o.info == 'fable'
+
+def is_heading(o):
+ return o.t =='ATXHeader'
+
+def write_document(bind, f, o):
+ pass
+
+def write_atxheader(bind, f, o):
+ f.write('{} {}\n\n'.format('#' * o.level, ' '.join(o.strings)))
+
+def write_setextheader(bind, f, o):
+ chars = {
+ 1: '=',
+ 2: '-',
+ }
+ c = chars[o.level]
+ f.write('{}\n{}\n\n'.format(' '.join(o.strings), c * 72))
+
+def write_paragraph(bind, f, o):
+ for s in o.strings:
+ f.write('{}\n'.format(s))
+ f.write('\n')
+
+def write_fable_snippet(bind, f, o):
+ for line in format_fable_snippet(bind, o.strings[1:]):
+ f.write('> {} \n'.format(line))
+ f.write('\n')
+
+def write_not_fable_snippet(bind, f, o):
+ fence = o.fence_char * o.fence_length
+ lang = o.strings[0]
+ f.write('{}{}\n'.format(fence, lang))
+ for line in o.strings[1:]:
+ f.write('{}\n'.format(line))
+ f.write('{}\n'.format(fence))
+ f.write('\n')
+
+def write_fencedcode(bind, f, o):
+ if is_fable_snippet(o):
+ write_fable_snippet(bind, f, o)
+ else:
+ write_not_fable_snippet(bind, f, o)
+
+def write_horizontalrule(bind, f, o):
+ f.write('---\n')
+
+writers = {
+ 'Document': write_document,
+ 'ATXHeader': write_atxheader,
+ 'SetextHeader': write_setextheader,
+ 'Paragraph': write_paragraph,
+ 'FencedCode': write_fencedcode,
+ 'HorizontalRule': write_horizontalrule,
+}
+
+def write(bind, f, o):
+ if o.t not in writers:
+ debug('{} not known'.format(repr(o.t)))
+ return
+ writer = writers[o.t]
+ writer(bind, f, o)
+
+def walk(o, func):
+ func(o)
+ for c in o.children:
+ walk(c, func)
+
+def debug(msg):
+ sys.stderr.write('DEBUG: {}\n'.format(msg))
+ sys.stderr.flush()
+
+debug('reading bindings')
+bindings = yaml.safe_load(open(sys.argv[1]))
+
+debug('reading inputs')
+text = ''.join(open(filename).read() for filename in sys.argv[2:])
+
+debug('parse')
+parser = CommonMark.DocParser()
+ast = parser.parse(text)
+
+debug('output')
+walk(ast, lambda o: write(bindings, sys.stdout, o))
+
+debug('ok')
diff --git a/muck.md b/muck.md
new file mode 100644
index 0000000..d29319c
--- /dev/null
+++ b/muck.md
@@ -0,0 +1,133 @@
+---
+title: Muck acceptance tests v2
+author: Lars Wirzenius / The Ick project
+...
+
+Introduction
+=============================================================================
+
+Muck is a persistent in-memory JSON store with an HTTP API and
+advanced access control using signed JWT access tokens. This document
+presents its automated acceptance tests, using a (for now
+hypothetical) language similar to the Gherkin langauge implemented by
+Cucumber.
+
+A happy path scenario
+=============================================================================
+
+This scenario does some basic resource management via the Muck API.
+
+Start Muck. This also sets up access to it for the user by getting an
+access token, which will be used for all requests.
+
+```fable
+given a running Muck
+```
+
+Check server status.
+
+```fable
+then there are no resources in Muck
+```
+
+Create a simple resource. Remember its id.
+
+```fable
+given I am tomjon
+when I create a resource {"foo": "bar"}
+then there is 1 resource in Muck
+and remember the resource id as ID
+and remember the resource revision as REV1
+```
+
+Retrieve the resource.
+
+```fable
+when I fetch resource ID
+then I get {"foo": "bar"}
+and it is mine
+and it has revision REV1
+```
+
+Make sure another user can't retreive, update, or delete the resource.
+
+```fable
+given I am verence
+when I fetch resource ID
+then it doesn't exist
+
+when I update ID, revision REV1, with {"foo": "somethingelse"}
+then it doesn't exist
+
+when I delete ID
+then it doesn't exist
+```
+
+Update the resource.
+
+```fable
+given I am tomjon
+when I update ID, revision wrong, with {"foo": "somethingelse"}
+then it doesnt't work
+
+when I update ID, revision REV1, with {"foo": "somethingelse"}
+then it works
+and remember the resource revision as REV2
+```
+
+Check the resource has been updated.
+
+```fable
+when I fetch resource ID
+then I get {"foo": "somethingelse"}
+and it is mine
+and it has revision REV2
+```
+
+Restart Muck. The resource should still exist.
+
+```fable
+when Muck is restarted
+and I fetch resource ID
+then I get {"foo": "somethingelse"}
+and it is mine
+and it has revision REV2
+```
+
+Search for the resource. First with a condition that is no longer
+true.
+
+```fable
+when I search for foo being bar
+then there are no matches
+```
+
+Now search for the correct value.
+
+```fable
+when I search for foo being somethingelse
+then I only get resource ID
+```
+
+Delete the resource.
+
+```fable
+when I delete ID
+then it works
+```
+
+
+```fable
+when I fetch resource ID
+then it doesn't exist
+```
+
+Restart Muck again. The resource should not exist.
+
+```fable
+when Muck is restarted
+and I fetch resource ID
+then it doesn't exist
+```
+
+All done.
diff --git a/muck.yaml b/muck.yaml
new file mode 100644
index 0000000..afc7249
--- /dev/null
+++ b/muck.yaml
@@ -0,0 +1,32 @@
+- pattern: when I fetch resource (?P<id>\S+)
+- pattern: and I fetch resource (?P<id>\S+)
+
+- pattern: then it has revision (?P<revision>\S+)
+- pattern: and it has revision (?P<revision>\S+)
+
+- pattern: then it is mine
+- pattern: and it is mine
+
+- pattern: then remember the resource id as (?P<name>\S+)
+- pattern: and remember the resource id as (?P<name>\S+)
+
+- pattern: then remember the resource revision as (?P<name>\S+)
+- pattern: and remember the resource revision as (?P<name>\S+)
+
+- pattern: given I am (?P<username>\S+)
+- pattern: given a running Muck
+- pattern: then I only get resource (?P<id>\S+)
+- pattern: then I get (?P<json>.+)
+- pattern: "then it doesn't exist"
+- pattern: then it works
+- pattern: then there are no resources in Muck
+
+- pattern: then there is (?P<number>\d+) resource in Muck
+- pattern: then there are (?P<number>\d+) resources in Muck
+
+- pattern: then there are no matches
+- pattern: when I create a resource (?P<json>.+)
+- pattern: when I delete (?P<id>\S+)
+- pattern: when I search for (?P<field>\S+) being (?P<value>.+)
+- pattern: when I update (?P<id>\S+), revision (?P<rev>\S+), with (?P<json>.+)
+- pattern: when Muck is restarted