Skip to content

Commit

Permalink
Merge pull request #40 from Zapharaos/3-tests
Browse files Browse the repository at this point in the history
feat(#3 tests): backend api unit tests
  • Loading branch information
Zapharaos authored Feb 10, 2025
2 parents eae5a7e + 70eebad commit 7443941
Show file tree
Hide file tree
Showing 120 changed files with 15,432 additions and 750 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: go build -v ./...

- name: Test with coverage
run: go test -v -coverprofile=coverage.out ./...
run: go test -race -suffle=on -v -coverprofile=coverage.out ./...

- name: Upload results to Codecov
uses: codecov/codecov-action@v5
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
![GitHub Release](https://img.shields.io/github/v/release/zapharaos/fihub-backend)
<a href="https://github.com/go-goyave/goyave/actions"><img src="https://github.com/go-goyave/goyave/workflows/CI/badge.svg" alt="Build Status"/></a>
[![codecov](https://codecov.io/gh/Zapharaos/fihub-backend/graph/badge.svg?token=BL7YP0GTK9)](https://codecov.io/gh/Zapharaos/fihub-backend)

![GitHub License](https://img.shields.io/github/license/zapharaos/fihub-backend)
[![Go Report Card](https://goreportcard.com/badge/github.com/Zapharaos/fihub-backend)](https://goreportcard.com/report/github.com/Zapharaos/fihub-backend)

# fihub-backend

## Development
Expand Down
14 changes: 10 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/Zapharaos/fihub-backend
go 1.23.3

require (
github.com/BurntSushi/toml v1.4.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/go-chi/httprate v0.14.1
Expand All @@ -11,17 +12,22 @@ require (
github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
github.com/nicksnyder/go-i18n/v2 v2.5.0
github.com/sendgrid/rest v2.6.9+incompatible
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
github.com/stretchr/testify v1.10.0
github.com/zhashkevych/go-sqlxmock v1.5.2-0.20201023121933-f973d0041cfc
go.uber.org/mock v0.5.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.32.0
golang.org/x/text v0.21.0
)

require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.0 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
19 changes: 15 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,23 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs=
github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
Expand All @@ -35,10 +40,14 @@ github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekuei
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zhashkevych/go-sqlxmock v1.5.2-0.20201023121933-f973d0041cfc h1:z6oWvrg2brc98tlcDChukX4BKc3t0Ayz9dSBtJRYw9w=
github.com/zhashkevych/go-sqlxmock v1.5.2-0.20201023121933-f973d0041cfc/go.mod h1:kgQytrOB1XCQEsf5P1GpvvmjRkJhrORDtR/jvxKEQBw=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
Expand All @@ -47,9 +56,11 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
53 changes: 19 additions & 34 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/Zapharaos/fihub-backend/pkg/email"
"github.com/Zapharaos/fihub-backend/pkg/env"
"github.com/Zapharaos/fihub-backend/pkg/translation"
"github.com/jmoiron/sqlx"
"github.com/joho/godotenv"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/text/language"
Expand All @@ -28,7 +30,7 @@ var (
func Init() {

// Load the .env file
err := env.Load()
err := godotenv.Load()
if err != nil {
log.Fatal(err)
return
Expand All @@ -40,8 +42,7 @@ func Init() {
zap.L().Info("Starting Fihub Backend", zap.String("version", Version), zap.String("build_date", BuildDate))

// Setup Database
initPostgres()
initRepositories()
initDatabase()

// Setup Email
email.ReplaceGlobals(email.NewSendgridService())
Expand Down Expand Up @@ -100,39 +101,23 @@ func initLogger() zap.Config {
return zapConfig
}

// initPostgres initializes the postgres connection.
func initPostgres() {

zap.L().Info("Initializing Postgres")

// Configure postgres
credentials := postgres.Credentials{
Host: env.GetString("POSTGRES_HOST", "host"),
Port: env.GetString("POSTGRES_PORT", "port"),
DbName: env.GetString("POSTGRES_DB", "database_name"),
User: env.GetString("POSTGRES_USER", "user"),
// initDatabase initializes the database connections.
func initDatabase() {
postgres := database.NewPostgresDB(database.NewSqlDatabase(database.SqlCredentials{
Host: env.GetString("POSTGRES_HOST", "localhost"),
Port: env.GetString("POSTGRES_PORT", "5432"),
User: env.GetString("POSTGRES_USER", "postgres"),
Password: env.GetString("POSTGRES_PASSWORD", "password"),
}

// Connect
dbClient, err := postgres.DbConnection(credentials)
if err != nil {
zap.L().Fatal("main.DbConnection:", zap.Error(err))
}
DbName: env.GetString("POSTGRES_DB", "postgres"),
}))
database.ReplaceGlobals(database.NewDatabases(postgres))

zap.L().Info("Connected to Postgres")

// Finish up configuration
dbClient.SetMaxOpenConns(env.GetInt("POSTGRES_MAX_OPEN_CONNS", 30))
dbClient.SetMaxIdleConns(env.GetInt("POSTGRES_MAX_IDLE_CONNS", 30))
postgres.ReplaceGlobals(dbClient)
// Initialize the postgres repositories
initPostgres(database.DB().Postgres())
}

// initRepositories initializes the repositories. (postgres)
func initRepositories() {
// Setup for postgres
dbClient := postgres.DB()

// initPostgres initializes the postgres repositories.
func initPostgres(dbClient *sqlx.DB) {
// Auth
users.ReplaceGlobals(users.NewPostgresRepository(dbClient))
password.ReplaceGlobals(password.NewPostgresRepository(dbClient))
Expand All @@ -145,8 +130,8 @@ func initRepositories() {

// Brokers
brokerRepository := brokers.NewPostgresRepository(dbClient)
userBrokerRepository := brokers.NewUserBrokerPostgresRepository(dbClient)
imageBrokerRepository := brokers.NewImageBrokerPostgresRepository(dbClient)
userBrokerRepository := brokers.NewUserPostgresRepository(dbClient)
imageBrokerRepository := brokers.NewImagePostgresRepository(dbClient)
brokers.ReplaceGlobals(brokers.NewRepository(brokerRepository, userBrokerRepository, imageBrokerRepository))

// Transactions
Expand Down
84 changes: 84 additions & 0 deletions internal/app/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package app

import (
"github.com/Zapharaos/fihub-backend/internal/auth/password"
"github.com/Zapharaos/fihub-backend/internal/auth/permissions"
"github.com/Zapharaos/fihub-backend/internal/auth/roles"
"github.com/Zapharaos/fihub-backend/internal/auth/users"
"github.com/Zapharaos/fihub-backend/internal/brokers"
"github.com/Zapharaos/fihub-backend/internal/database"
"github.com/Zapharaos/fihub-backend/internal/transactions"
"github.com/Zapharaos/fihub-backend/pkg/email"
"github.com/Zapharaos/fihub-backend/pkg/translation"
"github.com/Zapharaos/fihub-backend/test"
"github.com/stretchr/testify/assert"
sqlmock "github.com/zhashkevych/go-sqlxmock"
"go.uber.org/zap"
"testing"
)

// TestInit tests the Init function to ensure that it correctly initializes the application.
// This test only verifies : env, email, translation.
// Any further function calls within Init are tested by their respective tests.
func TestInit(t *testing.T) {
// Create a full test suite
ts := test.TestSuite{}
_ = ts.CreateConfigTranslationsFullTestSuite(t)
defer ts.CleanTestSuite(t)

// Call Init function
Init()

// Assertions to verify initialization
assert.NotNil(t, email.S(), "Email service should be initialized")
assert.NotNil(t, translation.S(), "Translation service should be initialized")
}

// TestInitLogger tests the initLogger function to ensure that it correctly initializes the logger.
func TestInitLogger(t *testing.T) {
// Create a full test suite
ts := test.TestSuite{}
_ = ts.CreateFullTestSuite(t)
defer ts.CleanTestSuite(t)

// Call initLogger function
initLogger()

// Assertions to verify logger configuration
assert.NotNil(t, zap.L(), "Logger should be initialized")
}

// TestInit tests the initDatabase function to ensure that it correctly initializes the database.
// This test only verifies the database.
// Any further function calls within initDatabase are tested by their respective tests.
func TestInitDatabase(t *testing.T) {
// Create a full test suite
ts := test.TestSuite{}
_ = ts.CreateFullTestSuite(t)
defer ts.CleanTestSuite(t)

// Call initDatabase function
initDatabase()

// Assertions to verify Database initialization
assert.NotNil(t, database.DB())
}

// TestInitPostgres tests the initPostgres function to ensure that it correctly initializes the repositories.
func TestInitPostgres(t *testing.T) {
// Simulate a successful connection
sqlxMock, _, err := sqlmock.Newx()
assert.NoError(t, err)
defer sqlxMock.Close()

// Call initPostgres function with the mock connection
initPostgres(sqlxMock)

// Assertions to verify repositories initialization
assert.NotNil(t, users.R(), "Users repository should be initialized")
assert.NotNil(t, password.R(), "Password repository should be initialized")
assert.NotNil(t, roles.R(), "Roles repository should be initialized")
assert.NotNil(t, permissions.R(), "Permissions repository should be initialized")
assert.NotNil(t, brokers.R(), "Brokers repository should be initialized")
assert.NotNil(t, transactions.R(), "Transactions repository should be initialized")
}
28 changes: 16 additions & 12 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package auth
import (
"context"
"encoding/json"
"fmt"
"errors"
"github.com/Zapharaos/fihub-backend/internal/app"
"github.com/Zapharaos/fihub-backend/internal/auth/users"
"github.com/Zapharaos/fihub-backend/internal/handlers/render"
Expand All @@ -16,6 +16,10 @@ import (
"time"
)

var (
ErrLoginInvalid = errors.New("login-invalid")
)

const (
CheckHeader = 1 << iota
CheckQuery
Expand All @@ -26,8 +30,8 @@ type JwtToken struct {
}

type Auth struct {
signingKey []byte
checks int8
SigningKey []byte
Checks int8
}

// New initialize a new instance of Auth and returns a pointer of it
Expand All @@ -39,8 +43,8 @@ func New(checks int8) *Auth {
zap.L().Fatal("no checks are enabled")
}
return &Auth{
signingKey: []byte(utils.RandString(128)),
checks: checks,
SigningKey: []byte(utils.RandString(128)),
Checks: checks,
}
}

Expand All @@ -66,14 +70,14 @@ func (a *Auth) GetToken(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
zap.L().Error("GetToken.ReadAll:", zap.Error(err))
render.BadRequest(w, r, fmt.Errorf("login-invalid"))
render.BadRequest(w, r, ErrLoginInvalid)
return
}

err = json.Unmarshal(body, &userCredentials)
if err != nil {
zap.L().Error("GetToken.Decode:", zap.Error(err))
render.BadRequest(w, r, fmt.Errorf("login-invalid"))
render.BadRequest(w, r, ErrLoginInvalid)
return
}

Expand All @@ -85,7 +89,7 @@ func (a *Auth) GetToken(w http.ResponseWriter, r *http.Request) {
}
if !found {
zap.L().Warn("GetToken.Authenticate", zap.Error(err))
render.BadRequest(w, r, fmt.Errorf("login-invalid"))
render.BadRequest(w, r, ErrLoginInvalid)
return
}

Expand All @@ -111,7 +115,7 @@ func (a *Auth) GenerateToken(user users.User) (JwtToken, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign the token with our signing key
tokenString, err := token.SignedString(a.signingKey)
tokenString, err := token.SignedString(a.SigningKey)
if err != nil {
return JwtToken{}, err
}
Expand All @@ -122,7 +126,7 @@ func (a *Auth) GenerateToken(user users.User) (JwtToken, error) {
// ValidateToken validate a JWT token
func (a *Auth) ValidateToken(tokenString string) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return a.signingKey, nil
return a.SigningKey, nil
})
if err != nil {
return nil, err
Expand All @@ -147,9 +151,9 @@ func (a *Auth) Middleware(next http.Handler) http.Handler {

// first check header if enabled
var tokenString string
if a.checks&CheckHeader != 0 {
if a.Checks&CheckHeader != 0 {
tokenString = r.Header.Get("Authorization")
} else if a.checks&CheckQuery != 0 {
} else if a.Checks&CheckQuery != 0 {
tokenString = r.URL.Query().Get("token")
}

Expand Down
Loading

0 comments on commit 7443941

Please sign in to comment.