lib/pq has built-in support to use pgpass file for authentication when no password has been provided. However this requires that the connection does not contain the password parameter at all. Removing password parameter when postgresql password is empty in SQL store.
202 lines
5.5 KiB
Go
202 lines
5.5 KiB
Go
package sqlstore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
type sqlStoreTest struct {
|
|
name string
|
|
dbType string
|
|
dbHost string
|
|
dbURL string
|
|
dbUser string
|
|
dbPwd string
|
|
connStrValues []string
|
|
connStrExcludedValues []string
|
|
err error
|
|
}
|
|
|
|
var sqlStoreTestCases = []sqlStoreTest{
|
|
{
|
|
name: "MySQL IPv4",
|
|
dbType: "mysql",
|
|
dbHost: "1.2.3.4:5678",
|
|
connStrValues: []string{"tcp(1.2.3.4:5678)"},
|
|
},
|
|
{
|
|
name: "Postgres IPv4",
|
|
dbType: "postgres",
|
|
dbHost: "1.2.3.4:5678",
|
|
connStrValues: []string{"host=1.2.3.4", "port=5678"},
|
|
},
|
|
{
|
|
name: "Postgres IPv4 (Default Port)",
|
|
dbType: "postgres",
|
|
dbHost: "1.2.3.4",
|
|
connStrValues: []string{"host=1.2.3.4", "port=5432"},
|
|
},
|
|
{
|
|
name: "Postgres username and password",
|
|
dbType: "postgres",
|
|
dbHost: "1.2.3.4",
|
|
dbUser: "grafana",
|
|
dbPwd: "password",
|
|
connStrValues: []string{"host=1.2.3.4", "port=5432", "user=grafana", "password=password"},
|
|
},
|
|
{
|
|
name: "Postgres username no password",
|
|
dbType: "postgres",
|
|
dbHost: "1.2.3.4",
|
|
dbUser: "grafana",
|
|
dbPwd: "",
|
|
connStrValues: []string{"host=1.2.3.4", "port=5432", "user=grafana"},
|
|
connStrExcludedValues: []string{"password"},
|
|
},
|
|
{
|
|
name: "MySQL IPv4 (Default Port)",
|
|
dbType: "mysql",
|
|
dbHost: "1.2.3.4",
|
|
connStrValues: []string{"tcp(1.2.3.4)"},
|
|
},
|
|
{
|
|
name: "MySQL IPv6",
|
|
dbType: "mysql",
|
|
dbHost: "[fe80::24e8:31b2:91df:b177]:1234",
|
|
connStrValues: []string{"tcp([fe80::24e8:31b2:91df:b177]:1234)"},
|
|
},
|
|
{
|
|
name: "Postgres IPv6",
|
|
dbType: "postgres",
|
|
dbHost: "[fe80::24e8:31b2:91df:b177]:1234",
|
|
connStrValues: []string{"host=fe80::24e8:31b2:91df:b177", "port=1234"},
|
|
},
|
|
{
|
|
name: "MySQL IPv6 (Default Port)",
|
|
dbType: "mysql",
|
|
dbHost: "[::1]",
|
|
connStrValues: []string{"tcp([::1])"},
|
|
},
|
|
{
|
|
name: "Postgres IPv6 (Default Port)",
|
|
dbType: "postgres",
|
|
dbHost: "[::1]",
|
|
connStrValues: []string{"host=::1", "port=5432"},
|
|
},
|
|
{
|
|
name: "Invalid database URL",
|
|
dbURL: "://invalid.com/",
|
|
err: &url.Error{Op: "parse", URL: "://invalid.com/", Err: errors.New("missing protocol scheme")},
|
|
},
|
|
{
|
|
name: "Sql mode set to ANSI_QUOTES",
|
|
dbType: "mysql",
|
|
dbHost: "[::1]",
|
|
connStrValues: []string{"sql_mode='ANSI_QUOTES'"},
|
|
},
|
|
}
|
|
|
|
func TestIntegrationSQLConnectionString(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
for _, testCase := range sqlStoreTestCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
sqlstore := &SQLStore{}
|
|
sqlstore.Cfg = makeSQLStoreTestConfig(t, testCase.dbType, testCase.dbHost, testCase.dbUser, testCase.dbPwd, testCase.dbURL)
|
|
connStr, err := sqlstore.buildConnectionString()
|
|
require.Equal(t, testCase.err, err)
|
|
|
|
for _, connSubStr := range testCase.connStrValues {
|
|
require.Contains(t, connStr, connSubStr)
|
|
}
|
|
|
|
for _, connExcludedSubStr := range testCase.connStrExcludedValues {
|
|
require.NotContains(t, connStr, connExcludedSubStr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegrationIsUniqueConstraintViolation(t *testing.T) {
|
|
store := InitTestDB(t)
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
f func(*testing.T, *DBSession) error
|
|
}{
|
|
{
|
|
desc: "successfully detect primary key violations",
|
|
f: func(t *testing.T, sess *DBSession) error {
|
|
// Attempt to insert org with provided ID (primary key) twice
|
|
now := time.Now()
|
|
org := org.Org{Name: "test org primary key violation", Created: now, Updated: now, ID: 42}
|
|
err := sess.InsertId(&org, store.Dialect)
|
|
require.NoError(t, err)
|
|
|
|
// Provide a different name to avoid unique constraint violation
|
|
org.Name = "test org 2"
|
|
return sess.InsertId(&org, store.Dialect)
|
|
},
|
|
},
|
|
{
|
|
desc: "successfully detect unique constrain violations",
|
|
f: func(t *testing.T, sess *DBSession) error {
|
|
// Attempt to insert org with reserved name
|
|
now := time.Now()
|
|
org := org.Org{Name: "test org unique constrain violation", Created: now, Updated: now, ID: 43}
|
|
err := sess.InsertId(&org, store.Dialect)
|
|
require.NoError(t, err)
|
|
|
|
// Provide a different ID to avoid primary key violation
|
|
org.ID = 44
|
|
return sess.InsertId(&org, store.Dialect)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
err := store.WithDbSession(context.Background(), func(sess *DBSession) error {
|
|
return tc.f(t, sess)
|
|
})
|
|
require.Error(t, err)
|
|
assert.True(t, store.Dialect.IsUniqueConstraintViolation(err))
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeSQLStoreTestConfig(t *testing.T, dbType, host, user, password, dbURL string) *setting.Cfg {
|
|
t.Helper()
|
|
|
|
cfg := setting.NewCfg()
|
|
|
|
sec, err := cfg.Raw.NewSection("database")
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("type", dbType)
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("host", host)
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("url", dbURL)
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("user", user)
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("name", "test_db")
|
|
require.NoError(t, err)
|
|
_, err = sec.NewKey("password", password)
|
|
require.NoError(t, err)
|
|
|
|
cfg.IsFeatureToggleEnabled = func(key string) bool { return true }
|
|
|
|
return cfg
|
|
}
|