diff --git a/service/internal/db/db.go b/service/internal/db/db.go index dad02e65a3..598774278a 100644 --- a/service/internal/db/db.go +++ b/service/internal/db/db.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "net" + "net/url" sq "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v5" @@ -85,7 +86,7 @@ func NewClient(config Config) (*Client, error) { config: config, } - dbConfig, err := pgxpool.ParseConfig(config.buildURL()) + dbConfig, err := config.buildConfig() if err != nil { return nil, fmt.Errorf("failed to parse pgx config: %w", err) } @@ -121,14 +122,15 @@ func (c *Client) Close() { c.SqlDB.Close() } -func (c Config) buildURL() string { - return fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", +func (c Config) buildConfig() (*pgxpool.Config, error) { + u := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", c.User, - c.Password, + url.QueryEscape(c.Password), net.JoinHostPort(c.Host, fmt.Sprint(c.Port)), c.Database, c.SSLMode, ) + return pgxpool.ParseConfig(u) } // Common function for all queryRow calls diff --git a/service/internal/db/db_test.go b/service/internal/db/db_test.go index 28277155e0..0fd5d73996 100644 --- a/service/internal/db/db_test.go +++ b/service/internal/db/db_test.go @@ -1,88 +1,53 @@ package db -// var ( -// //nolint:gochecknoglobals // Test data and should be reintialized for each test -// resourceDescriptor = &common.ResourceDescriptor{ -// Name: "relto", -// Namespace: "opentdf", -// Version: 1, -// Fqn: "http://opentdf.com/attr/relto", -// Labels: map[string]string{"origin": "Country of Origin"}, -// Description: "The relto attribute is used to describe the relationship of the resource to the country of origin.", -// Type: common.PolicyResourceType_POLICY_RESOURCE_TYPE_RESOURCE_ENCODING_SYNONYM, -// } - -// //nolint:gochecknoglobals // Test data and should be reintialized for each test -// testResource = &acre.Synonyms{ -// Terms: []string{"relto", "rel-to", "rel_to"}, -// } -// ) - -// func Test_RunMigrations_Returns_Expected_Applied(t *testing.T) { -// client := &Client{ -// PgxIface: nil, -// config: Config{ -// RunMigrations: false, -// }, -// } - -// applied, err := client.RunMigrations() - -// require.NoError(t, err) -// assert.Equal(t, 0, applied) -// } - -// func Test_RunMigrations_Returns_Error_When_PGX_Iface_Is_Nil(t *testing.T) { -// client := &Client{ -// PgxIface: nil, -// config: Config{ -// RunMigrations: true, -// }, -// } - -// applied, err := client.RunMigrations() - -// assert.ErrorContains(t, err, "failed to cast pgxpool.Pool") -// assert.Equal(t, 0, applied) -// } - -// type BadPGX struct{} - -// func (b BadPGX) Acquire(_ context.Context) (*pgxpool.Conn, error) { return &pgxpool.Conn{}, nil } -// func (b BadPGX) Exec(context.Context, string, ...any) (pgconn.CommandTag, error) { -// return pgconn.CommandTag{}, nil -// } -// func (b BadPGX) QueryRow(context.Context, string, ...any) pgx.Row { return nil } -// func (b BadPGX) Query(context.Context, string, ...any) (pgx.Rows, error) { return nil, nil } -// func (b BadPGX) Ping(context.Context) error { return nil } -// func (b BadPGX) Close() {} -// func (b BadPGX) Config() *pgxpool.Config { return nil } - -// func Test_RunMigrations_Returns_Error_When_PGX_Iface_Is_Wrong_Type(t *testing.T) { -// client := &Client{ -// PgxIface: &BadPGX{}, -// config: Config{ -// RunMigrations: true, -// }, -// } - -// applied, err := client.RunMigrations() - -// assert.ErrorContains(t, err, "failed to cast pgxpool.Pool") -// assert.Equal(t, 0, applied) -// } - -// func Test_BuildURL_Returns_Expected_Connection_String(t *testing.T) { -// c := Config{ -// Host: "localhost", -// Port: 5432, -// Database: "opentdf", -// User: "postgres", -// Password: "postgres", -// SSLMode: "require", -// } - -// url := c.buildURL() - -// assert.Equal(t, "postgres://postgres:postgres@localhost:5432/opentdf?sslmode=require", url) -// } +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_BuildConfig(t *testing.T) { + tests := []struct { + config *Config + want string + }{ + { + config: &Config{ + Host: "localhost", + Port: 5432, + Database: "opentdf", + User: "postgres", + Password: "changeme", + }, + want: "postgres://postgres:changeme@localhost:5432/opentdf?sslmode=", + }, + { + config: &Config{ + Host: "localhost", + Port: 5432, + Database: "opentdf", + User: "postgres", + Password: "tes}t64@N0test;/-test/-z", + }, + want: "postgres://postgres:tes%7Dt64%40N0test%3B%2F-test%2F-z@localhost:5432/opentdf?sslmode=", + }, + { + config: &Config{ + Host: "localhost", + Port: 5432, + Database: "opentdf", + User: "postgres", + Password: "k!jBwK@$gn@M!ikpHo8SZ8", + SSLMode: "prefer", + }, + want: "postgres://postgres:k%21jBwK%40%24gn%40M%21ikpHo8SZ8@localhost:5432/opentdf?sslmode=prefer", + }, + } + + for _, test := range tests { + cfg, err := test.config.buildConfig() + require.NoError(t, err) + assert.Equal(t, test.want, cfg.ConnString()) + } +}