@@ -1,11 +1,16 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gavv/httpexpect/v2"
 | 
						"github.com/gavv/httpexpect/v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TestGoalName = "Test Goal"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func startServer(t *testing.T) *httpexpect.Expect {
 | 
					func startServer(t *testing.T) *httpexpect.Expect {
 | 
				
			||||||
	config := ServerConfig{
 | 
						config := ServerConfig{
 | 
				
			||||||
		Datasource: ":memory:",
 | 
							Datasource: ":memory:",
 | 
				
			||||||
@@ -63,7 +68,7 @@ func TestCreateUserGoal(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Create a new goal
 | 
						// Create a new goal
 | 
				
			||||||
	requestBody := map[string]interface{}{
 | 
						requestBody := map[string]interface{}{
 | 
				
			||||||
		"name":   "Test Goal",
 | 
							"name":   TestGoalName,
 | 
				
			||||||
		"target": 5000,
 | 
							"target": 5000,
 | 
				
			||||||
		"weight": 10,
 | 
							"weight": 10,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -88,7 +93,7 @@ func TestCreateUserGoal(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	goal := goals.Value(0).Object()
 | 
						goal := goals.Value(0).Object()
 | 
				
			||||||
	goal.Value("id").IsEqual(goalId)
 | 
						goal.Value("id").IsEqual(goalId)
 | 
				
			||||||
	goal.Value("name").IsEqual("Test Goal")
 | 
						goal.Value("name").IsEqual(TestGoalName)
 | 
				
			||||||
	goal.Value("target").IsEqual(5000)
 | 
						goal.Value("target").IsEqual(5000)
 | 
				
			||||||
	goal.Value("weight").IsEqual(10)
 | 
						goal.Value("weight").IsEqual(10)
 | 
				
			||||||
	goal.Value("progress").IsEqual(0)
 | 
						goal.Value("progress").IsEqual(0)
 | 
				
			||||||
@@ -98,7 +103,7 @@ func TestCreateUserGoalNoUser(t *testing.T) {
 | 
				
			|||||||
	e := startServer(t)
 | 
						e := startServer(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestBody := map[string]interface{}{
 | 
						requestBody := map[string]interface{}{
 | 
				
			||||||
		"name":   "Test Goal",
 | 
							"name":   TestGoalName,
 | 
				
			||||||
		"target": 5000,
 | 
							"target": 5000,
 | 
				
			||||||
		"weight": 10,
 | 
							"weight": 10,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -139,7 +144,7 @@ func TestCreateUserGoalBadId(t *testing.T) {
 | 
				
			|||||||
	e := startServer(t)
 | 
						e := startServer(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestBody := map[string]interface{}{
 | 
						requestBody := map[string]interface{}{
 | 
				
			||||||
		"name":   "Test Goal",
 | 
							"name":   TestGoalName,
 | 
				
			||||||
		"target": 5000,
 | 
							"target": 5000,
 | 
				
			||||||
		"weight": 10,
 | 
							"weight": 10,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -149,3 +154,54 @@ func TestCreateUserGoalBadId(t *testing.T) {
 | 
				
			|||||||
		Expect().
 | 
							Expect().
 | 
				
			||||||
		Status(400)
 | 
							Status(400)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteUserGoal(t *testing.T) {
 | 
				
			||||||
 | 
						e := startServer(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a new goal to delete
 | 
				
			||||||
 | 
						createRequest := map[string]interface{}{
 | 
				
			||||||
 | 
							"name":   TestGoalName,
 | 
				
			||||||
 | 
							"target": 1000,
 | 
				
			||||||
 | 
							"weight": 5,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						response := e.POST("/user/1/goals").
 | 
				
			||||||
 | 
							WithJSON(createRequest).
 | 
				
			||||||
 | 
							Expect().
 | 
				
			||||||
 | 
							Status(201).
 | 
				
			||||||
 | 
							JSON().Object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						goalId := response.Value("id").Number().Raw()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete the goal
 | 
				
			||||||
 | 
						e.DELETE("/user/1/goal/" + strconv.Itoa(int(goalId))).
 | 
				
			||||||
 | 
							Expect().
 | 
				
			||||||
 | 
							Status(200).
 | 
				
			||||||
 | 
							JSON().Object().Value("message").IsEqual("Goal deleted successfully")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify the goal no longer exists
 | 
				
			||||||
 | 
						goals := e.GET("/user/1/goals").
 | 
				
			||||||
 | 
							Expect().
 | 
				
			||||||
 | 
							Status(200).
 | 
				
			||||||
 | 
							JSON().Array()
 | 
				
			||||||
 | 
						goals.Length().IsEqual(0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteUserGoalNotFound(t *testing.T) {
 | 
				
			||||||
 | 
						e := startServer(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attempt to delete a non-existent goal
 | 
				
			||||||
 | 
						e.DELETE("/user/1/goal/999").
 | 
				
			||||||
 | 
							Expect().
 | 
				
			||||||
 | 
							Status(404).
 | 
				
			||||||
 | 
							JSON().Object().Value("error").IsEqual("Goal not found")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteUserGoalInvalidId(t *testing.T) {
 | 
				
			||||||
 | 
						e := startServer(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attempt to delete a goal with an invalid ID
 | 
				
			||||||
 | 
						e.DELETE("/user/1/goal/invalid-id").
 | 
				
			||||||
 | 
							Expect().
 | 
				
			||||||
 | 
							Status(400).
 | 
				
			||||||
 | 
							JSON().Object().Value("error").IsEqual("Invalid goal ID")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,3 +128,25 @@ func (db *Db) CreateGoal(userId int, goal *CreateGoalRequest) (int, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return lastId, nil
 | 
						return lastId, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (db *Db) DeleteGoal(userId int, goalId int) error {
 | 
				
			||||||
 | 
						// Check if the goal exists for the user
 | 
				
			||||||
 | 
						count := 0
 | 
				
			||||||
 | 
						err := db.db.Query("select count(*) from goals where id = ? and user_id = ?").
 | 
				
			||||||
 | 
							Bind(goalId, userId).ScanSingle(&count)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if count == 0 {
 | 
				
			||||||
 | 
							return errors.New("goal not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete the goal
 | 
				
			||||||
 | 
						err = db.db.Query("delete from goals where id = ? and user_id = ?").
 | 
				
			||||||
 | 
							Bind(goalId, userId).Exec()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,13 @@ import (
 | 
				
			|||||||
var migrations embed.FS
 | 
					var migrations embed.FS
 | 
				
			||||||
var db *Db
 | 
					var db *Db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ErrInternalServerError = "Internal Server Error"
 | 
				
			||||||
 | 
						ErrInvalidUserID       = "Invalid user ID"
 | 
				
			||||||
 | 
						ErrUserNotFound        = "User not found"
 | 
				
			||||||
 | 
						ErrCheckingUserExist   = "Error checking user existence: %v"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ServerConfig holds configuration for the server.
 | 
					// ServerConfig holds configuration for the server.
 | 
				
			||||||
type ServerConfig struct {
 | 
					type ServerConfig struct {
 | 
				
			||||||
	// The datasource to the SQLite database.
 | 
						// The datasource to the SQLite database.
 | 
				
			||||||
@@ -34,7 +41,7 @@ func getUsers(c *gin.Context) {
 | 
				
			|||||||
	users, err := db.GetUsers()
 | 
						users, err := db.GetUsers()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Error getting users: %v", err)
 | 
							log.Printf("Error getting users: %v", err)
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c.IndentedJSON(http.StatusOK, users)
 | 
						c.IndentedJSON(http.StatusOK, users)
 | 
				
			||||||
@@ -44,19 +51,19 @@ func getUser(c *gin.Context) {
 | 
				
			|||||||
	userIdStr := c.Param("userId")
 | 
						userIdStr := c.Param("userId")
 | 
				
			||||||
	userId, err := strconv.Atoi(userIdStr)
 | 
						userId, err := strconv.Atoi(userIdStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Invalid user ID: %v", err)
 | 
							log.Printf(ErrInvalidUserID+": %v", err)
 | 
				
			||||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
 | 
							c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	user, err := db.GetUser(userId)
 | 
						user, err := db.GetUser(userId)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Error getting user: %v", err)
 | 
							log.Printf("Error getting user: %v", err)
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if user == nil {
 | 
						if user == nil {
 | 
				
			||||||
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
 | 
							c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,27 +74,27 @@ func getUserGoals(c *gin.Context) {
 | 
				
			|||||||
	userIdStr := c.Param("userId")
 | 
						userIdStr := c.Param("userId")
 | 
				
			||||||
	userId, err := strconv.Atoi(userIdStr)
 | 
						userId, err := strconv.Atoi(userIdStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Invalid user ID: %v", err)
 | 
							log.Printf(ErrInvalidUserID+": %v", err)
 | 
				
			||||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
 | 
							c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	exists, err := db.UserExists(userId)
 | 
						exists, err := db.UserExists(userId)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Error checking user existence: %v", err)
 | 
							log.Printf(ErrCheckingUserExist, err)
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !exists {
 | 
						if !exists {
 | 
				
			||||||
		log.Printf("Error checking user existence: %v", err)
 | 
							log.Printf(ErrCheckingUserExist, err)
 | 
				
			||||||
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
 | 
							c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	goals, err := db.GetUserGoals(userId)
 | 
						goals, err := db.GetUserGoals(userId)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Error getting user goals: %v", err)
 | 
							log.Printf("Error getting user goals: %v", err)
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c.IndentedJSON(http.StatusOK, goals)
 | 
						c.IndentedJSON(http.StatusOK, goals)
 | 
				
			||||||
@@ -97,8 +104,8 @@ func createUserGoal(c *gin.Context) {
 | 
				
			|||||||
	userIdStr := c.Param("userId")
 | 
						userIdStr := c.Param("userId")
 | 
				
			||||||
	userId, err := strconv.Atoi(userIdStr)
 | 
						userId, err := strconv.Atoi(userIdStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Invalid user ID: %v", err)
 | 
							log.Printf(ErrInvalidUserID+": %v", err)
 | 
				
			||||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
 | 
							c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -121,7 +128,7 @@ func createUserGoal(c *gin.Context) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Error creating goal: %v", err)
 | 
							log.Printf("Error creating goal: %v", err)
 | 
				
			||||||
		if err.Error() == "user does not exist" {
 | 
							if err.Error() == "user does not exist" {
 | 
				
			||||||
			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
 | 
								c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			c.JSON(http.StatusBadRequest, gin.H{"error": "Could not create goal"})
 | 
								c.JSON(http.StatusBadRequest, gin.H{"error": "Could not create goal"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -133,6 +140,49 @@ func createUserGoal(c *gin.Context) {
 | 
				
			|||||||
	c.IndentedJSON(http.StatusCreated, response)
 | 
						c.IndentedJSON(http.StatusCreated, response)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deleteUserGoal(c *gin.Context) {
 | 
				
			||||||
 | 
						userIdStr := c.Param("userId")
 | 
				
			||||||
 | 
						goalIdStr := c.Param("goalId")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userId, err := strconv.Atoi(userIdStr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf(ErrInvalidUserID+": %v", err)
 | 
				
			||||||
 | 
							c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						goalId, err := strconv.Atoi(goalIdStr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("Invalid goal ID: %v", err)
 | 
				
			||||||
 | 
							c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exists, err := db.UserExists(userId)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf(ErrCheckingUserExist, err)
 | 
				
			||||||
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !exists {
 | 
				
			||||||
 | 
							c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.DeleteGoal(userId, goalId)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err.Error() == "goal not found" {
 | 
				
			||||||
 | 
								c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								log.Printf("Error deleting goal: %v", err)
 | 
				
			||||||
 | 
								c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
Initialises the database, and then starts the server.
 | 
					Initialises the database, and then starts the server.
 | 
				
			||||||
@@ -147,6 +197,7 @@ func start(ctx context.Context, config *ServerConfig) {
 | 
				
			|||||||
	router.GET("/api/user/:userId", getUser)
 | 
						router.GET("/api/user/:userId", getUser)
 | 
				
			||||||
	router.GET("/api/user/:userId/goals", getUserGoals)
 | 
						router.GET("/api/user/:userId/goals", getUserGoals)
 | 
				
			||||||
	router.POST("/api/user/:userId/goals", createUserGoal)
 | 
						router.POST("/api/user/:userId/goals", createUserGoal)
 | 
				
			||||||
 | 
						router.DELETE("/api/user/:userId/goal/:goalId", deleteUserGoal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	srv := &http.Server{
 | 
						srv := &http.Server{
 | 
				
			||||||
		Addr:    ":" + config.Port,
 | 
							Addr:    ":" + config.Port,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user