From 9c80cb9885c37e9d1fabdd33f8a26c649e5a21cb Mon Sep 17 00:00:00 2001 From: Floriansylvain Date: Mon, 22 Dec 2025 02:49:57 +0100 Subject: [PATCH] feat: unit testing mocked repos / domain --- test/article_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++++ test/image_test.go | 67 ++++++++++++++++++++++++++++++ test/mail_test.go | 55 ++++++++++++++++++++++++ test/user_test.go | 89 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 test/article_test.go create mode 100644 test/image_test.go create mode 100644 test/mail_test.go create mode 100644 test/user_test.go diff --git a/test/article_test.go b/test/article_test.go new file mode 100644 index 0000000..2827a0c --- /dev/null +++ b/test/article_test.go @@ -0,0 +1,99 @@ +package test + +import ( + "RenewCMS/internal/domain/article" + "RenewCMS/internal/infrastructure/useCases" + "errors" + "testing" +) + +type mockArticleRepo struct { + articles map[uint32]article.Article +} + +func (m *mockArticleRepo) FindByFilters(f article.Filters) ([]article.Article, error) { + var results []article.Article + for _, a := range m.articles { + if f.IsOnline != nil && a.IsOnline == *f.IsOnline { + results = append(results, a) + } + } + return results, nil +} + +func (m *mockArticleRepo) Get(id uint32) (article.Article, error) { + a, ok := m.articles[id] + if !ok { + return article.Article{}, article.ErrArticleNotFound + } + return a, nil +} + +func (m *mockArticleRepo) Create(a article.Article) (article.Article, error) { + a.ID = uint32(len(m.articles) + 1) + m.articles[a.ID] = a + return a, nil +} + +func (m *mockArticleRepo) Delete(id uint32) error { + delete(m.articles, id) + return nil +} + +func (m *mockArticleRepo) GetByName(n string) (article.Article, error) { return article.Article{}, nil } +func (m *mockArticleRepo) GetAll() []article.Article { return nil } +func (m *mockArticleRepo) UpdateBody(id uint32, b string) (article.Article, error) { + return article.Article{}, nil +} +func (m *mockArticleRepo) UpdateIsOnline(id uint32, o bool) (article.Article, error) { + return article.Article{}, nil +} +func (m *mockArticleRepo) AddImage(p, i uint32) error { return nil } + +var _ article.Repository = &mockArticleRepo{} + +func TestCreateArticle(t *testing.T) { + repo := &mockArticleRepo{articles: make(map[uint32]article.Article)} + uc := useCases.NewCreateArticleUseCase(repo) + + t.Run("Valid Title", func(t *testing.T) { + cmd := useCases.CreateArticleCommand{Title: "Valid Title", Body: "Content"} + res, err := uc.CreateArticle(cmd) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + if res.Title != cmd.Title { + t.Errorf("Expected %s, got %s", cmd.Title, res.Title) + } + }) + + t.Run("Short Title", func(t *testing.T) { + cmd := useCases.CreateArticleCommand{Title: "Ab", Body: "Content"} + _, err := uc.CreateArticle(cmd) + if !errors.Is(err, article.ErrInvalidTitle) { + t.Errorf("Expected ErrInvalidTitle") + } + }) +} + +func TestGetOnlineArticle(t *testing.T) { + repo := &mockArticleRepo{articles: map[uint32]article.Article{ + 1: {ID: 1, IsOnline: true}, + 2: {ID: 2, IsOnline: false}, + }} + uc := useCases.NewGetArticleUseCase(repo) + + t.Run("Article Online", func(t *testing.T) { + _, err := uc.GetOnlineArticle(1) + if err != nil { + t.Errorf("Expected article to be found") + } + }) + + t.Run("Article Offline", func(t *testing.T) { + _, err := uc.GetOnlineArticle(2) + if !errors.Is(err, article.ErrArticleNotFound) { + t.Errorf("Offline article should return not found") + } + }) +} diff --git a/test/image_test.go b/test/image_test.go new file mode 100644 index 0000000..2e1097a --- /dev/null +++ b/test/image_test.go @@ -0,0 +1,67 @@ +package test + +import ( + "RenewCMS/internal/domain/image" + "RenewCMS/internal/infrastructure/useCases" + "strings" + "testing" +) + +type mockImageRepo struct { + images map[uint32]image.Image +} + +func (m *mockImageRepo) Create(input image.ImageInput) (image.Image, error) { + newId := uint32(len(m.images) + 1) + img := image.Image{ + ID: newId, + Path: "/static/" + input.Filename, + } + m.images[newId] = img + return img, nil +} + +func (m *mockImageRepo) Delete(id uint32) error { + delete(m.images, id) + return nil +} + +var _ image.Repository = &mockImageRepo{} + +func TestImageUseCases(t *testing.T) { + repo := &mockImageRepo{images: make(map[uint32]image.Image)} + createUc := useCases.NewCreateImageUseCase(repo) + deleteUc := useCases.NewDeleteImageUseCase(repo) + + t.Run("Create Image", func(t *testing.T) { + input := image.ImageInput{ + Content: strings.NewReader("fake-image-data"), + Filename: "test.png", + ContentType: "image/png", + } + + res, err := createUc.CreateImage(input) + if err != nil { + t.Fatalf("CreateImage failed: %v", err) + } + if res.ID == 0 { + t.Errorf("Expected valid ID, got 0") + } + if !strings.Contains(res.Path, "test.png") { + t.Errorf("Path mismatch: %s", res.Path) + } + }) + + t.Run("Delete Image", func(t *testing.T) { + repo.images[99] = image.Image{ID: 99, Path: "/static/delete-me.png"} + + err := deleteUc.DeleteImage(99) + if err != nil { + t.Fatalf("DeleteImage failed: %v", err) + } + + if _, exists := repo.images[99]; exists { + t.Errorf("Image 99 should have been deleted") + } + }) +} diff --git a/test/mail_test.go b/test/mail_test.go new file mode 100644 index 0000000..b0b36aa --- /dev/null +++ b/test/mail_test.go @@ -0,0 +1,55 @@ +package test + +import ( + "RenewCMS/internal/domain/mail" + "RenewCMS/internal/infrastructure/useCases" + "errors" + "testing" +) + +type mockMailRepo struct { + SentEmails []struct { + Address string + Template string + Data any + } + ShouldFail bool +} + +func (m *mockMailRepo) Send(receiverAddress string, templateName string, data any) error { + if m.ShouldFail { + return errors.New("smtp error") + } + m.SentEmails = append(m.SentEmails, struct { + Address string + Template string + Data any + }{receiverAddress, templateName, data}) + return nil +} + +var _ mail.Repository = &mockMailRepo{} + +func TestSendMail(t *testing.T) { + repo := &mockMailRepo{} + uc := useCases.NewSendMailUseCase(repo) + + t.Run("Successful Email Send", func(t *testing.T) { + repo.ShouldFail = false + err := uc.SendMail("user@example.com", "welcome", "User123") + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + if len(repo.SentEmails) != 1 { + t.Errorf("Expected 1 email to be sent, got %d", len(repo.SentEmails)) + } + }) + + t.Run("Failed Email Send", func(t *testing.T) { + repo.ShouldFail = true + err := uc.SendMail("fail@example.com", "reset", nil) + if err == nil { + t.Errorf("Expected an error but got nil") + } + }) +} diff --git a/test/user_test.go b/test/user_test.go new file mode 100644 index 0000000..a45eb43 --- /dev/null +++ b/test/user_test.go @@ -0,0 +1,89 @@ +package test + +import ( + "RenewCMS/internal/domain/user" + "RenewCMS/internal/infrastructure/useCases" + "testing" +) + +type mockUserRepo struct { + users map[uint32]user.User +} + +func (m *mockUserRepo) Create(u user.User) (user.User, error) { + u.ID = uint32(len(m.users) + 1) + m.users[u.ID] = u + return u, nil +} + +func (m *mockUserRepo) Get(id uint32) (user.User, error) { + u, ok := m.users[id] + if !ok { + return user.User{}, nil + } + return u, nil +} + +func (m *mockUserRepo) GetAll() []user.User { + var list []user.User + for _, u := range m.users { + list = append(list, u) + } + return list +} + +func (m *mockUserRepo) UpdateVerificationStatus(id uint32, v bool) (user.User, error) { + u := m.users[id] + u.IsVerified = v + m.users[id] = u + return u, nil +} + +func (m *mockUserRepo) GetByUsername(n string) (user.User, error) { return user.User{}, nil } +func (m *mockUserRepo) GetByEmail(e string) (user.User, error) { return user.User{}, nil } +func (m *mockUserRepo) Delete(id uint32) error { return nil } +func (m *mockUserRepo) UpdatePassword(id uint32, p string) (user.User, error) { + return user.User{}, nil +} +func (m *mockUserRepo) UpdatePasswordResetCode(id uint32, c string) (user.User, error) { + return user.User{}, nil +} + +var _ user.Repository = &mockUserRepo{} + +func TestCreateUser(t *testing.T) { + repo := &mockUserRepo{users: make(map[uint32]user.User)} + uc := useCases.NewCreateUserUseCase(repo) + + cmd := useCases.CreateUserCommand{ + Username: "testuser", + Password: "password123", + Email: "test@example.com", + } + + res, err := uc.CreateUser(cmd) + if err != nil { + t.Fatalf("User creation failed: %v", err) + } + if res.Username != cmd.Username { + t.Errorf("Username mismatch") + } + if res.VerificationCode == "" { + t.Errorf("Verification code was not generated") + } +} + +func TestUpdateUserVerification(t *testing.T) { + repo := &mockUserRepo{users: map[uint32]user.User{ + 1: {ID: 1, IsVerified: false}, + }} + uc := useCases.NewUpdateUserUseCase(repo) + + res, err := uc.UpdateVerificationStatus(1, true) + if err != nil { + t.Fatalf("Update failed: %v", err) + } + if !res.IsVerified { + t.Errorf("User should be verified") + } +}