Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into 3268_test_db_cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
sreuland committed Jan 18, 2022
2 parents 54e6d9b + 73b2df1 commit da59f74
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 6 deletions.
61 changes: 61 additions & 0 deletions services/horizon/TESTING_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Writing effective test coverage for Horizon packages

Before discussing test development, just a brief mention of developer resoures:
- [Quickstart Guide](internal/docs/quickstart.md)
- [Developer Guide](internal/docs/developing.md)
- [Developer Notes](internal/docs/notes_for_developers.md)

Authoring tests to assert coverage is key importance, to facilitate best experience for writing tests within Horizon packages, there are some conventions to be aware of:

## Best Practices
* For unit tests:
* Adhere to [idiomatic go testing](https://go.dev/doc/tutorial/add-a-test) for
baseline

* Try to maintain a `_test.go` file co-located in same folder as the `.go`
sourcefile.

* Try to limit target code path under test. Use mocks on dependencies as much as
possible. Take dependencies into account during implementations, to encapsulate dependencies as much as possible through functional, interface, packaging, so they can be mocked out.

* Assert on functional output/results, try to avoid assert on other aspects as that
tends to lead to brittle tests.

* Do not use `services/horizon/internal/test/scenarios` DB setups, that framework is deprecated and will be EOL.

* For multi-table db seeding as part of test setup, use the newer notion of 'fixtures' for sql batch datasets. A 'fixture' is just a helper function that programatically loads DB from a hardcoded set of seed data and uses the session interface to do so. Refer to `services/horizon/internal/db2/history/trade_scenario.go` for example of a 'fixture' dataset.

* For integration tests, they should be located in services/horizon/integration package. Tests located in this package will only run when `HORIZON_INTEGRATION_TESTS=true` is present in environment.

## Leverage Scaffolding for Test Cases
* Mocked DB unit tests that avoid needing a live db connection:

* `db/mock_session.go` has pre-defined mocks of all standard SessionInterface. `services/horizon/internal/httpx/stream_handler_test.go` is good example of mocking out just low level db session interface where sql statements are executed.

* `services/horizon/internal/db2/history/mock_q_*.go`. This is a great set of mocked out horizon queries. Since this layer is mocked out, no need to deal with mocking any lower, i.e. the db session interfaces. `services/horizon/internal/ingest/processors/accounts_processor_test.go` is a good reference example of using these mocked out db query interfaces from tests.

* Live DB unit tests, if you don't want to spend time mocking out sql results, there is lightweight db helper scaffolding framework available from `services/horizon/internal/test` package, it'll wire up session interface to a real connection that it initiates to `postgres://localhost:5432/horizon?sslmode=disable`, so your test won't have any boilerplate for setup.

* `services/horizon/internal/db2/history/account_data_test.go` is a good example test that uses this live db scaffolding package. As part of test setup, it first inserts test data into tables using the same session interface.

* If your test requires alot of repetitive test data loaded up front, then consider using a 'fixture' function instead to reduce duplicated code and encourage reuse of the fixture across other tests. Good example of 'fixture' is `TradeScenario` function in `services/horizon/internal/db2/history/trade_scenario.go`. Refer to `services/horizon/internal/db2/history/trade_test.go` for example of test that uses the 'fixture' function.

* Live DB with Mocked Web unit tests, mainly for exercising web server controllers in `services/horizon/internal/actions` for a given url path. The tests for controllers are all located at `services/horizon/internal/actions_*_test.go`. A good example of a test that mocks up the web layer but has a live DB is `services/horizon/internal/actions_path_test.go`.

* Live DB/Web unit tests, this can be used in any test case, but should be used sparingly, as it basically incurs same resources as integration test, but it will be run as part of unit tests. Try to consider whether any mocked levels of unit test can exercise the same target code path first. A good example of test that runs live web and db is `services/horizon/internal/actions_data_test.go`. Note how it uses `StartHTTPTestWithoutScenario` which explicitly avoids usage of deprecated scenarios framework. The test first inserts data into DB through session interfaces, then executes the web layer and checks responses.

* Live DB/Web integration tests, there is a helper scaffolding framework from `services/horizon/internal/test/integration`:

* `services/horizon/internal/integration/clawback_test.go` is good example of integration test that uses the scaffolding.

* integration tests only execute when `HORIZON_INTEGRATION_TESTS=true` is present as environment variable.










22 changes: 17 additions & 5 deletions services/horizon/internal/docs/notes_for_developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This document contains additional information related to the development of Hori

- [Initial set up](#setup)
- [Regenerating generated code](#regen)
- [Writing tests](#author_tests)
- [Running tests](#tests)
- [Logging](#logging)
- [Adding migrations](#migrations)
Expand Down Expand Up @@ -44,17 +45,17 @@ payment :scott, :bartek, [:native, 5]

You can find more recipes in [`scc` examples](https://github.com/stellar/stellar_core_commander/tree/84d5ffb97202ecc3a0ed34a739c98e69536c0c2c/examples).

Scenarios are in [horizon test scenarios](https://github.com/stellar/go/tree/master/services/horizon/internal/test/scenarios). They are
used by many different integration tests.

### Deprecated Scenario sql files

1. Scenario .sql files are located in services/horizon/internal/test/scenarios and have been used in unit and integeration tests, however, they are deprecated and are not meant to be used or included in new development. They were manually maintained and have not been updated with more recent db schema changes and are not associated with db migrations.
Scenarios are in [horizon test scenarios](https://github.com/stellar/go/tree/master/services/horizon/internal/test/scenarios). They are used by many different integration tests, however, they are deprecated and are not meant to be used or included in new development. They were manually maintained and have not been updated with more recent db schema changes and are not associated with db migrations.

## <a name="author_tests"></a> Writing Tests

When authoring tests, refer to [Testing Best Practices](../../TESTING_README.md) for context.

## <a name="tests"></a> Running Tests

run the all the Go monorepo tests like so (assuming you are at stellar/go, or run from stellar/go/services/horizon for just the Horizon subset):
Run all the Go monorepo unit tests like so (assuming you are at stellar/go, or run from stellar/go/services/horizon for just the Horizon subset):

```bash
go test ./...
Expand All @@ -66,6 +67,17 @@ or run individual Horizon tests like so, providing the expected arguments:
go test github.com/stellar/go/services/horizon/...
```

To run the integration tests, move to top folder of working copy of `go` repo to run all integration tests
or /services/horizon to run just Horizon integration tests:
```
HORIZON_INTEGRATION_TESTS=true go test -race -timeout 25m -v ./...
```

To run just one specific integration test, e.g. like `TestTxSub`:
```
HORIZON_INTEGRATION_TESTS=true go test -run TestTxsub -race -timeout 25m -v ./...
```

## <a name="logging"></a> Logging

All logging infrastructure is in the `github.com/stellar/go/tree/master/services/horizon/internal/log` package. This package provides "level-based" logging: Each logging statement has a severity, one of "Debug", "Info", "Warn", "Error" or "Panic". The Horizon server has a configured level "filter", specified either using the `--log-level` command line flag or the `LOG_LEVEL` environment variable. When a logging statement is executed, the statements declared severity is checked against the filter and will only be emitted if the severity of the statement is equal or higher severity than the filter.
Expand Down
2 changes: 1 addition & 1 deletion services/horizon/internal/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

func mustNewDBSession(subservice db.Subservice, databaseURL string, maxIdle, maxOpen int, registry *prometheus.Registry) db.SessionInterface {
log.Infof("Establishing database session at %s for %v", databaseURL, subservice)
log.Infof("Establishing database session for %v", subservice)
session, err := db.Open("postgres", databaseURL)
if err != nil {
log.Fatalf("cannot open %v DB: %v", subservice, err)
Expand Down

0 comments on commit da59f74

Please sign in to comment.