package services import ( "database/sql" "testing" "time" "quyun/v2/app/commands/testx" "quyun/v2/app/http/tenant/dto" "quyun/v2/database" "quyun/v2/database/models" "quyun/v2/pkg/consts" . "github.com/smartystreets/goconvey/convey" "github.com/stretchr/testify/suite" _ "go.ipao.vip/atom" "go.ipao.vip/atom/contracts" "go.ipao.vip/gen/types" "go.uber.org/dig" ) type ContentTestSuiteInjectParams struct { dig.In DB *sql.DB Initials []contracts.Initial `group:"initials"` // nolint:structcheck } type ContentTestSuite struct { suite.Suite ContentTestSuiteInjectParams } func Test_Content(t *testing.T) { providers := testx.Default().With(Provide) testx.Serve(providers, t, func(p ContentTestSuiteInjectParams) { suite.Run(t, &ContentTestSuite{ContentTestSuiteInjectParams: p}) }) } func (s *ContentTestSuite) Test_Create() { Convey("Content.Create", s.T(), func() { ctx := s.T().Context() tenantID := int64(1) userID := int64(2) database.Truncate(ctx, s.DB, models.TableNameContent) Convey("成功创建草稿内容并应用默认策略", func() { form := &dto.ContentCreateForm{ Title: "标题", Description: "描述", } m, err := Content.Create(ctx, tenantID, userID, form) So(err, ShouldBeNil) So(m, ShouldNotBeNil) So(m.TenantID, ShouldEqual, tenantID) So(m.UserID, ShouldEqual, userID) So(m.Status, ShouldEqual, consts.ContentStatusDraft) So(m.Visibility, ShouldEqual, consts.ContentVisibilityTenantOnly) So(m.PreviewSeconds, ShouldEqual, consts.DefaultContentPreviewSeconds) So(m.PreviewDownloadable, ShouldBeFalse) }) }) } func (s *ContentTestSuite) Test_Update() { Convey("Content.Update", s.T(), func() { ctx := s.T().Context() tenantID := int64(1) userID := int64(2) database.Truncate(ctx, s.DB, models.TableNameContent) m := &models.Content{ TenantID: tenantID, UserID: userID, Title: "标题", Description: "描述", Status: consts.ContentStatusDraft, Visibility: consts.ContentVisibilityTenantOnly, PreviewSeconds: consts.DefaultContentPreviewSeconds, PreviewDownloadable: false, } So(m.Create(ctx), ShouldBeNil) Convey("发布内容应写入 published_at", func() { status := consts.ContentStatusPublished form := &dto.ContentUpdateForm{Status: &status} updated, err := Content.Update(ctx, tenantID, userID, m.ID, form) So(err, ShouldBeNil) So(updated, ShouldNotBeNil) So(updated.Status, ShouldEqual, consts.ContentStatusPublished) So(updated.PublishedAt.IsZero(), ShouldBeFalse) }) }) } func (s *ContentTestSuite) Test_UpsertPrice() { Convey("Content.UpsertPrice", s.T(), func() { ctx := s.T().Context() tenantID := int64(1) userID := int64(2) database.Truncate(ctx, s.DB, models.TableNameContentPrice, models.TableNameContent) content := &models.Content{ TenantID: tenantID, UserID: userID, Title: "标题", Description: "描述", Status: consts.ContentStatusDraft, Visibility: consts.ContentVisibilityTenantOnly, PreviewSeconds: consts.DefaultContentPreviewSeconds, PreviewDownloadable: false, } So(content.Create(ctx), ShouldBeNil) Convey("首次 upsert 应创建价格记录", func() { form := &dto.ContentPriceUpsertForm{ PriceAmount: 100, } price, err := Content.UpsertPrice(ctx, tenantID, userID, content.ID, form) So(err, ShouldBeNil) So(price, ShouldNotBeNil) So(price.PriceAmount, ShouldEqual, 100) So(price.Currency, ShouldEqual, consts.CurrencyCNY) So(price.DiscountType, ShouldEqual, consts.DiscountTypeNone) }) Convey("再次 upsert 应更新价格记录", func() { form1 := &dto.ContentPriceUpsertForm{PriceAmount: 100} _, err := Content.UpsertPrice(ctx, tenantID, userID, content.ID, form1) So(err, ShouldBeNil) form2 := &dto.ContentPriceUpsertForm{ PriceAmount: 200, DiscountType: consts.DiscountTypePercent, DiscountValue: 10, } price2, err := Content.UpsertPrice(ctx, tenantID, userID, content.ID, form2) So(err, ShouldBeNil) So(price2.PriceAmount, ShouldEqual, 200) So(price2.DiscountType, ShouldEqual, consts.DiscountTypePercent) So(price2.DiscountValue, ShouldEqual, 10) }) }) } func (s *ContentTestSuite) Test_AttachAsset() { Convey("Content.AttachAsset", s.T(), func() { ctx := s.T().Context() now := time.Now().UTC() tenantID := int64(1) userID := int64(2) database.Truncate(ctx, s.DB, models.TableNameContentAsset, models.TableNameMediaAsset, models.TableNameContent) content := &models.Content{ TenantID: tenantID, UserID: userID, Title: "标题", Description: "描述", Status: consts.ContentStatusDraft, Visibility: consts.ContentVisibilityTenantOnly, PreviewSeconds: consts.DefaultContentPreviewSeconds, PreviewDownloadable: false, } So(content.Create(ctx), ShouldBeNil) asset := &models.MediaAsset{ TenantID: tenantID, UserID: userID, Type: consts.MediaAssetTypeVideo, Status: consts.MediaAssetStatusReady, Provider: "test", Bucket: "bucket", ObjectKey: "obj", Meta: types.JSON([]byte("{}")), } So(asset.Create(ctx), ShouldBeNil) Convey("成功绑定资源到内容", func() { m, err := Content.AttachAsset(ctx, tenantID, userID, content.ID, asset.ID, consts.ContentAssetRoleMain, 1, now) So(err, ShouldBeNil) So(m, ShouldNotBeNil) So(m.ContentID, ShouldEqual, content.ID) So(m.AssetID, ShouldEqual, asset.ID) So(m.Role, ShouldEqual, consts.ContentAssetRoleMain) }) }) } func (s *ContentTestSuite) Test_HasAccess() { Convey("Content.HasAccess", s.T(), func() { ctx := s.T().Context() now := time.Now().UTC() tenantID := int64(1) userID := int64(2) database.Truncate(ctx, s.DB, models.TableNameContentAccess, models.TableNameContent) content := &models.Content{ TenantID: tenantID, UserID: userID, Title: "标题", Description: "描述", Status: consts.ContentStatusPublished, Visibility: consts.ContentVisibilityTenantOnly, PreviewSeconds: consts.DefaultContentPreviewSeconds, PreviewDownloadable: false, PublishedAt: now, } So(content.Create(ctx), ShouldBeNil) Convey("未授予权益应返回 false", func() { ok, err := Content.HasAccess(ctx, tenantID, userID, content.ID) So(err, ShouldBeNil) So(ok, ShouldBeFalse) }) Convey("权益 active 应返回 true", func() { access := &models.ContentAccess{ TenantID: tenantID, UserID: userID, ContentID: content.ID, OrderID: 0, Status: consts.ContentAccessStatusActive, RevokedAt: time.Time{}, CreatedAt: now, UpdatedAt: now, } So(access.Create(ctx), ShouldBeNil) ok, err := Content.HasAccess(ctx, tenantID, userID, content.ID) So(err, ShouldBeNil) So(ok, ShouldBeTrue) }) }) }