Skip to content

Commit be89f75

Browse files
authored
New Feature: Application Boxes in Indexer (#1168)
1 parent 95658bc commit be89f75

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5941
-510
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
go_version:
4747
type: string
4848
environment:
49-
CI_E2E_FILENAME: "fad05790/rel-nightly"
49+
CI_E2E_FILENAME: "faab6dcf/rel-nightly"
5050
steps:
5151
- go/install:
5252
version: << parameters.go_version >>

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ tmp/
1717
# Output of the go coverage tool, specifically when used with LiteIDE
1818
*.out
1919

20+
# Output of fixtures_test.go
21+
_*.json
22+
2023
# Dependency directories (remove the comment below to include it)
2124
# vendor/
2225

@@ -29,6 +32,9 @@ __pycache__
2932
# jetbrains IDE
3033
.idea
3134

35+
# VS Code
36+
.vscode
37+
3238
.deb_tmp
3339
.tar_tmp
3440
*.deb

api/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ We are using a documentation driven process.
55
The API is defined using [OpenAPI v2](https://swagger.io/specification/v2/) in **indexer.oas2.yml**.
66

77
## Updating REST API
8+
89
The Makefile will install our fork of **oapi-codegen**, use `make oapi-codegen` to install it directly.
910

1011
1. Document your changes by editing **indexer.oas2.yml**
@@ -20,3 +21,104 @@ Specifically, `uint64` types aren't strictly supported by OpenAPI. So we added a
2021
## Why do we have indexer.oas2.yml and indexer.oas3.yml?
2122

2223
We chose to maintain V2 and V3 versions of the spec because OpenAPI v3 doesn't seem to be widely supported. Some tools worked better with V3 and others with V2, so having both available has been useful. To reduce developer burdon, the v2 specfile is automatically converted v3 using [converter.swagger.io](http://converter.swagger.io/).
24+
25+
# Fixtures Test
26+
## What is a **Fixtures Test**?
27+
28+
Currently (September 2022) [fixtures_test.go](./fixtures_test.go) is a library that allows testing Indexer's router to verify that endpoints accept parameters and respond as expected, and guard against future regressions. [app_boxes_fixtures_test.go](./app_boxes_fixtures_test.go) is an example _fixtures test_ and is the _creator_ of the fixture [boxes.json](./test_resources/boxes.json).
29+
30+
A fixtures test
31+
32+
1. is defined by a go-slice called a _Seed Fixture_ e.g. [var boxSeedFixture](https://github.com/algorand/indexer/blob/b5025ad640fabac0d778b4cac60d558a698ed560/api/app_boxes_fixtures_test.go#L302-L692) which contains request information for making HTTP requests against an Indexer server
33+
2. iterates through the slice, making each of the defined requests and generating a _Live Fixture_
34+
3. reads a _Saved Fixture_ from a json file e.g. [boxes.json](./test_resources/boxes.json)
35+
4. persists the _Live Fixture_ to a json file not in source control
36+
5. asserts that the _Saved Fixture_ is equal to the _Live Fixture_
37+
38+
In reality, because we always want to save the _Live Fixture_ before making assertions that could fail the test and pre-empt saving, steps (3) and (4) happen in the opposite order.
39+
40+
## What's the purpose of a Fixtures Test?
41+
42+
A fixtures test should allow one to quickly stand up an end-to-end test to validate that Indexer endpoints are working as expected. After Indexer's state is programmatically set up, it's easy to add new requests and verify that the responses look exactly as expected. Once you're satisfied that the responses are correct, it's easy to _freeze_ the test and guard against future regressions.
43+
## What does a **Fixtures Test Function** Look Like?
44+
45+
[func TestBoxes](https://github.com/algorand/indexer/blob/b5025ad640fabac0d778b4cac60d558a698ed560/api/app_boxes_fixtures_test.go#L694_L704) shows the basic structure of a fixtures test.
46+
47+
1. `setupIdbAndReturnShutdownFunc()` is called to set up the Indexer database
48+
* this isn't expected to require modification
49+
2. `setupLiveBoxes()` is used to prepare the local ledger and process blocks in order to bring Indexer into a particular state
50+
* this will always depend on what the test is trying to achieve
51+
* in this case, an app was used to create and modify a set of boxes which are then queried against
52+
* it is conceivable that instead of bringing Indexer into a particular state, the responses from the DB or even the handler may be mocked, so we could have had `setupLiveBoxesMocker()` instead of `setupLiveBoxes()`
53+
3. `setupLiveServerAndReturnShutdownFunc()` is used to bring up an instance of a real Indexer.
54+
* this shouldn't need to be modified; however, if running in parallel and making assertions that conflict with other tests, you may need to localize the variable `fixtestListenAddr` and run on a separate port
55+
* if running a mock server instead, a different setup function would be needed
56+
4. `validateLiveVsSaved()` runs steps (1) through (5) defined in the previous section
57+
* this is designed to be generic and ought not require much modification going forward
58+
59+
60+
## Which Endpoints are Currently _Testable_ in a Fixtures Test?
61+
62+
Endpoints defined in [proverRoutes](https://github.com/algorand/indexer/blob/b955a31b10d8dce7177383895ed8e57206d69f67/api/fixtures_test.go#L232-L263) are testable.
63+
64+
Currently (September 2022) these are:
65+
66+
* `/v2/accounts`
67+
* `/v2/applications`
68+
* `/v2/applications/:application-id`
69+
* `/v2/applications/:application-id/box`
70+
* `/v2/applications/:application-id/boxes`
71+
72+
## How to Introduce a New Fixtures Test for an _Already Testable_ Endpoint?
73+
74+
To set up a new test for endpoints defined above one needs to:
75+
76+
### 1. Define a new _Seed Fixture_
77+
78+
For example, consider
79+
80+
```go
81+
var boxSeedFixture = fixture{
82+
File: "boxes.json",
83+
Owner: "TestBoxes",
84+
Frozen: true,
85+
Cases: []testCase{
86+
// /v2/accounts - 1 case
87+
{
88+
Name: "What are all the accounts?",
89+
Request: requestInfo{
90+
Path: "/v2/accounts",
91+
Params: []param{},
92+
},
93+
},
94+
...
95+
```
96+
97+
A seed fixture is a `struct` with fields
98+
* `File` (_required_) - the name in [test_resources](./test_resources/) where the fixture is read from (and written to with an `_` prefix)
99+
* `Owner` (_recommended_) - a name to define which test "owns" the seed
100+
* `Frozen` (_required_) - set _true_ when you need to run assertions of the _Live Fixture_ vs. the _Saved Fixture_. For tests to pass, it needs to be set _true_.
101+
* `Cases` - the slice of `testCase`s. Each of these has the fields:
102+
* `Name` (_required_) - an identifier for the test case
103+
* `Request` (_required_) - a `requestInfo` struct specifying:
104+
* `Path` (_required_) - the path to be queried
105+
* `Params` (_required but may be empty_) - the slice of parameters (strings `name` and `value`) to be appended to the path
106+
### 2. Define a new _Indexer State_ Setup Function
107+
108+
There are many examples of setting up state that can be emulated. For example:
109+
* [setupLiveBoxes()](https://github.com/algorand/indexer/blob/b5025ad640fabac0d778b4cac60d558a698ed560/api/app_boxes_fixtures_test.go#L43) for application boxes
110+
* [TestApplicationHandlers()](https://github.com/algorand/indexer/blob/3a9095c2b5ee25093708f980445611a03f2cf4e2/api/handlers_e2e_test.go#L93) for applications
111+
* [TestBlockWithTransactions()](https://github.com/algorand/indexer/blob/800cb135a0c6da0109e7282acf85cbe1961930c6/idb/postgres/postgres_integration_test.go#L339) setup state consisting of a set of basic transactions
112+
113+
## How to Make a _New Endpoint_ Testable by Fixtures Tests?
114+
115+
There are 2 steps:
116+
117+
1. Implement a new function _witness generator_ aka [prover function](https://github.com/algorand/indexer/blob/b955a31b10d8dce7177383895ed8e57206d69f67/api/fixtures_test.go#L103) of type `func(responseInfo) (interface{}, *string)` as examplified in [this section](https://github.com/algorand/indexer/blob/b955a31b10d8dce7177383895ed8e57206d69f67/api/fixtures_test.go#L107-L200). Such a function is supposed to parse an Indexer response's body into a generated model. Currently, all provers are boilerplate, and with generics, it's expected that this step will no longer be necessary (this [POC](https://github.com/tzaffi/indexer/blob/generic-boxes/api/fixtures_test.go#L119-L155) shows how it would be done with generics).
118+
2. Define a new route in the [proverRoutes struct](https://github.com/algorand/indexer/blob/b955a31b10d8dce7177383895ed8e57206d69f67/api/fixtures_test.go#L232_L263). This is a tree structure which is traversed by splitting a path using `/` and eventually reaching a leaf which consists of a `prover` as defined in #1.
119+
120+
For example, to enable the endpoint `GET /v2/applications/{application-id}/logs` for fixtures test, one need only define a `logsProof` witness generator and have it mapped in `proverRoutes` under:
121+
122+
```
123+
proverRoutes.parts["v2"].parts["applications"].parts[":application-id"].parts["logs"] = logsProof
124+
```

0 commit comments

Comments
 (0)