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/model" ) // 全链路一致:发码→入库→正确注入,追责判定审播一致。 func TestAccountability_Consistent(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "M", "陕西IPTV媒资库")) require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) _, err := s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "CT-SX", "cdn://x") require.NoError(t, err) rep := s.Accountability(maCode) assert.True(t, rep.Consistent, "全链路应一致") assert.Nil(t, rep.FirstChange) assert.Equal(t, "filehash-abc", rep.BaselineHash) assert.NotEmpty(t, rep.Trail) } // 注入环节偷换:追责定位到 cdn_inject 节点与运营商。 func TestAccountability_TamperLocated(t *testing.T) { s := newService(t) maCode, ctid, cert := issueOne(t, s) require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "M", "媒资库")) require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert})) // 直接往存证里记录一次"被偷换"的注入(绕过校验模拟违规留痕) // 实际中 InjectToCDN 会拒绝不匹配,但运营商侧若绕过校验偷换,存证仍会暴露 _, _ = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "CT-SX", "cdn://x") s.prov.Record(model.ProvenanceEvent{ MACode: maCode, Node: model.NodeInject, HashValue: "TAMPERED", Operator: "CT-SX-违规", Detail: "疑似偷换", }) rep := s.Accountability(maCode) assert.False(t, rep.Consistent) require.NotNil(t, rep.FirstChange) assert.Equal(t, model.NodeInject, rep.FirstChange.Node) assert.Contains(t, rep.Conclusion, "cdn_inject") } // 转码版哈希不同属合法,不应误判为篡改。 func TestAccountability_TranscodeNotFlagged(t *testing.T) { s := newService(t) maCode, ctid, _ := issueOne(t, s) _, err := s.BindTranscoded(chain.RoleReviewer, ctid, "filehash-abc", "h265-4k", "H.265", "4K", "v1-4k") require.NoError(t, err) s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeTranscode, HashValue: "h265-4k", Operator: "转码中心"}) rep := s.Accountability(maCode) assert.True(t, rep.Consistent, "转码版哈希不同属合法,不应判为篡改") } func TestCopyrightEvidence(t *testing.T) { s := newService(t) maCode, _, _ := issueOne(t, s) ev, err := s.CopyrightEvidence(maCode) require.NoError(t, err) assert.Equal(t, maCode, ev.MACode) assert.NotEmpty(t, ev.Trail) assert.False(t, ev.FirstSeenAt.IsZero(), "应有最早登记时间戳") assert.Contains(t, ev.Statement, "谁先锁定谁有权") } func TestMatchInfringement(t *testing.T) { s := newService(t) // 注册一部正版,感知哈希为合法 16 位十六进制(模拟 aHash/dHash 输出) sub := sampleSub() sub.Perceptual = "a1b2c3d4e5f60718" r, err := s.SubmitForReview(sub) require.NoError(t, err) require.NoError(t, s.ReviewCSPS(r.ReviewID, true, "rv-1")) issued, err := s.ApproveAndIssue(chain.RoleRegulator, r.ReviewID, "陕西IPTV") require.NoError(t, err) // 完全相同的感知哈希 → 高度相似(距离 0) matches, err := s.MatchInfringement("a1b2c3d4e5f60718", 5, 10) require.NoError(t, err) require.NotEmpty(t, matches) assert.Equal(t, issued.MACode, matches[0].MACode) assert.Equal(t, "high", matches[0].Similarity) assert.Equal(t, 0, matches[0].Distance) // 差异极大的哈希 → 不命中 noMatch, err := s.MatchInfringement("ffffffffffffffff", 5, 10) require.NoError(t, err) assert.Empty(t, noMatch) }