package test import ( "RenewCMS/internal/domain/article" "encoding/json" "net/http" "strconv" "testing" ) type mediaListBody struct { Items []struct { ID uint32 `json:"id"` UsedIn int `json:"used_in"` } `json:"items"` Counts struct { Unused int `json:"unused"` } `json:"counts"` } func (e *siteEnv) useImageOnSite(siteID uint32, imageID uint32) { s := e.sites.sites[siteID] s.DefaultSocialImageID = idPointer(imageID) e.sites.sites[siteID] = s } func (e *siteEnv) seedDuplicates() { e.seedBanner(5, "banner.png", "samehash") e.seedBanner(6, "banner-copy.png", "samehash") } func TestSitesMediaUsage(t *testing.T) { t.Run("An image used as a default social image is counted as used", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedBanner(5, "banner.png", "hash") e.useImageOnSite(siteShopID, 5) rec := e.owner(t, http.MethodGet, "/v1/media/", "") unused := e.owner(t, http.MethodGet, "/v1/media/?filter=unused", "") var list, unusedList mediaListBody _ = json.Unmarshal(rec.Body.Bytes(), &list) _ = json.Unmarshal(unused.Body.Bytes(), &unusedList) if len(list.Items) != 1 || list.Items[0].UsedIn != 1 || list.Counts.Unused != 0 || len(unusedList.Items) != 0 { t.Errorf("list %s, unused %s", rec.Body.String(), unused.Body.String()) } }) t.Run("Deleting the image clears the site's field", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedBanner(5, "banner.png", "hash") e.useImageOnSite(siteShopID, 5) rec := e.owner(t, http.MethodDelete, "/v1/media/5", "") if rec.Code != http.StatusNoContent || e.sites.sites[siteShopID].DefaultSocialImageID != nil { t.Errorf("status %d, image %v", rec.Code, e.sites.sites[siteShopID].DefaultSocialImageID) } }) t.Run("A failed image deletion keeps the site's field", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedBanner(5, "banner.png", "hash") e.useImageOnSite(siteShopID, 5) e.images.failDeleteFor = map[uint32]error{5: errRepositoryFailure} rec := e.owner(t, http.MethodDelete, "/v1/media/5", "") if rec.Code != http.StatusInternalServerError || e.sites.sites[siteShopID].DefaultSocialImageID == nil { t.Errorf("status %d, image %v", rec.Code, e.sites.sites[siteShopID].DefaultSocialImageID) } }) t.Run("Merging copies repoints the site", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedDuplicates() e.useImageOnSite(siteShopID, 6) rec := e.owner(t, http.MethodPost, "/v1/media/merge", `{"keep_id": 5, "merge_ids": [6]}`) var body map[string]int _ = json.Unmarshal(rec.Body.Bytes(), &body) kept := e.sites.sites[siteShopID].DefaultSocialImageID if rec.Code != http.StatusOK || body["sites_updated"] != 1 || kept == nil || *kept != 5 { t.Errorf("status %d, body %s, image %v", rec.Code, rec.Body.String(), kept) } }) t.Run("Purge repoints sites the same way", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedDuplicates() e.articles.articles[1] = articleUsingImage(5) e.useImageOnSite(siteShopID, 6) rec := e.owner(t, http.MethodPost, "/v1/media/purge", "") var body map[string]int _ = json.Unmarshal(rec.Body.Bytes(), &body) kept := e.sites.sites[siteShopID].DefaultSocialImageID if rec.Code != http.StatusOK || body["sites_updated"] != 1 || kept == nil || *kept != 5 { t.Errorf("status %d, body %s, image %v", rec.Code, rec.Body.String(), kept) } }) t.Run("A failed purge group leaves the site pointing at an existing image", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedDuplicates() e.articles.articles[1] = articleUsingImage(5) e.useImageOnSite(siteShopID, 6) e.images.failDeleteFor = map[uint32]error{6: errRepositoryFailure} rec := e.owner(t, http.MethodPost, "/v1/media/purge", "") referenced := e.sites.sites[siteShopID].DefaultSocialImageID if rec.Code != http.StatusOK || referenced == nil { t.Fatalf("status %d, image %v", rec.Code, referenced) } if _, exists := e.images.images[*referenced]; !exists { t.Errorf("the site points at the missing image %d", *referenced) } }) t.Run("The usage lookup stays an array and later gains additive keys", func(t *testing.T) { e := newSiteEnvWithShop(t) e.seedBanner(5, "banner.png", "hash") e.useImageOnSite(siteShopID, 5) e.articles.articles[1] = articleUsingImage(5) rec := e.owner(t, http.MethodGet, "/v1/media/5/usage", "") var items []map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &items); err != nil || rec.Code != http.StatusOK || len(items) != 2 { t.Fatalf("status %d, body %s", rec.Code, rec.Body.String()) } first := items[0] if first["id"] != float64(1) || first["title"] != "Spring release" || first["published"] != true || first["type"] != "article" { t.Errorf("the article item lost a key: %v", first) } }) } func articleUsingImage(imageID uint32) article.Article { return article.Article{ID: 1, Title: "Spring release", Body: ``, IsOnline: true, SiteID: siteMainID} }