package service import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tcs-iptv/tcs/internal/chain" "github.com/tcs-iptv/tcs/internal/cccode" ) func newService(t *testing.T) *Service { t.Helper() gen := cccode.NewGenerator(cccode.NewMemoryStore()) require.NoError(t, gen.RegisterSegment(cccode.Segment{ IndustryNode: "8531", OrgNode: "4401", Category: cccode.CategoryMicroDrama, Start: 1, End: 100, SeqWidth: 7, })) return New(chain.NewMemoryChain(), gen) } func sampleSub() Submission { return Submission{ Title: "示例微短剧", EpisodeCount: 24, Category: cccode.CategoryMicroDrama, FileHash: "filehash-abc", MerkleRoot: "merkle-abc", Perceptual: "phash-abc", CPMediaID: "FS-MEDIA-77821", CPName: "飞翮信息", } } func TestSubmit_IncompleteHashRejected(t *testing.T) { s := newService(t) _, err := s.SubmitForReview(Submission{Title: "无哈希", Category: cccode.CategoryMicroDrama}) assert.ErrorIs(t, err, ErrIncompleteHashPkg) } func TestSubmit_Success(t *testing.T) { s := newService(t) res, err := s.SubmitForReview(sampleSub()) require.NoError(t, err) assert.NotEmpty(t, res.ReviewID) assert.NotEmpty(t, res.ContentTwinID) assert.Equal(t, "pending", res.Status) } func TestApproveAndIssue_GeneratesCCCode(t *testing.T) { s := newService(t) sub, err := s.SubmitForReview(sampleSub()) require.NoError(t, err) require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1")) issued, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "北京市广播电视局") require.NoError(t, err) // 模式B:MA 码由系统按号段生成 assert.True(t, cccode.IsValid(issued.CCCode), "应生成合法 MA 码: %s", issued.CCCode) assert.True(t, strings.HasPrefix(issued.CCCode, "MA.156.8531.4401/WD/"), "前缀应匹配号段: %s", issued.CCCode) assert.NotEmpty(t, issued.TxID) assert.Contains(t, issued.Certificate, issued.CCCode) } func TestApproveAndIssue_OnlyRegulator(t *testing.T) { s := newService(t) sub, _ := s.SubmitForReview(sampleSub()) require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1")) // 先过审,才轮到校验角色 _, err := s.ApproveAndIssue(chain.RoleCP, sub.ReviewID, "x") assert.ErrorIs(t, err, chain.ErrPermissionDenied) } func TestSubmit_DuplicateRejectedAfterIssue(t *testing.T) { s := newService(t) sub, _ := s.SubmitForReview(sampleSub()) require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1")) _, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer") require.NoError(t, err) // 同哈希再次送审 → 换壳重发拦截 _, err = s.SubmitForReview(sampleSub()) assert.ErrorIs(t, err, ErrDuplicateContent) } func TestVerify_MatchAndMismatch(t *testing.T) { s := newService(t) sub, _ := s.SubmitForReview(sampleSub()) require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1")) issued, _ := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer") res, err := s.Verify(issued.CCCode, "filehash-abc") require.NoError(t, err) assert.True(t, res.Match) _, err = s.Verify(issued.CCCode, "tampered") assert.ErrorIs(t, err, ErrHashMismatch) } func TestApproveAndIssue_TwoContentsUniqueCodes(t *testing.T) { s := newService(t) sub1, _ := s.SubmitForReview(sampleSub()) require.NoError(t, s.ReviewCSPS(sub1.ReviewID, true, "rv-1")) i1, err := s.ApproveAndIssue(chain.RoleRegulator, sub1.ReviewID, "issuer") require.NoError(t, err) sub2v := sampleSub() sub2v.FileHash = "filehash-def" sub2v.MerkleRoot = "merkle-def" sub2, _ := s.SubmitForReview(sub2v) require.NoError(t, s.ReviewCSPS(sub2.ReviewID, true, "rv-1")) i2, err := s.ApproveAndIssue(chain.RoleRegulator, sub2.ReviewID, "issuer") require.NoError(t, err) assert.NotEqual(t, i1.CCCode, i2.CCCode, "两条内容应分配不同 MA 码") }