197 lines
6.9 KiB
Go
197 lines
6.9 KiB
Go
package test
|
|
|
|
import (
|
|
"RenewCMS/api/handlers/image"
|
|
"RenewCMS/api/middleware"
|
|
"RenewCMS/api/pkg/apperr"
|
|
validator "RenewCMS/api/pkg/validator"
|
|
domainArticle "RenewCMS/internal/domain/article"
|
|
domainImage "RenewCMS/internal/domain/image"
|
|
"RenewCMS/internal/infrastructure/useCases"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/jwtauth/v5"
|
|
)
|
|
|
|
func newRemoveArticleImageRouter(articleRepo *mockArticleRepo, imageRepo *mockImageRepo, token *jwtauth.JWTAuth) *chi.Mux {
|
|
userRepo := newVerifiedUserRepo()
|
|
getUserUseCase := useCases.NewGetUserUseCase(userRepo)
|
|
|
|
imageHandler := image.NewHandler(
|
|
useCases.NewCreateImageUseCase(imageRepo),
|
|
useCases.NewGetArticleUseCase(articleRepo),
|
|
useCases.NewUpdateArticleUseCase(articleRepo),
|
|
useCases.NewDeleteImageUseCase(imageRepo),
|
|
)
|
|
|
|
r := chi.NewRouter()
|
|
r.Route("/v1", func(r chi.Router) {
|
|
r.Route("/article", func(r chi.Router) {
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(jwtauth.Verifier(token))
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(jwtauth.Authenticator(token))
|
|
r.Use(middleware.RequireVerifiedUser(getUserUseCase))
|
|
r.Delete("/{id}/image/{imageId}", imageHandler.DeleteImage)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
return r
|
|
}
|
|
|
|
func setRemoveImageJwtCookie(t *testing.T, req *http.Request, token *jwtauth.JWTAuth) {
|
|
t.Helper()
|
|
_, tokenString, err := token.Encode(map[string]any{"user_id": uint32(1)})
|
|
if err != nil {
|
|
t.Fatalf("failed to encode jwt: %v", err)
|
|
}
|
|
req.AddCookie(&http.Cookie{Name: "jwt", Value: tokenString})
|
|
}
|
|
|
|
func TestRemoveArticleImageHttp(t *testing.T) {
|
|
validator.InitValidator()
|
|
token := jwtauth.New("HS256", []byte("test-secret"), nil)
|
|
|
|
t.Run("Removing an image from an article it belongs to succeeds", func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
articleRepo.articles[1] = domainArticle.Article{ID: 1, Title: "Backyard Renovation Update", Images: []*domainImage.Image{{ID: 10}}}
|
|
imageRepo := newMockImageRepo()
|
|
imageRepo.images[10] = domainImage.Image{ID: 10, Path: "/static/uploadedImages/before.png"}
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/1/image/10", nil)
|
|
setRemoveImageJwtCookie(t, req, token)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNoContent {
|
|
t.Fatalf("expected status 204, got %d, body %q", rec.Code, rec.Body.String())
|
|
}
|
|
if rec.Body.Len() != 0 {
|
|
t.Errorf("expected empty body, got %q", rec.Body.String())
|
|
}
|
|
if _, ok := imageRepo.images[10]; ok {
|
|
t.Errorf("expected image 10 to have been removed")
|
|
}
|
|
})
|
|
|
|
t.Run("Removing an image from an unknown article answers 404 and never touches the image repository", func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
imageRepo := newMockImageRepo()
|
|
imageRepo.images[10] = domainImage.Image{ID: 10, Path: "/static/uploadedImages/before.png"}
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/9999/image/10", nil)
|
|
setRemoveImageJwtCookie(t, req, token)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("expected status 404, got %d", rec.Code)
|
|
}
|
|
if rec.Body.String() != apperr.ArticleNotFoundErrorMessage+"\n" {
|
|
t.Errorf("unexpected body: %q", rec.Body.String())
|
|
}
|
|
if len(imageRepo.deleteCalls) != 0 {
|
|
t.Errorf("expected Delete to never be called, got %d calls", len(imageRepo.deleteCalls))
|
|
}
|
|
if _, ok := imageRepo.images[10]; !ok {
|
|
t.Errorf("expected image 10 to still exist")
|
|
}
|
|
})
|
|
|
|
t.Run("Removing an unknown image from an existing article answers 404", func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
articleRepo.articles[1] = domainArticle.Article{ID: 1, Title: "Backyard Renovation Update"}
|
|
imageRepo := newMockImageRepo()
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/1/image/9999", nil)
|
|
setRemoveImageJwtCookie(t, req, token)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("expected status 404, got %d", rec.Code)
|
|
}
|
|
if rec.Body.String() != apperr.ImageNotFoundErrorMessage+"\n" {
|
|
t.Errorf("unexpected body: %q", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("Removing an image id that belongs to a different article still succeeds, because the check is shallow", func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
articleRepo.articles[1] = domainArticle.Article{ID: 1, Title: "Backyard Renovation Update"}
|
|
articleRepo.articles[2] = domainArticle.Article{ID: 2, Images: []*domainImage.Image{{ID: 10}}}
|
|
imageRepo := newMockImageRepo()
|
|
imageRepo.images[10] = domainImage.Image{ID: 10, Path: "/static/uploadedImages/before.png"}
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/1/image/10", nil)
|
|
setRemoveImageJwtCookie(t, req, token)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNoContent {
|
|
t.Fatalf("expected status 204, got %d, body %q", rec.Code, rec.Body.String())
|
|
}
|
|
if _, ok := imageRepo.images[10]; ok {
|
|
t.Errorf("expected image 10 to have been removed")
|
|
}
|
|
})
|
|
|
|
t.Run("Removing an image still requires authentication", func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
articleRepo.articles[1] = domainArticle.Article{ID: 1, Title: "Backyard Renovation Update", Images: []*domainImage.Image{{ID: 10}}}
|
|
imageRepo := newMockImageRepo()
|
|
imageRepo.images[10] = domainImage.Image{ID: 10, Path: "/static/uploadedImages/before.png"}
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/1/image/10", nil)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("expected status 401, got %d", rec.Code)
|
|
}
|
|
if _, ok := imageRepo.images[10]; !ok {
|
|
t.Errorf("expected image 10 to still exist")
|
|
}
|
|
})
|
|
|
|
t.Run("A malformed id in the URL answers 400 before either repository is touched", func(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
articleId string
|
|
imageId string
|
|
}{
|
|
{"malformed article id", "abc", "10"},
|
|
{"malformed image id", "1", "abc"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
articleRepo := newMockArticleRepo()
|
|
imageRepo := newMockImageRepo()
|
|
router := newRemoveArticleImageRouter(articleRepo, imageRepo, token)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/article/"+tc.articleId+"/image/"+tc.imageId, nil)
|
|
setRemoveImageJwtCookie(t, req, token)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected status 400, got %d", rec.Code)
|
|
}
|
|
if rec.Body.String() != apperr.IdUint32ErrorMessage+"\n" {
|
|
t.Errorf("unexpected body: %q", rec.Body.String())
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|