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/macode" ) func newService(t *testing.T) *Service { t.Helper() gen := macode.NewGenerator(macode.NewMemoryStore()) require.NoError(t, gen.RegisterSegment(macode.Segment{ IndustryNode: "8531", OrgNode: "4401", Category: macode.CategoryMicroDrama, Start: 1, End: 100, SeqWidth: 7, })) return New(chain.NewMemoryChain(), gen) } func sampleSub() Submission { return Submission{ Title: "示例微短剧", EpisodeCount: 24, Category: macode.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: macode.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_GeneratesMACode(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, macode.IsValid(issued.MACode), "应生成合法 MA 码: %s", issued.MACode) assert.True(t, strings.HasPrefix(issued.MACode, "MA.156.8531.4401/WD/"), "前缀应匹配号段: %s", issued.MACode) assert.NotEmpty(t, issued.TxID) assert.Contains(t, issued.Certificate, issued.MACode) } 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.MACode, "filehash-abc") require.NoError(t, err) assert.True(t, res.Match) _, err = s.Verify(issued.MACode, "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.MACode, i2.MACode, "两条内容应分配不同 MA 码") }