feat: editor image removal QOLs
This commit is contained in:
1 parent
0fc00ceeff
commit
b29d0ed045
23 files changed
+1398
-151
No files matched your search
@@ -0,0 +1,196 @@
|
||||
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())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user