Skip to content

Commit

Permalink
Add PostgreSQL support to stock-persistence module
Browse files Browse the repository at this point in the history
This commit introduces PostgreSQL support to the stock-persistence module, enables database connection testing and handles connection errors. New Go files added under the infrastructure/database and database/postgres directories implement the database functionality. The module now depends on the lib/pq PostgreSQL driver and the go-sqlmock library for mocking SQL queries during testing.
  • Loading branch information
Helgart committed Apr 3, 2024
1 parent 91ea84a commit c917aa4
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 0 deletions.
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/Helgart/stock-persistence

go 1.22.1

require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/lib/pq v1.10.9
)
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
60 changes: 60 additions & 0 deletions infrastructure/database/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package database

import (
"database/sql"
"fmt"
)

// connexionErrorMessage represents the error message format for a database connection error.
const connexionErrorMessage = "Database connexion error (%s@%s:%d/%s) : %s"

// Database is a type that represents a database connection.
type Database struct {
connexion *sql.DB
}

// DatabaseParameters represents the parameters used to establish a database connection.
type DatabaseParameters struct {
Host string
Port uint
User string
Password string
Name string
}

// ConnexionError represents an error that occurred during database connection.
type ConnexionError struct {
PreviousError error
PsqlInfo string
DatabaseParameters DatabaseParameters
}

// Error returns the error message representation of a ConnexionError.
func (connexionError ConnexionError) Error() string {
return fmt.Sprintf(
connexionErrorMessage,
connexionError.DatabaseParameters.User,
connexionError.DatabaseParameters.Host,
connexionError.DatabaseParameters.Port,
connexionError.DatabaseParameters.Name,
connexionError.PreviousError.Error(),
)
}

// NewDatabase creates a new Database struct with the provided connection.
// It takes a pointer to sql.DB and returns a Database instance.
func NewDatabase(connexion *sql.DB) Database {
return Database{connexion: connexion}
}

// Ping pings the database to check its availability.
// It returns an error if the ping fails.
func (database *Database) Ping() error {
err := database.connexion.Ping()

if err != nil {
return err
}

return nil
}
80 changes: 80 additions & 0 deletions infrastructure/database/connection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package database

import (
"database/sql"
"fmt"
"testing"

sqlmock "github.com/DATA-DOG/go-sqlmock"
)

func TestDatabase_Ping(t *testing.T) {
tests := []struct {
name string
setup func(mock sqlmock.Sqlmock)
wantErr bool
}{
{
name: "Success",
setup: func(mock sqlmock.Sqlmock) {
mock.ExpectPing().WillReturnError(nil)
},
wantErr: false,
},
{
name: "Failure",
setup: func(mock sqlmock.Sqlmock) {
mock.ExpectPing().WillReturnError(sql.ErrConnDone)
},
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db, mock, err := sqlmock.New(sqlmock.MonitorPingsOption(true))

if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}

tt.setup(mock)

database := NewDatabase(db)

if err := database.Ping(); (err != nil) != tt.wantErr {
t.Errorf("Database.Ping() error = %v, wantErr %v", err, tt.wantErr)
}

if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("Database.Ping() there were unfulfilled expectations: %s", err)
}
})
}
}

func TestConnexionError_Error(t *testing.T) {
tests := []struct {
name string
err ConnexionError
want string
}{
{
name: "Error Message",
err: ConnexionError{
PreviousError: fmt.Errorf("previousError"),
PsqlInfo: "psqlInfo",
DatabaseParameters: DatabaseParameters{User: "user", Host: "host", Port: 9999, Name: "name"},
},
want: fmt.Sprintf(connexionErrorMessage, "user", "host", 9999, "name", "previousError"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.err.Error(); got != tt.want {
t.Errorf("ConnexionError.Error() = %v, want %v", got, tt.want)
}
})
}
}
43 changes: 43 additions & 0 deletions infrastructure/database/postgres/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package postgres

import (
"database/sql"
"fmt"
"github.com/Helgart/stock-persistence/infrastructure/database"

_ "github.com/lib/pq"
)

const infoFormat = "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable"
const driver = "postgres"

func NewDatabase(databaseParameters database.DatabaseParameters) (database.Database, error) {
psqlInfo := fmt.Sprintf(
infoFormat,
databaseParameters.Host,
databaseParameters.Port,
databaseParameters.User,
databaseParameters.Password,
databaseParameters.Name,
)

connexion, err := sql.Open(driver, psqlInfo)

if err != nil {
return database.Database{}, database.ConnexionError{
PreviousError: err,
PsqlInfo: psqlInfo,
DatabaseParameters: databaseParameters,
}
}

if err := connexion.Ping(); err != nil {
return database.Database{}, database.ConnexionError{
PreviousError: err,
PsqlInfo: psqlInfo,
DatabaseParameters: databaseParameters,
}
}

return database.NewDatabase(connexion), nil
}

0 comments on commit c917aa4

Please sign in to comment.