package service import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tcs-iptv/tcs/internal/chain" "github.com/tcs-iptv/tcs/internal/hash" ) // issueOne 完成一次"送审→CSPS审核→发码签发",返回 maCode、ctid、证书。 func issueOne(t *testing.T, s *Service) (string, string, string) { t.Helper() sub, err := s.SubmitForReview(sampleSub()) require.NoError(t, err) require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "reviewer-1")) // 审核在前 issued, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "北京市广播电视局") require.NoError(t, err) return issued.MACode, issued.ContentTwinID, issued.Certificate } func TestCSPSAndTranscode(t *testing.T) { s := newService(t) maCode, ctid, _ := issueOne(t, s) _, err := s.BindTranscoded(chain.RoleReviewer, ctid, "filehash-abc", "transcoded-h265-4k", "H.265", "3840x2160", "v1.0-4k") require.NoError(t, err) // 转码版也能验真通过 res, err := s.Verify(maCode, "transcoded-h265-4k") require.NoError(t, err) assert.True(t, res.Match) } func TestCSPSRejected(t *testing.T) { s := newService(t) sub, err := s.SubmitForReview(sampleSub()) require.NoError(t, err) // CSPS 审核驳回 → 不得发码 require.NoError(t, s.ReviewCSPS(sub.ReviewID, false, "reviewer-1")) _, err = s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer") assert.ErrorIs(t, err, ErrNotApproved) } func TestIssueRequiresCSPSApproval(t *testing.T) { s := newService(t) sub, err := s.SubmitForReview(sampleSub()) require.NoError(t, err) // 未经 CSPS 审核直接发码 → 拒绝 _, err = s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer") assert.ErrorIs(t, err, ErrNotApproved) } func TestIngestAndPublish(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "广东IPTV媒资库")) // 无证书发布被拒 err := s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: ""}) assert.ErrorIs(t, err, ErrNoCertificate) // 携带证书发布成功 require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) } func TestInjectToCDN_MatchAndMismatch(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库")) require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) // 哈希匹配 → 允许注入 res, err := s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "CT-IPTV-GD", "cdn://ct-gd/iptv/vod/008923") require.NoError(t, err) assert.True(t, res.Allowed) assert.NotEmpty(t, res.DistributionID) // 哈希不匹配 → 拒绝注入 res, err = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "tampered-hash", "CT-IPTV-GD", "cdn://x") assert.ErrorIs(t, err, ErrHashMismatch) assert.False(t, res.Allowed) } func TestInjectToCDN_RevokedBlocked(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库")) require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) // 下架后不得注入 _, err := s.Takedown(chain.RoleRegulator, maCode, "违规") require.NoError(t, err) _, err = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "OP", "cdn://x") assert.ErrorIs(t, err, ErrNotApproved) } func TestTakedown_ResolvesMappings(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库")) require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) _, _ = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "CT-IPTV-GD", "cdn://ct-gd/vod/1") // 非监管主体不得下架 _, err := s.Takedown(chain.RoleOperator, maCode, "越权") assert.ErrorIs(t, err, chain.ErrPermissionDenied) // 监管下架,解析出 CDN 端点 res, err := s.Takedown(chain.RoleRegulator, maCode, "违规") require.NoError(t, err) assert.Contains(t, res.CDNEndpoints, "cdn://ct-gd/vod/1") } func TestReportVersionChange_LocatesEpisode(t *testing.T) { s := newService(t) _, ctid, _ := issueOne(t, s) old := []string{ hash.SHA256Hex([]byte("ep1")), hash.SHA256Hex([]byte("ep2")), hash.SHA256Hex([]byte("ep3")), } neu := []string{ hash.SHA256Hex([]byte("ep1")), hash.SHA256Hex([]byte("ep2-tampered")), hash.SHA256Hex([]byte("ep3")), } episodes, err := s.ReportVersionChange(ctid, "第2集被替换", "root-old", "root-new", old, neu) require.NoError(t, err) assert.Equal(t, []int{2}, episodes, "应定位到第2集(1-based)") }