package test import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/language" "RenewCMS/internal/domain/site" "RenewCMS/internal/infrastructure/useCases" "context" "errors" "testing" ) const ( createLanguageSiteID = 1 createLanguageTitle = "Bonjour le monde" confirmedDefaultTag = "fr-CA" createLanguageOtherSiteID = 2 createLanguageOtherSiteKey = "atelier" ) func newCreateLanguageFixture(confirmed map[uint32]string) (*useCases.CreateArticleUseCase, *mockCreateTransactor, *mockArticleRepo) { articles := newMockArticleRepo() transactor := passthroughCreateTransactor(articles) transactor.languages.confirmed = confirmed return useCases.NewCreateArticleUseCase(transactor, &fakeBodyRenderer{}, discardRecorder, plainSites(), plainAuthors()), transactor, articles } func createLanguageCommand() useCases.CreateArticleCommand { return useCases.CreateArticleCommand{Title: createLanguageTitle, Caller: ownerCaller(1)} } func TestCreateArticleStartingLanguage(t *testing.T) { t.Run("Creating an article before the default is confirmed", func(t *testing.T) { create, transactor, articles := newCreateLanguageFixture(map[uint32]string{}) created, err := create.CreateArticle(context.Background(), createLanguageCommand()) if err != nil || articles.articles[created.ID].Lang != language.Unassigned { t.Fatalf("expected an unassigned article, got %+v %v", articles.articles[created.ID], err) } if len(transactor.languages.locked) != 1 || transactor.languages.locked[0] != createLanguageSiteID { t.Errorf("the site language must be read once under the site lock: %v", transactor.languages.locked) } }) t.Run("Creating an article after the default is confirmed", func(t *testing.T) { create, _, articles := newCreateLanguageFixture(map[uint32]string{createLanguageSiteID: confirmedDefaultTag}) created, err := create.CreateArticle(context.Background(), createLanguageCommand()) if err != nil || articles.articles[created.ID].Lang != confirmedDefaultTag { t.Fatalf("expected lang %s, got %+v %v", confirmedDefaultTag, articles.articles[created.ID], err) } }) t.Run("A slug is unique per language when a new article takes the confirmed default", func(t *testing.T) { create, _, articles := newCreateLanguageFixture(map[uint32]string{createLanguageSiteID: confirmedDefaultTag}) articles.articles[50] = article.Article{ID: 50, SiteID: createLanguageSiteID, Lang: "en", Slug: "bonjour-le-monde"} first, firstErr := create.CreateArticle(context.Background(), createLanguageCommand()) second, secondErr := create.CreateArticle(context.Background(), createLanguageCommand()) if firstErr != nil || secondErr != nil { t.Fatalf("unexpected errors %v %v", firstErr, secondErr) } if first.Slug != "bonjour-le-monde" || second.Slug != "bonjour-le-monde-2" { t.Errorf("slugs %q then %q", first.Slug, second.Slug) } }) t.Run("A failure reading the site language aborts the creation", func(t *testing.T) { create, transactor, articles := newCreateLanguageFixture(map[uint32]string{}) failure := errors.New("language read failed") transactor.languages.failWith = failure _, err := create.CreateArticle(context.Background(), createLanguageCommand()) if !errors.Is(err, failure) || articles.createCalled { t.Errorf("expected %v without a create, got %v", failure, err) } }) t.Run("The command site decides the site and the language lock", func(t *testing.T) { create, transactor, articles := newCreateLanguageFixture(map[uint32]string{createLanguageSiteID: confirmedDefaultTag, createLanguageOtherSiteID: "de"}) create = useCases.NewCreateArticleUseCase(transactor, &fakeBodyRenderer{}, discardRecorder, createLanguageSites(), plainAuthors()) command := createLanguageCommand() command.Site = stringPointer(createLanguageOtherSiteKey) created, err := create.CreateArticle(context.Background(), command) stored := articles.articles[created.ID] if err != nil || stored.SiteID != createLanguageOtherSiteID || stored.Lang != "de" { t.Fatalf("expected a de article on site %d, got %+v %v", createLanguageOtherSiteID, stored, err) } if len(transactor.languages.locked) != 1 || transactor.languages.locked[0] != createLanguageOtherSiteID { t.Errorf("the chosen site must be locked: %v", transactor.languages.locked) } }) t.Run("An unknown command site stores nothing", func(t *testing.T) { create, transactor, articles := newCreateLanguageFixture(map[uint32]string{}) create = useCases.NewCreateArticleUseCase(transactor, &fakeBodyRenderer{}, discardRecorder, createLanguageSites(), plainAuthors()) command := createLanguageCommand() command.Site = stringPointer("missing") _, err := create.CreateArticle(context.Background(), command) if !errors.Is(err, site.ErrSiteNotFound) || articles.createCalled || transactor.runs != 0 { t.Errorf("expected site not found without a transaction, got %v", err) } }) t.Run("An omitted command site reads the default site", func(t *testing.T) { sites := createLanguageSites() create, transactor, articles := newCreateLanguageFixture(map[uint32]string{createLanguageSiteID: confirmedDefaultTag}) create = useCases.NewCreateArticleUseCase(transactor, &fakeBodyRenderer{}, discardRecorder, sites, plainAuthors()) created, err := create.CreateArticle(context.Background(), createLanguageCommand()) if err != nil || articles.articles[created.ID].SiteID != createLanguageSiteID { t.Errorf("expected the default site, got %+v %v", articles.articles[created.ID], err) } }) } func createLanguageSites() *mockSiteRepo { sites := plainSites() sites.seed(createLanguageOtherSiteID, "Atelier", createLanguageOtherSiteKey, false) return sites }