mirror of
https://github.com/ilyakooo0/roboservant.git
synced 2024-09-11 08:05:47 +03:00
docs
This commit is contained in:
parent
c338551b07
commit
df67dfc1c6
@ -64,7 +64,7 @@ config = defaultConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- there are other tweakable things in the config, like maximum runtime, reps,
|
-- there are other tweakable things in the config, like maximum runtime, reps,
|
||||||
-- per-request healthchecks, and verbose logging. Have a look at
|
-- per-request healthchecks, seeds, and verbose logging. Have a look at
|
||||||
-- Roboservant.Types.Config for details.
|
-- Roboservant.Types.Config for details.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
74
README.md
74
README.md
@ -14,6 +14,63 @@ Servant gives us a lot of information about what a server can do. We
|
|||||||
use this information to generate arbitrarily long request/response
|
use this information to generate arbitrarily long request/response
|
||||||
sessions and verify properties that should hold over them.
|
sessions and verify properties that should hold over them.
|
||||||
|
|
||||||
|
## how?
|
||||||
|
|
||||||
|
In essence, ```fuzz @Api yourServer config``` will make a bunch of
|
||||||
|
calls to your API, and record the results in a type-indexed
|
||||||
|
dictionary. This means that they are now available for the
|
||||||
|
prerequisites of other call, so as you proceed, more and more api
|
||||||
|
calls become possible.
|
||||||
|
|
||||||
|
### what does it mean to be "available"?
|
||||||
|
|
||||||
|
In a simple API, you may make a call and get back a `Foo`, which will
|
||||||
|
allow you to make another call that requires a `Foo`. In a more
|
||||||
|
complicated app, it's likely that you'll send a request body that
|
||||||
|
includes many subcomponents, and it's likely you'll get a response
|
||||||
|
that needs to be broken down into pieces before it's useful.
|
||||||
|
|
||||||
|
To cope with this, we have the typeclasses `BuildFrom` and
|
||||||
|
`Breakdown`. You can write instances for them if you feel like it, and
|
||||||
|
indeed it's currently required for recursive datatypes if you don't
|
||||||
|
want the fuzzer to hang, but for the majority of your types it should
|
||||||
|
be sufficient to derive them generically. (Sensible instances are
|
||||||
|
provided for lists.)
|
||||||
|
|
||||||
|
There are two basic strategies here. In some cases, you want to regard
|
||||||
|
a type as indivisible: that's why we like newtypes, right? In this
|
||||||
|
case, we can derive using the `Atom` strategy.
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
deriving via (Atom NewtypedKey) instance Breakdown NewtypedKey
|
||||||
|
deriving via (Atom NewtypedKey) instance BuildFrom NewtypedKey
|
||||||
|
```
|
||||||
|
|
||||||
|
This is saying "A can neither be built from components or broken down
|
||||||
|
for spare parts. Hands off!". This is a good strategy for key types,
|
||||||
|
for instance.
|
||||||
|
|
||||||
|
If instead it's a big complicated thing with lots of juicy
|
||||||
|
subcomponents, we want to rip it apart using Generics and feast on
|
||||||
|
its succulent headmeats:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
deriving via (Compound Payload) instance Breakdown Payload
|
||||||
|
deriving via (Compound Payload) instance BuildFrom Payload
|
||||||
|
```
|
||||||
|
|
||||||
|
### priming the pump
|
||||||
|
|
||||||
|
Sometimes there are values we'd like to smuggle into the API that are
|
||||||
|
not derivable from within the API itself: sometimes this is a warning
|
||||||
|
sign that your API is incomplete, but it can be quite reasonable to
|
||||||
|
require identifying credentials within an API and not provide a way to
|
||||||
|
get them. For those cases, override the `seed` in the `Config` with a
|
||||||
|
list of seed values, suitably hashed:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
defaultConfig { seed = [hashedDyn creds, hashedDyn userJwt]}
|
||||||
|
```
|
||||||
|
|
||||||
## why not servant-quickcheck?
|
## why not servant-quickcheck?
|
||||||
|
|
||||||
@ -23,3 +80,20 @@ there's a lot of the state space you just can't explore without context: modern
|
|||||||
full of pointer-like structures, whether they're URLs or database
|
full of pointer-like structures, whether they're URLs or database
|
||||||
keys/uuids, and servant-quickcheck requires that you be able to generate
|
keys/uuids, and servant-quickcheck requires that you be able to generate
|
||||||
these without context via Arbitrary.
|
these without context via Arbitrary.
|
||||||
|
|
||||||
|
## limitations and future work
|
||||||
|
|
||||||
|
Currently, the display of failing traces is pretty tragic, both in the
|
||||||
|
formatting and in its non-minimality. This is pretty ticklish:
|
||||||
|
arguably the right way to do this is to return a trace that we can
|
||||||
|
also rerun, and let quickcheck or hedgehog a level up shrink it until
|
||||||
|
it's satisfactorily short. In the interest of being useful earlier
|
||||||
|
rather than later, I'm releasing v1.0 before I crack this particular
|
||||||
|
nut. We do know which calls we made that led to the failing case, so
|
||||||
|
we would want to show that distinction in a visible way: it's possible
|
||||||
|
that other calls that don't have direct data dependencies were
|
||||||
|
important, but we definitely know we need the direct data dependencies.
|
||||||
|
|
||||||
|
It would also be nice to have a robust strategy for deriving recursive
|
||||||
|
datatypes, or at least rejecting attempts to generate them that don't
|
||||||
|
end in an infinite loop.
|
||||||
|
Loading…
Reference in New Issue
Block a user