Skip to content

Commit

Permalink
#4418: added DSN for read-only role on db connections in unit tests, …
Browse files Browse the repository at this point in the history
…switched the internal/action_* tests to use the read only db connection.
  • Loading branch information
sreuland committed Jun 8, 2022
1 parent f7b770a commit 32f17c6
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 74 deletions.
11 changes: 6 additions & 5 deletions services/horizon/internal/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ import (

"github.com/stellar/go/network"
"github.com/stellar/go/services/horizon/internal/test"
tdb "github.com/stellar/go/services/horizon/internal/test/db"
supportLog "github.com/stellar/go/support/log"
)

func NewTestApp() *App {
app, err := NewApp(NewTestConfig())
func NewTestApp(dsn string) *App {
app, err := NewApp(NewTestConfig(dsn))
if err != nil {
log.Fatal("cannot create app", err)
}
return app
}

func NewTestConfig() Config {
func NewTestConfig(dsn string) Config {
return Config{
DatabaseURL: test.DatabaseURL(),
StellarCoreDatabaseURL: test.StellarCoreDatabaseURL(),
DatabaseURL: dsn,
StellarCoreDatabaseURL: tdb.StellarCoreURL(),
RateQuota: &throttled.RateQuota{
MaxRate: throttled.PerHour(1000),
MaxBurst: 100,
Expand Down
5 changes: 4 additions & 1 deletion services/horizon/internal/httpt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"net/url"
"testing"

"github.com/stellar/go/services/horizon/internal/db2/history"
"github.com/stellar/go/services/horizon/internal/test"
tdb "github.com/stellar/go/services/horizon/internal/test/db"
)

type HTTPT struct {
Expand All @@ -25,7 +27,7 @@ func startHTTPTest(t *testing.T, scenario string) *HTTPT {
} else {
ret.Scenario(scenario)
}
ret.App = NewTestApp()
ret.App = NewTestApp(tdb.HorizonURL())
ret.RH = test.NewRequestHelper(ret.App.webServer.Router.Mux)
ret.Assert = &test.Assertions{ret.T.Assert}

Expand Down Expand Up @@ -102,6 +104,7 @@ func (ht *HTTPT) Post(
// setting the retention count to the provided number.
func (ht *HTTPT) ReapHistory(retention uint) {
ht.App.reaper.RetentionCount = retention
ht.App.reaper.HistoryQ = &history.Q{ht.HorizonSession()}
err := ht.App.DeleteUnretainedHistory(context.Background())
ht.Require.NoError(err)
ht.App.UpdateCoreLedgerState(context.Background())
Expand Down
3 changes: 2 additions & 1 deletion services/horizon/internal/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/stellar/go/services/horizon/internal/ledger"
hProblem "github.com/stellar/go/services/horizon/internal/render/problem"
"github.com/stellar/go/services/horizon/internal/test"
tdb "github.com/stellar/go/services/horizon/internal/test/db"
"github.com/stellar/go/support/db"
"github.com/stellar/go/support/log"
"github.com/stellar/go/xdr"
Expand Down Expand Up @@ -52,7 +53,7 @@ func (suite *RateLimitMiddlewareTestSuite) SetupSuite() {
}

func (suite *RateLimitMiddlewareTestSuite) SetupTest() {
suite.c = NewTestConfig()
suite.c = NewTestConfig(tdb.HorizonURL())
suite.c.RateQuota = &throttled.RateQuota{
MaxRate: throttled.PerHour(10),
MaxBurst: 9,
Expand Down
65 changes: 39 additions & 26 deletions services/horizon/internal/test/db/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,69 @@ import (
"log"
"testing"

"github.com/jmoiron/sqlx"
// pq enables postgres support
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
db "github.com/stellar/go/support/db/dbtest"
)

var (
coreDB *sqlx.DB
coreUrl *string
horizonDB *sqlx.DB
horizonUrl *string
horizonDB *db.DB
coreDB *db.DB
coreDBConn *sqlx.DB
horizonDBConn *sqlx.DB
)

// Horizon returns a connection to the horizon test database
func Horizon(t *testing.T) *sqlx.DB {
func horizonPostgres(t *testing.T) *db.DB {
if horizonDB != nil {
return horizonDB
}
postgres := db.Postgres(t)
horizonUrl = &postgres.DSN
horizonDB = postgres.Open()

horizonDB = db.Postgres(t)
return horizonDB
}

// HorizonURL returns the database connection the url any test
// use when connecting to the history/horizon database
func corePostgres(t *testing.T) *db.DB {
if coreDB != nil {
return coreDB
}
coreDB = db.Postgres(t)
return coreDB
}

func Horizon(t *testing.T) *sqlx.DB {
if horizonDBConn != nil {
return horizonDBConn
}

horizonDBConn = horizonPostgres(t).Open()
return horizonDBConn
}

func HorizonURL() string {
if horizonUrl == nil {
if horizonDB == nil {
log.Panic(fmt.Errorf("Horizon not initialized"))
}
return *horizonUrl
return horizonDB.DSN
}

func HorizonROURL() string {
if horizonDB == nil {
log.Panic(fmt.Errorf("Horizon not initialized"))
}
return horizonDB.RO_DSN
}

// StellarCore returns a connection to the stellar core test database
func StellarCore(t *testing.T) *sqlx.DB {
if coreDB != nil {
return coreDB
if coreDBConn != nil {
return coreDBConn
}
postgres := db.Postgres(t)
coreUrl = &postgres.DSN
coreDB = postgres.Open()
return coreDB
coreDBConn = corePostgres(t).Open()
return coreDBConn
}

// StellarCoreURL returns the database connection the url any test
// use when connecting to the stellar-core database
func StellarCoreURL() string {
if coreUrl == nil {
if coreDB == nil {
log.Panic(fmt.Errorf("StellarCore not initialized"))
}
return *coreUrl
return coreDB.DSN
}
34 changes: 2 additions & 32 deletions services/horizon/internal/test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,6 @@ func Context() context.Context {
return log.Set(context.Background(), testLogger)
}

// Database returns a connection to the horizon test database
//
// DEPRECATED: use `Horizon()` from test/db package
func Database(t *testing.T) *sqlx.DB {
return tdb.Horizon(t)
}

// DatabaseURL returns the database connection the url any test
// use when connecting to the history/horizon database
//
// DEPRECATED: use `HorizonURL()` from test/db package
func DatabaseURL() string {
return tdb.HorizonURL()
}

// Start initializes a new test helper object, a new instance of log,
// and conceptually "starts" a new test
func Start(t *testing.T) *T {
Expand All @@ -64,26 +49,11 @@ func Start(t *testing.T) *T {
logger := log.New()

result.Ctx = log.Set(context.Background(), logger)
result.HorizonDB = Database(t)
result.CoreDB = StellarCoreDatabase(t)
result.HorizonDB = tdb.Horizon(t)
result.CoreDB = tdb.StellarCore(t)
result.Assert = assert.New(t)
result.Require = require.New(t)
result.EndLogTest = logger.StartTest(log.DebugLevel)

return result
}

// StellarCoreDatabase returns a connection to the stellar core test database
//
// DEPRECATED: use `StellarCore()` from test/db package
func StellarCoreDatabase(t *testing.T) *sqlx.DB {
return tdb.StellarCore(t)
}

// StellarCoreDatabaseURL returns the database connection the url any test
// use when connecting to the stellar-core database
//
// DEPRECATED: use `StellarCoreURL()` from test/db package
func StellarCoreDatabaseURL() string {
return tdb.StellarCoreURL()
}
5 changes: 5 additions & 0 deletions services/horizon/internal/test/scenarios/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ func Load(url string, path string) {
if err != nil {
log.Fatalf("could not exec scenario %v: %v\n", path, err)
}

// facilitates 'user_ro' db connecction for read only usage in tests
db.Exec("GRANT USAGE ON SCHEMA public TO PUBLIC;")
db.Exec("GRANT SELECT ON ALL TABLES IN SCHEMA public TO PUBLIC;")
db.Exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;")
}
5 changes: 3 additions & 2 deletions services/horizon/internal/test/t.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/stellar/go/services/horizon/internal/db2/schema"
"github.com/stellar/go/services/horizon/internal/ledger"
"github.com/stellar/go/services/horizon/internal/operationfeestats"
tdb "github.com/stellar/go/services/horizon/internal/test/db"
"github.com/stellar/go/services/horizon/internal/test/scenarios"
"github.com/stellar/go/support/db"
"github.com/stellar/go/support/render/hal"
Expand Down Expand Up @@ -51,11 +52,11 @@ func (t *T) HorizonSession() *db.Session {
func (t *T) loadScenario(scenarioName string, includeHorizon bool) {
stellarCorePath := scenarioName + "-core.sql"

scenarios.Load(StellarCoreDatabaseURL(), stellarCorePath)
scenarios.Load(tdb.StellarCoreURL(), stellarCorePath)

if includeHorizon {
horizonPath := scenarioName + "-horizon.sql"
scenarios.Load(DatabaseURL(), horizonPath)
scenarios.Load(tdb.HorizonURL(), horizonPath)
}
}

Expand Down
40 changes: 33 additions & 7 deletions support/db/dbtest/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dbtest

import (
"crypto/rand"

"encoding/hex"
"fmt"
"os"
Expand All @@ -21,6 +22,7 @@ import (
type DB struct {
Dialect string
DSN string
RO_DSN string
dbName string
t testing.TB
closer func()
Expand Down Expand Up @@ -101,14 +103,28 @@ func (db *DB) Version() (major int) {
return major
}

func execStatement(t testing.TB, pguser, query string) {
db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s@localhost/?sslmode=disable", pguser))
func execStatement(t testing.TB, query string, DSN string) {
db, err := sqlx.Open("postgres", DSN)
require.NoError(t, err)
_, err = db.Exec(query)
require.NoError(t, err)
require.NoError(t, db.Close())
}

func checkReadOnly(t testing.TB, query string, DSN string) bool {
db, err := sqlx.Open("postgres", DSN)
require.NoError(t, err)
rows, err := db.Query(query)
require.NoError(t, err)
hasRORole := false
if rows.Next() {
hasRORole = true
}
require.NoError(t, rows.Close())
require.NoError(t, db.Close())
return hasRORole
}

// Postgres provisions a new, blank database with a random name on the localhost
// of the running process. It assumes that you have postgres running on the
// default port, have the command line postgres tools installed, and that the
Expand All @@ -126,16 +142,26 @@ func Postgres(t testing.TB) *DB {
pgUser = "postgres"
}

// create the db
execStatement(t, pgUser, "CREATE DATABASE "+pq.QuoteIdentifier(result.dbName))

postgresDSN := fmt.Sprintf("postgres://%s@localhost/?sslmode=disable", pgUser)
result.DSN = fmt.Sprintf("postgres://%s@localhost/%s?sslmode=disable&timezone=UTC", pgUser, result.dbName)
result.RO_DSN = fmt.Sprintf("postgres://%s@localhost/%s?sslmode=disable&timezone=UTC", "user_ro", result.dbName)

execStatement(t, fmt.Sprintf("CREATE DATABASE %s;", result.dbName), postgresDSN)
execStatement(t, fmt.Sprintf("GRANT CONNECT ON DATABASE %s TO PUBLIC;", result.dbName), postgresDSN)
execStatement(t, "GRANT USAGE ON SCHEMA public TO PUBLIC;", result.DSN)
execStatement(t, "GRANT SELECT ON ALL TABLES IN SCHEMA public TO PUBLIC;", result.DSN)
execStatement(t, "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;", result.DSN)

hasRORole := checkReadOnly(t, "SELECT FROM pg_user WHERE usename = 'user_ro'", postgresDSN)
if !hasRORole {
execStatement(t, "CREATE ROLE user_ro LOGIN;", postgresDSN)
}

result.closer = func() {
// pg_terminate_backend is a best effort, it does not gaurantee that it can close any lingering connections
// it sends a quit signal to each remaining connection in the db
execStatement(t, pgUser, "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '"+pq.QuoteIdentifier(result.dbName)+"';")
execStatement(t, pgUser, "DROP DATABASE "+pq.QuoteIdentifier(result.dbName))
execStatement(t, "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '"+pq.QuoteIdentifier(result.dbName)+"';", postgresDSN)
execStatement(t, "DROP DATABASE "+pq.QuoteIdentifier(result.dbName), postgresDSN)
}

return &result
Expand Down

0 comments on commit 32f17c6

Please sign in to comment.