package test import ( "RenewCMS/internal/domain/article" "encoding/json" "net/http" "testing" ) func editorialField[T any](t *testing.T, response storyResponse, key string) T { t.Helper() var value T if err := json.Unmarshal(response.fields(t)[key], &value); err != nil { t.Fatalf("%s: %v in %s", key, err, response.text()) } return value } func translationEntry(t *testing.T, response storyResponse, id string) map[string]json.RawMessage { t.Helper() for _, entry := range translationsOf(t, response) { if string(entry["id"]) == id { return entry } } t.Fatalf("no translation %s in %s", id, response.text()) return nil } func putTrackingDraft(w *storyWorld) { source := uint32(12) w.put(article.Article{ID: 14, Title: "Costes", CreatedBy: storyRestrict, Slug: "costes", Lang: "es", StoryID: storyStory, SourceArticleID: &source, SourceRevision: 2}) } func TestArticleEditorialContextHTTP(t *testing.T) { t.Run("Missing languages are the enabled languages without a readable version", func(t *testing.T) { w := newStoryWorld(t) cases := []struct { userID uint32 missing []string }{ {storyOwner, []string{}}, {storyRestrict, []string{"de", "fr"}}, {storyReader, []string{}}, } for _, c := range cases { got := editorialField[[]string](t, w.send(c.userID, http.MethodGet, "/v1/article/10", ""), "missing_langs") if len(got) != len(c.missing) || len(got) > 0 && (got[0] != c.missing[0] || got[1] != c.missing[1]) { t.Errorf("user %d: missing_langs %v, want %v", c.userID, got, c.missing) } } }) t.Run("A version in a disabled language or without language is left out of public surfaces", func(t *testing.T) { w := newStoryWorld(t) putTrackingDraft(w) w.put(article.Article{ID: 30, Title: "Unassigned", CreatedBy: storyWriter1, Slug: "unassigned", StoryID: 30, IsOnline: true}) owner := w.send(storyOwner, http.MethodGet, "/v1/article/14", "") if !editorialField[bool](t, owner, "excluded_from_public") || string(translationEntry(t, owner, "14")["excluded_from_public"]) != "true" || string(translationEntry(t, owner, "10")["excluded_from_public"]) != "false" { t.Errorf("es must be left out, en kept: %s", owner.text()) } if !editorialField[bool](t, w.send(storyOwner, http.MethodGet, "/v1/article/30", ""), "excluded_from_public") { t.Error("a version without language must be left out of the public language surfaces") } if editorialField[bool](t, w.send(storyOwner, http.MethodGet, "/v1/article/10", ""), "excluded_from_public") { t.Error("an enabled language must not be left out") } }) t.Run("A deleted source and an unreadable source read the same", func(t *testing.T) { w := newStoryWorld(t) putTrackingDraft(w) tracked := w.send(storyOwner, http.MethodGet, "/v1/article/14", "") unreadable := w.send(storyRestrict, http.MethodGet, "/v1/article/14", "") delete(w.articles.articles, 12) deleted := w.send(storyOwner, http.MethodGet, "/v1/article/14", "") if string(tracked.fields(t)["source"]) != `{"article_id":12,"revision":2}` || editorialField[bool](t, tracked, "source_unavailable") { t.Fatalf("a readable source is shown: %s", tracked.text()) } for name, response := range map[string]storyResponse{"unreadable": unreadable, "deleted": deleted} { if string(response.fields(t)["source"]) != "null" || !editorialField[bool](t, response, "source_unavailable") { t.Errorf("%s source: %s", name, response.text()) } } }) t.Run("Translations name the source they track only when it is readable", func(t *testing.T) { w := newStoryWorld(t) putTrackingDraft(w) owner := w.send(storyOwner, http.MethodGet, "/v1/article/10", "") restricted := w.send(storyRestrict, http.MethodGet, "/v1/article/14", "") if string(translationEntry(t, owner, "14")["source_article_id"]) != "12" || string(translationEntry(t, owner, "10")["source_article_id"]) != "null" { t.Errorf("owner translations: %s", owner.text()) } if string(translationEntry(t, restricted, "14")["source_article_id"]) != "null" { t.Errorf("an unreadable source is named: %s", restricted.text()) } }) t.Run("Partial view follows the read scope of the caller only", func(t *testing.T) { w := newStoryWorld(t) if editorialField[bool](t, w.send(storyOwner, http.MethodGet, "/v1/article/10", ""), "partial_view") { t.Error("a caller who reads every version has a complete view") } if !editorialField[bool](t, w.send(storyRestrict, http.MethodGet, "/v1/article/13", ""), "partial_view") { t.Error("a caller limited to own versions has a partial view") } }) t.Run("Anonymous reads carry no editorial keys", func(t *testing.T) { w := newStoryWorld(t) fields := w.send(storyAnon, http.MethodGet, "/v1/article/10", "").fields(t) for _, key := range []string{"missing_langs", "excluded_from_public", "source_unavailable", "partial_view"} { if _, present := fields[key]; present { t.Errorf("anonymous read carries %s", key) } } }) }