Initial commit
This commit is contained in:
commit
5146fd4ba7
46
.air.toml
Normal file
46
.air.toml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./main"
|
||||||
|
cmd = "make build"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with "go test -c"
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
# IDE specific files
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# .env file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Project build
|
||||||
|
main
|
||||||
60
Makefile
Normal file
60
Makefile
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Simple Makefile for a Go project
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
@echo "Building..."
|
||||||
|
|
||||||
|
@go build -o main cmd/api/main.go
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
run:
|
||||||
|
@go run cmd/api/main.go
|
||||||
|
|
||||||
|
# Create DB container
|
||||||
|
docker-run:
|
||||||
|
@if docker compose up 2>/dev/null; then \
|
||||||
|
: ; \
|
||||||
|
else \
|
||||||
|
echo "Falling back to Docker Compose V1"; \
|
||||||
|
docker-compose up; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Shutdown DB container
|
||||||
|
docker-down:
|
||||||
|
@if docker compose down 2>/dev/null; then \
|
||||||
|
: ; \
|
||||||
|
else \
|
||||||
|
echo "Falling back to Docker Compose V1"; \
|
||||||
|
docker-compose down; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test the application
|
||||||
|
test:
|
||||||
|
@echo "Testing..."
|
||||||
|
@go test ./tests -v
|
||||||
|
|
||||||
|
# Clean the binary
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning..."
|
||||||
|
@rm -f main
|
||||||
|
|
||||||
|
# Live Reload
|
||||||
|
watch:
|
||||||
|
@if command -v air > /dev/null; then \
|
||||||
|
air; \
|
||||||
|
echo "Watching...";\
|
||||||
|
else \
|
||||||
|
read -p "Go's 'air' is not installed on your machine. Do you want to install it? [Y/n] " choice; \
|
||||||
|
if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \
|
||||||
|
go install github.com/cosmtrek/air@latest; \
|
||||||
|
air; \
|
||||||
|
echo "Watching...";\
|
||||||
|
else \
|
||||||
|
echo "You chose not to install air. Exiting..."; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: all build run test clean
|
||||||
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Project gothtest
|
||||||
|
|
||||||
|
One Paragraph of project description goes here
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
|
||||||
|
|
||||||
|
## MakeFile
|
||||||
|
|
||||||
|
run all make commands with clean tests
|
||||||
|
```bash
|
||||||
|
make all build
|
||||||
|
```
|
||||||
|
|
||||||
|
build the application
|
||||||
|
```bash
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
run the application
|
||||||
|
```bash
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
|
Create DB container
|
||||||
|
```bash
|
||||||
|
make docker-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Shutdown DB container
|
||||||
|
```bash
|
||||||
|
make docker-down
|
||||||
|
```
|
||||||
|
|
||||||
|
live reload the application
|
||||||
|
```bash
|
||||||
|
make watch
|
||||||
|
```
|
||||||
|
|
||||||
|
run the test suite
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
clean up binary from the last build
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
16
cmd/api/main.go
Normal file
16
cmd/api/main.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gothtest/internal/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
server := server.NewServer()
|
||||||
|
|
||||||
|
err := server.ListenAndServe()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot start server: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
psql:
|
||||||
|
image: postgres:latest
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${DB_DATABASE}
|
||||||
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT}:5432"
|
||||||
|
volumes:
|
||||||
|
- psql_volume:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
psql_volume:
|
||||||
18
go.mod
Normal file
18
go.mod
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module gothtest
|
||||||
|
|
||||||
|
go 1.22.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
)
|
||||||
32
go.sum
Normal file
32
go.sum
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||||
|
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
114
internal/database/database.go
Normal file
114
internal/database/database.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service represents a service that interacts with a database.
|
||||||
|
type Service interface {
|
||||||
|
// Health returns a map of health status information.
|
||||||
|
// The keys and values in the map are service-specific.
|
||||||
|
Health() map[string]string
|
||||||
|
|
||||||
|
// Close terminates the database connection.
|
||||||
|
// It returns an error if the connection cannot be closed.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
database = os.Getenv("DB_DATABASE")
|
||||||
|
password = os.Getenv("DB_PASSWORD")
|
||||||
|
username = os.Getenv("DB_USERNAME")
|
||||||
|
port = os.Getenv("DB_PORT")
|
||||||
|
host = os.Getenv("DB_HOST")
|
||||||
|
dbInstance *service
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() Service {
|
||||||
|
// Reuse Connection
|
||||||
|
if dbInstance != nil {
|
||||||
|
return dbInstance
|
||||||
|
}
|
||||||
|
connStr := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", username, password, host, port, database)
|
||||||
|
db, err := sql.Open("pgx", connStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
dbInstance = &service{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
return dbInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health checks the health of the database connection by pinging the database.
|
||||||
|
// It returns a map with keys indicating various health statistics.
|
||||||
|
func (s *service) Health() map[string]string {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
stats := make(map[string]string)
|
||||||
|
|
||||||
|
// Ping the database
|
||||||
|
err := s.db.PingContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
stats["status"] = "down"
|
||||||
|
stats["error"] = fmt.Sprintf("db down: %v", err)
|
||||||
|
log.Fatalf(fmt.Sprintf("db down: %v", err)) // Log the error and terminate the program
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database is up, add more statistics
|
||||||
|
stats["status"] = "up"
|
||||||
|
stats["message"] = "It's healthy"
|
||||||
|
|
||||||
|
// Get database stats (like open connections, in use, idle, etc.)
|
||||||
|
dbStats := s.db.Stats()
|
||||||
|
stats["open_connections"] = strconv.Itoa(dbStats.OpenConnections)
|
||||||
|
stats["in_use"] = strconv.Itoa(dbStats.InUse)
|
||||||
|
stats["idle"] = strconv.Itoa(dbStats.Idle)
|
||||||
|
stats["wait_count"] = strconv.FormatInt(dbStats.WaitCount, 10)
|
||||||
|
stats["wait_duration"] = dbStats.WaitDuration.String()
|
||||||
|
stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleClosed, 10)
|
||||||
|
stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeClosed, 10)
|
||||||
|
|
||||||
|
// Evaluate stats to provide a health message
|
||||||
|
if dbStats.OpenConnections > 40 { // Assuming 50 is the max for this example
|
||||||
|
stats["message"] = "The database is experiencing heavy load."
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbStats.WaitCount > 1000 {
|
||||||
|
stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks."
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbStats.MaxIdleClosed > int64(dbStats.OpenConnections)/2 {
|
||||||
|
stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings."
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbStats.MaxLifetimeClosed > int64(dbStats.OpenConnections)/2 {
|
||||||
|
stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern."
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the database connection.
|
||||||
|
// It logs a message indicating the disconnection from the specific database.
|
||||||
|
// If the connection is successfully closed, it returns nil.
|
||||||
|
// If an error occurs while closing the connection, it returns the error.
|
||||||
|
func (s *service) Close() error {
|
||||||
|
log.Printf("Disconnected from database: %s", database)
|
||||||
|
return s.db.Close()
|
||||||
|
}
|
||||||
38
internal/server/routes.go
Normal file
38
internal/server/routes.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) RegisterRoutes() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
|
||||||
|
r.Get("/", s.HelloWorldHandler)
|
||||||
|
|
||||||
|
r.Get("/health", s.healthHandler)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
resp := make(map[string]string)
|
||||||
|
resp["message"] = "Hello World"
|
||||||
|
|
||||||
|
jsonResp, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error handling JSON marshal. Err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write(jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
jsonResp, _ := json.Marshal(s.db.Health())
|
||||||
|
_, _ = w.Write(jsonResp)
|
||||||
|
}
|
||||||
39
internal/server/server.go
Normal file
39
internal/server/server.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
|
||||||
|
"gothtest/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
port int
|
||||||
|
|
||||||
|
db database.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer() *http.Server {
|
||||||
|
port, _ := strconv.Atoi(os.Getenv("PORT"))
|
||||||
|
NewServer := &Server{
|
||||||
|
port: port,
|
||||||
|
|
||||||
|
db: database.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare Server config
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", NewServer.port),
|
||||||
|
Handler: NewServer.RegisterRoutes(),
|
||||||
|
IdleTimeout: time.Minute,
|
||||||
|
ReadTimeout: 10 * time.Second,
|
||||||
|
WriteTimeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
32
tests/handler_test.go
Normal file
32
tests/handler_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gothtest/internal/server"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandler(t *testing.T) {
|
||||||
|
s := &server.Server{}
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(s.HelloWorldHandler))
|
||||||
|
defer server.Close()
|
||||||
|
resp, err := http.Get(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error making request to server. Err: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
// Assertions
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("expected status OK; got %v", resp.Status)
|
||||||
|
}
|
||||||
|
expected := "{\"message\":\"Hello World\"}"
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error reading response body. Err: %v", err)
|
||||||
|
}
|
||||||
|
if expected != string(body) {
|
||||||
|
t.Errorf("expected response body to be %v; got %v", expected, string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user