Skip to content

Commit

Permalink
Added test suite, made changes according to review
Browse files Browse the repository at this point in the history
  • Loading branch information
KostLinux committed Apr 22, 2024
1 parent d03656b commit de30591
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 75 deletions.
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# APP
APP_PORT=8000
APP_HOST=localhost
DEBUG_MODE=true

## Possible values are "development" & "production"
APP_ENV=development

# API
API_PATH=/api/v1/

# MISC
FORCE_TLS=false
DEBUG_MODE=true

# External Services

Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions controller/error/http_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ func StatusNotFound(c *gin.Context) {
c.Status(http.StatusNotFound)
file, err := os.Open("./public/error/404.html")
if err != nil {
log.Printf("Error opening file: %v", err)
log.Printf("error opening file: %v", err)
return
}
defer file.Close()
if _, err := io.Copy(c.Writer, file); err != nil {
if err := c.Error(err); err != nil {
log.Printf("Error adding error to Gin context: %v", err)
log.Printf("error adding error to Gin context: %v", err)
}
}
}
Expand All @@ -33,13 +33,13 @@ func StatusInternalServerError() gin.HandlerFunc {
c.Status(http.StatusInternalServerError)
file, err := os.Open("./public/error/500.html")
if err != nil {
log.Printf("Error opening file: %v", err)
log.Printf("error opening file: %v", err)
return
}
defer file.Close()
if _, err := io.Copy(c.Writer, file); err != nil {
if err := c.Error(err); err != nil {
log.Printf("Error adding error to Gin context: %v", err)
log.Printf("error adding error to Gin context: %v", err)
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"log"
"os"
"web/middleware"
"web/repository"
repository "web/repository"

"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
Expand All @@ -13,26 +13,26 @@ import (

func main() {
if err := godotenv.Load(".env"); err != nil {
log.Fatalf("Error loading .env file")
log.Fatalf("error loading .env file")
}

if os.Getenv("DEBUG_MODE") != "true" {
gin.SetMode(gin.ReleaseMode)
}

psql := os.Getenv("PSQLEnabled")
if psql == "true" {
db, err := repository.NewDBConnection()
if err != nil {
log.Fatalf("Failed to establish database connection: %v", err)
}
defer db.Close()
database, err := repository.NewDBConnection()
if err != nil {
log.Fatalf("failed to establish database connection: %v", err)
}

if database != nil && database.DB != nil {
defer database.DB.Close()
}

router := gin.New()
httpRouter := middleware.NewRouter(router)
err := httpRouter.Run(":" + os.Getenv("APP_PORT"))
if err != nil {
log.Printf("Failed to start the server: %v", err)

if err := httpRouter.Run(":" + os.Getenv("APP_PORT")); err != nil {
log.Printf("failed to start the server: %v", err)
}
}
7 changes: 7 additions & 0 deletions model/repository_db_struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

import "database/sql"

type Database struct {
DB *sql.DB
}
7 changes: 0 additions & 7 deletions model/service_status.go

This file was deleted.

6 changes: 6 additions & 0 deletions model/status_page_struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package model

type StatusPage struct {
Name string
URL string
}
31 changes: 18 additions & 13 deletions repository/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,34 @@ import (
"fmt"
"log"
"os"
model "web/model"

// Import pq to register the Postgres driver.
_ "github.com/lib/pq"
)

func NewDBConnection() (*sql.DB, error) {
conn := fmt.Sprintf(
// postgres://username:password@host:port/database?sslmode=disable
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PASSWORD"),
func NewDBConnection() (*model.Database, error) {
if os.Getenv("PSQL_ENABLED") != "true" && os.Getenv("APP_ENV") == "development" {
log.Println("Warning: PSQL is not enabled. Database queries will fail.")
return nil, nil
}

connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
os.Getenv("POSTGRES_HOST"),
os.Getenv("POSTGRES_PORT"),
os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PASSWORD"),
os.Getenv("POSTGRES_DB"),
)

db, err := sql.Open("postgres", conn)
db, err := sql.Open("postgres", connStr)
if err != nil {
return nil, fmt.Errorf("the database refused the connection - %v", err)
return nil, err
}

err = db.Ping()
if err != nil {
return nil, fmt.Errorf("the database connection timed out - %v", err)
if err = db.Ping(); err != nil {
return nil, err
}

log.Println("Connected to the PostgreSQL database!")
return db, nil
return &model.Database{DB: db}, nil
}
21 changes: 12 additions & 9 deletions tests/statuspage_test.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
package tests

import (
"log"
"net/http"
"os"
"testing"
model "web/model"
httpTemplates "web/views/templates"

"github.com/jarcoal/httpmock"
"github.com/joho/godotenv"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

func TestStatusPageWhenAllServicesDown(tests *testing.T) {
if err := godotenv.Load("../.env.tests"); err != nil {
log.Fatalf("Error loading .env.tests file")
}

func (suite *StatusPageSuite) TestStatusPageWhenAllServicesDown() {
// Arrange
urlPrefix := "http://"
httpmock.Activate()
defer httpmock.DeactivateAndReset()

services := []model.ServiceStatusInfo{
services := []model.StatusPage{
{
Name: "API",
URL: urlPrefix + os.Getenv("APP_HOST") + ":" + os.Getenv("APP_PORT") + os.Getenv("API_PATH") + "ping",
},
}

// Act

// Register mock responders for all service endpoints
for _, service := range services {
httpmock.RegisterResponder("GET", service.URL, httpmock.NewErrorResponder(http.ErrServerClosed))
}

statuses := httpTemplates.CheckServicesStatus(services)

// Assert
// Check the status of all services
for i, service := range services {
assert.Equalf(tests, "down", statuses[i]["status"], "Expected %s status to be down, got %s", service.Name, statuses[i]["status"])
assert.Equalf(suite.T(), "down", statuses[i]["status"], "expected %s status to be down, got %s", service.Name, statuses[i]["status"])
}
}

func WrapTestsIntoSuite(t *testing.T) {
suite.Run(t, new(StatusPageSuite))
}
26 changes: 26 additions & 0 deletions tests/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package tests

import (
"os"

"github.com/stretchr/testify/suite"
)

type StatusPageSuite struct {
suite.Suite
}

// SetupTest will be run before each test in the suite
func (suite *StatusPageSuite) SetupTestEnvironmentals() {
os.Setenv("APP_PORT", "8000")
os.Setenv("APP_HOST", "localhost")
os.Setenv("DEBUG_MODE", "true")
os.Setenv("API_PATH", "/api/v1/")
os.Setenv("FORCE_TLS", "false")
os.Setenv("PSQL_ENABLED", "false")
os.Setenv("POSTGRES_HOST", "localhost")
os.Setenv("POSTGRES_PORT", "5432")
os.Setenv("POSTGRES_USER", "postgres")
os.Setenv("POSTGRES_PASSWORD", "postgres")
os.Setenv("POSTGRES_DB", "postgres")
}
3 changes: 3 additions & 0 deletions views/assets/status.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
body {
font-family: Arial, sans-serif;
}

.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}

.status {
text-align: center;
font-size: 24px;
margin-bottom: 20px;
}

.service {
margin-bottom: 10px;
}
58 changes: 29 additions & 29 deletions views/templates/statuspage.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package view
package views

import (
"log"
Expand All @@ -9,44 +9,24 @@ import (
"github.com/gin-gonic/gin"
)

func serviceHealthHandler(serviceInfo model.ServiceStatusInfo) map[string]string {
log.Println("Checking service status at URL:", serviceInfo.URL)
resp, err := http.Get(serviceInfo.URL)
service := map[string]string{
"name": serviceInfo.Name,
}

if err != nil {
log.Println("Error checking service status:", err)
service["status"] = "down"
} else {
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
service["status"] = "up"
} else {
service["status"] = "down"
}
}

return service
}

func CheckServicesStatus(services []model.ServiceStatusInfo) []map[string]string {
statuses := make([]map[string]string, 0)
func CheckServicesStatus(services []model.StatusPage) []map[string]string {
serviceStatuses := make([]map[string]string, 0)
for _, service := range services {
status := serviceHealthHandler(service)
statuses = append(statuses, status)
status := servicesHealthHandler(service)
serviceStatuses = append(serviceStatuses, status)
}
return statuses

return serviceStatuses
}

func StatusPageResponse() gin.HandlerFunc {
urlPrefix := "http://"
if os.Getenv("FORCE_TLS") == "true" {
urlPrefix = "https://"
}

return func(c *gin.Context) {
services := []model.ServiceStatusInfo{
services := []model.StatusPage{
{
Name: "API",
URL: urlPrefix + os.Getenv("APP_HOST") + ":" + os.Getenv("APP_PORT") + os.Getenv("API_PATH") + "ping",
Expand All @@ -59,3 +39,23 @@ func StatusPageResponse() gin.HandlerFunc {
c.Next()
}
}

func servicesHealthHandler(serviceInfo model.StatusPage) map[string]string {
resp, err := http.Get(serviceInfo.URL)

service := map[string]string{
"name": serviceInfo.Name,
"status": "up",
}

if err != nil {
log.Println("Error checking service status:", err)
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
service["status"] = "down"
}
}

return service
}

0 comments on commit de30591

Please sign in to comment.