
This commit adds a complete Go implementation of the Todo application. The application uses Gin framework for the web server, GORM for database access, and SQLite for storage. Key features: - Todo CRUD operations with the same API endpoints - Health check endpoint - Database migrations - Tests for models, services, and API handlers - Documentation for the API - Configurable settings
228 lines
5.0 KiB
Go
228 lines
5.0 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/simpletodoapp/go-todo-app/internal/dto"
|
|
"github.com/simpletodoapp/go-todo-app/internal/model"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// setupTestDB sets up an in-memory SQLite database for testing
|
|
func setupTestDB(t *testing.T) *gorm.DB {
|
|
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("Failed to connect to in-memory database: %v", err)
|
|
}
|
|
|
|
// Auto-migrate the schema
|
|
err = db.AutoMigrate(&model.Todo{})
|
|
if err != nil {
|
|
t.Fatalf("Failed to migrate test database: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
// populateTestData adds test data to the database
|
|
func populateTestData(t *testing.T, db *gorm.DB) {
|
|
todos := []model.Todo{
|
|
{
|
|
Title: "Test Todo 1",
|
|
Description: "Test Description 1",
|
|
Completed: false,
|
|
},
|
|
{
|
|
Title: "Test Todo 2",
|
|
Description: "Test Description 2",
|
|
Completed: true,
|
|
},
|
|
}
|
|
|
|
for _, todo := range todos {
|
|
if err := db.Create(&todo).Error; err != nil {
|
|
t.Fatalf("Failed to create test data: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetTodos(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
populateTestData(t, db)
|
|
|
|
service := NewTodoService(db)
|
|
|
|
// Test getting all todos
|
|
todos, err := service.GetTodos(0, 100, nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get todos: %v", err)
|
|
}
|
|
|
|
if len(todos) != 2 {
|
|
t.Errorf("Expected 2 todos, got %d", len(todos))
|
|
}
|
|
|
|
// Test filtering by completed
|
|
completed := true
|
|
todos, err = service.GetTodos(0, 100, &completed)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get completed todos: %v", err)
|
|
}
|
|
|
|
if len(todos) != 1 {
|
|
t.Errorf("Expected 1 completed todo, got %d", len(todos))
|
|
}
|
|
|
|
if !todos[0].Completed {
|
|
t.Errorf("Expected todo to be completed, got %t", todos[0].Completed)
|
|
}
|
|
|
|
// Test pagination
|
|
todos, err = service.GetTodos(1, 1, nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get paginated todos: %v", err)
|
|
}
|
|
|
|
if len(todos) != 1 {
|
|
t.Errorf("Expected 1 todo with pagination, got %d", len(todos))
|
|
}
|
|
}
|
|
|
|
func TestCreateTodo(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
service := NewTodoService(db)
|
|
|
|
// Create a new todo
|
|
todoCreate := dto.TodoCreate{
|
|
TodoBase: dto.TodoBase{
|
|
Title: "New Todo",
|
|
Description: "New Description",
|
|
Completed: false,
|
|
},
|
|
}
|
|
|
|
todo, err := service.CreateTodo(todoCreate)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create todo: %v", err)
|
|
}
|
|
|
|
if todo.ID == 0 {
|
|
t.Errorf("Expected todo ID to be set, got %d", todo.ID)
|
|
}
|
|
|
|
if todo.Title != "New Todo" {
|
|
t.Errorf("Expected title to be 'New Todo', got '%s'", todo.Title)
|
|
}
|
|
|
|
if todo.Description != "New Description" {
|
|
t.Errorf("Expected description to be 'New Description', got '%s'", todo.Description)
|
|
}
|
|
|
|
if todo.Completed != false {
|
|
t.Errorf("Expected completed to be false, got %t", todo.Completed)
|
|
}
|
|
}
|
|
|
|
func TestGetTodoByID(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
populateTestData(t, db)
|
|
service := NewTodoService(db)
|
|
|
|
// Get a todo by ID
|
|
todo, err := service.GetTodoByID(1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get todo by ID: %v", err)
|
|
}
|
|
|
|
if todo == nil {
|
|
t.Fatalf("Expected todo to be found, got nil")
|
|
}
|
|
|
|
if todo.ID != 1 {
|
|
t.Errorf("Expected todo ID to be 1, got %d", todo.ID)
|
|
}
|
|
|
|
// Test non-existent todo
|
|
todo, err = service.GetTodoByID(999)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error for non-existent todo, got %v", err)
|
|
}
|
|
|
|
if todo != nil {
|
|
t.Errorf("Expected nil for non-existent todo, got %+v", todo)
|
|
}
|
|
}
|
|
|
|
func TestUpdateTodo(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
populateTestData(t, db)
|
|
service := NewTodoService(db)
|
|
|
|
// Update a todo
|
|
title := "Updated Title"
|
|
completed := true
|
|
todoUpdate := dto.TodoUpdate{
|
|
Title: &title,
|
|
Completed: &completed,
|
|
}
|
|
|
|
todo, err := service.UpdateTodo(1, todoUpdate)
|
|
if err != nil {
|
|
t.Fatalf("Failed to update todo: %v", err)
|
|
}
|
|
|
|
if todo.Title != "Updated Title" {
|
|
t.Errorf("Expected title to be 'Updated Title', got '%s'", todo.Title)
|
|
}
|
|
|
|
if !todo.Completed {
|
|
t.Errorf("Expected completed to be true, got %t", todo.Completed)
|
|
}
|
|
|
|
// Test updating non-existent todo
|
|
todo, err = service.UpdateTodo(999, todoUpdate)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error for non-existent todo, got %v", err)
|
|
}
|
|
|
|
if todo != nil {
|
|
t.Errorf("Expected nil for non-existent todo, got %+v", todo)
|
|
}
|
|
}
|
|
|
|
func TestDeleteTodo(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
populateTestData(t, db)
|
|
service := NewTodoService(db)
|
|
|
|
// Delete a todo
|
|
deleted, err := service.DeleteTodo(1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete todo: %v", err)
|
|
}
|
|
|
|
if !deleted {
|
|
t.Errorf("Expected todo to be deleted")
|
|
}
|
|
|
|
// Try to get the deleted todo
|
|
todo, err := service.GetTodoByID(1)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error after deletion: %v", err)
|
|
}
|
|
|
|
if todo != nil {
|
|
t.Errorf("Expected todo to be nil after deletion, got %+v", todo)
|
|
}
|
|
|
|
// Test deleting non-existent todo
|
|
deleted, err = service.DeleteTodo(999)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error for non-existent todo, got %v", err)
|
|
}
|
|
|
|
if deleted {
|
|
t.Errorf("Expected false for deleting non-existent todo, got %t", deleted)
|
|
}
|
|
} |