package handlers import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strconv" "testing" blogModels "ginimageApi/app/blogs/models" "ginimageApi/configs" "github.com/gin-gonic/gin" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func setupBlogHandlersTestDB(t *testing.T) { t.Helper() prev := configs.DB dsn := "file:" + t.Name() + "?mode=memory&cache=shared" db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{}) if err != nil { t.Fatalf("sqlite open failed: %v", err) } if err := db.AutoMigrate(&blogModels.Category{}, &blogModels.Tag{}, &blogModels.Post{}, &blogModels.CategoryView{}, &blogModels.Comment{}); err != nil { t.Fatalf("migrate failed: %v", err) } configs.DB = db t.Cleanup(func() { if sqlDB, err := db.DB(); err == nil { _ = sqlDB.Close() } configs.DB = prev }) } func withUser(userID uint) gin.HandlerFunc { return func(c *gin.Context) { c.Set("user_id", userID) c.Next() } } func TestListPostsReturnsOnlyActive(t *testing.T) { gin.SetMode(gin.TestMode) setupBlogHandlersTestDB(t) posts := []blogModels.Post{ {Title: "Active Post", Content: "A", IsActive: true, IsFront: true}, {Title: "Passive Post", Content: "B", IsActive: true, IsFront: false}, } if err := configs.DB.Create(&posts).Error; err != nil { t.Fatalf("seed failed: %v", err) } if err := configs.DB.Model(&blogModels.Post{}).Where("title = ?", "Passive Post").Update("is_active", false).Error; err != nil { t.Fatalf("seed update failed: %v", err) } r := gin.New() r.GET("/blogs", ListPosts) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/blogs", nil)) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String()) } var resp struct { Count int `json:"count"` } if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("json parse failed: %v", err) } if resp.Count != 1 { t.Fatalf("expected only active posts, got %d", resp.Count) } } func TestAdminCreateUpdateDeletePost(t *testing.T) { gin.SetMode(gin.TestMode) setupBlogHandlersTestDB(t) r := gin.New() r.POST("/blogs", withUser(1), CreatePost) r.PUT("/blogs/:id", withUser(1), UpdatePost) r.DELETE("/blogs/:id", withUser(1), DeletePost) createBody := []byte(`{"title":"Yeni Blog","content":"icerik"}`) wCreate := httptest.NewRecorder() reqCreate := httptest.NewRequest(http.MethodPost, "/blogs", bytes.NewReader(createBody)) reqCreate.Header.Set("Content-Type", "application/json") r.ServeHTTP(wCreate, reqCreate) if wCreate.Code != http.StatusCreated { t.Fatalf("expected 201, got %d body=%s", wCreate.Code, wCreate.Body.String()) } // SQLite'da mevcut model taniminda auto ID davranisi tutarsiz olabildigi icin // update/delete senaryosunu explicit ID ile seed edilen kayit uzerinden dogruluyoruz. seedForUpdate := blogModels.Post{ID: 77, Title: "Seeded Blog", Content: "x", IsActive: true, IsFront: true} if err := configs.DB.Create(&seedForUpdate).Error; err != nil { t.Fatalf("seed for update failed: %v", err) } updateBody := []byte(`{"title":"Guncel Blog"}`) wUpdate := httptest.NewRecorder() reqUpdate := httptest.NewRequest(http.MethodPut, "/blogs/"+strconv.FormatUint(seedForUpdate.ID, 10), bytes.NewReader(updateBody)) reqUpdate.Header.Set("Content-Type", "application/json") r.ServeHTTP(wUpdate, reqUpdate) if wUpdate.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", wUpdate.Code, wUpdate.Body.String()) } wDelete := httptest.NewRecorder() r.ServeHTTP(wDelete, httptest.NewRequest(http.MethodDelete, "/blogs/"+strconv.FormatUint(seedForUpdate.ID, 10), nil)) if wDelete.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d", wDelete.Code) } } func TestCategoryAndTagEndpoints(t *testing.T) { gin.SetMode(gin.TestMode) setupBlogHandlersTestDB(t) r := gin.New() r.GET("/blogs/categories", ListCategories) r.GET("/blogs/tags", ListTags) r.POST("/blogs/categories", withUser(1), CreateCategory) r.PUT("/blogs/categories/:id", withUser(1), UpdateCategory) r.DELETE("/blogs/categories/:id", withUser(1), DeleteCategory) r.POST("/blogs/tags", withUser(1), CreateTag) r.PUT("/blogs/tags/:id", withUser(1), UpdateTag) r.DELETE("/blogs/tags/:id", withUser(1), DeleteTag) wCreateCategory := httptest.NewRecorder() reqCreateCategory := httptest.NewRequest(http.MethodPost, "/blogs/categories", bytes.NewReader([]byte(`{"title":"Genel"}`))) reqCreateCategory.Header.Set("Content-Type", "application/json") r.ServeHTTP(wCreateCategory, reqCreateCategory) if wCreateCategory.Code != http.StatusCreated { t.Fatalf("category create expected 201, got %d body=%s", wCreateCategory.Code, wCreateCategory.Body.String()) } wCreateTag := httptest.NewRecorder() reqCreateTag := httptest.NewRequest(http.MethodPost, "/blogs/tags", bytes.NewReader([]byte(`{"tag":"Go"}`))) reqCreateTag.Header.Set("Content-Type", "application/json") r.ServeHTTP(wCreateTag, reqCreateTag) if wCreateTag.Code != http.StatusCreated { t.Fatalf("tag create expected 201, got %d body=%s", wCreateTag.Code, wCreateTag.Body.String()) } // SQLite'da mevcut model taniminda auto ID davranisi tutarsiz olabildigi icin // kategori/tag update-delete senaryosunu explicit ID ile seed edilen kayitlar uzerinden dogruluyoruz. seedCategory := blogModels.Category{ID: 77, Title: "SeedCategory", IsActive: true, Order: 1} if err := configs.DB.Create(&seedCategory).Error; err != nil { t.Fatalf("seed category for update failed: %v", err) } seedTag := blogModels.Tag{ID: 88, Tag: "SeedTag", IsActive: true} if err := configs.DB.Create(&seedTag).Error; err != nil { t.Fatalf("seed tag for update failed: %v", err) } wListCategories := httptest.NewRecorder() r.ServeHTTP(wListCategories, httptest.NewRequest(http.MethodGet, "/blogs/categories", nil)) if wListCategories.Code != http.StatusOK { t.Fatalf("category list expected 200, got %d", wListCategories.Code) } wListTags := httptest.NewRecorder() r.ServeHTTP(wListTags, httptest.NewRequest(http.MethodGet, "/blogs/tags", nil)) if wListTags.Code != http.StatusOK { t.Fatalf("tag list expected 200, got %d", wListTags.Code) } wUpdateCategory := httptest.NewRecorder() reqUpdateCategory := httptest.NewRequest(http.MethodPut, "/blogs/categories/"+strconv.FormatUint(seedCategory.ID, 10), bytes.NewReader([]byte(`{"title":"Teknoloji"}`))) reqUpdateCategory.Header.Set("Content-Type", "application/json") r.ServeHTTP(wUpdateCategory, reqUpdateCategory) if wUpdateCategory.Code != http.StatusOK { t.Fatalf("category update expected 200, got %d body=%s", wUpdateCategory.Code, wUpdateCategory.Body.String()) } wUpdateTag := httptest.NewRecorder() reqUpdateTag := httptest.NewRequest(http.MethodPut, "/blogs/tags/"+strconv.FormatUint(seedTag.ID, 10), bytes.NewReader([]byte(`{"tag":"Golang"}`))) reqUpdateTag.Header.Set("Content-Type", "application/json") r.ServeHTTP(wUpdateTag, reqUpdateTag) if wUpdateTag.Code != http.StatusOK { t.Fatalf("tag update expected 200, got %d body=%s", wUpdateTag.Code, wUpdateTag.Body.String()) } wDeleteCategory := httptest.NewRecorder() r.ServeHTTP(wDeleteCategory, httptest.NewRequest(http.MethodDelete, "/blogs/categories/"+strconv.FormatUint(seedCategory.ID, 10), nil)) if wDeleteCategory.Code != http.StatusNoContent { t.Fatalf("category delete expected 204, got %d", wDeleteCategory.Code) } wDeleteTag := httptest.NewRecorder() r.ServeHTTP(wDeleteTag, httptest.NewRequest(http.MethodDelete, "/blogs/tags/"+strconv.FormatUint(seedTag.ID, 10), nil)) if wDeleteTag.Code != http.StatusNoContent { t.Fatalf("tag delete expected 204, got %d", wDeleteTag.Code) } }