init: AIGC-Hub/AVCC 方案文档 + TCS-IPTV 内容可信锁定系统 MVP
- 方案文档: AVCC 体系建设、IPTV TCS 需求(0-req)/PRD(1-prd)/任务(2-task)/二三四期任务 - tcs-iptv: Go 后端(哈希SDK/MA码生成/可信数据空间mock/业务编排/HTTP API+HMAC鉴权) - web-console: React+AntD 监管大屏(角色工作台/全流程演示/监管片库) - 一剧一码+集级哈希, 集级下架/恢复, 全栈测试通过
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
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)")
|
||||
}
|
||||
Reference in New Issue
Block a user