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,151 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func writeTempFile(t *testing.T, data []byte) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
p := filepath.Join(dir, "master.bin")
|
||||
require.NoError(t, os.WriteFile(p, data, 0o644))
|
||||
return p
|
||||
}
|
||||
|
||||
func TestSHA256Hex_Deterministic(t *testing.T) {
|
||||
a := SHA256Hex([]byte("hello"))
|
||||
b := SHA256Hex([]byte("hello"))
|
||||
assert.Equal(t, a, b)
|
||||
// 已知向量
|
||||
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", a)
|
||||
}
|
||||
|
||||
func TestFileSHA256_MatchesBytes(t *testing.T) {
|
||||
data := []byte("the quick brown fox")
|
||||
p := writeTempFile(t, data)
|
||||
got, err := FileSHA256(p)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, SHA256Hex(data), got)
|
||||
}
|
||||
|
||||
func TestSegmentHashes_SmallSegments(t *testing.T) {
|
||||
// 25 字节,分段 10 → 3 段(10/10/5)
|
||||
data := []byte("0123456789ABCDEFGHIJ12345")
|
||||
p := writeTempFile(t, data)
|
||||
segs, err := SegmentHashes(p, 10)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, segs, 3)
|
||||
assert.Equal(t, SHA256Hex(data[0:10]), segs[0])
|
||||
assert.Equal(t, SHA256Hex(data[10:20]), segs[1])
|
||||
assert.Equal(t, SHA256Hex(data[20:25]), segs[2])
|
||||
}
|
||||
|
||||
func TestMerkleTree_RootStableAndChangesOnEdit(t *testing.T) {
|
||||
leaves := []string{
|
||||
SHA256Hex([]byte("ep1")),
|
||||
SHA256Hex([]byte("ep2")),
|
||||
SHA256Hex([]byte("ep3")),
|
||||
SHA256Hex([]byte("ep4")),
|
||||
}
|
||||
root1 := BuildMerkleTree(leaves).Root()
|
||||
root2 := BuildMerkleTree(leaves).Root()
|
||||
assert.Equal(t, root1, root2, "同样叶子根应一致")
|
||||
assert.NotEmpty(t, root1)
|
||||
|
||||
// 改第3集 → 根变化
|
||||
edited := append([]string(nil), leaves...)
|
||||
edited[2] = SHA256Hex([]byte("ep3-tampered"))
|
||||
root3 := BuildMerkleTree(edited).Root()
|
||||
assert.NotEqual(t, root1, root3, "篡改任一集,根必变")
|
||||
}
|
||||
|
||||
func TestMerkleTree_OddLeaves(t *testing.T) {
|
||||
leaves := []string{
|
||||
SHA256Hex([]byte("a")),
|
||||
SHA256Hex([]byte("b")),
|
||||
SHA256Hex([]byte("c")),
|
||||
}
|
||||
mt := BuildMerkleTree(leaves)
|
||||
assert.NotEmpty(t, mt.Root())
|
||||
}
|
||||
|
||||
func TestLocateChangedLeaves(t *testing.T) {
|
||||
old := []string{"h1", "h2", "h3", "h4"}
|
||||
neu := []string{"h1", "x2", "h3", "x4"}
|
||||
changed := LocateChangedLeaves(old, neu)
|
||||
assert.Equal(t, []int{1, 3}, changed, "应定位到第2集和第4集被改")
|
||||
}
|
||||
|
||||
func TestComputeFile_FullPackage(t *testing.T) {
|
||||
data := make([]byte, 25*1024) // 25KB
|
||||
for i := range data {
|
||||
data[i] = byte(i % 251)
|
||||
}
|
||||
p := writeTempFile(t, data)
|
||||
|
||||
pkg, err := ComputeFile(p, Options{SegmentSize: 10 * 1024})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, pkg.Validate())
|
||||
assert.Equal(t, int64(25*1024), pkg.FileSize)
|
||||
assert.Len(t, pkg.SegmentHashes, 3)
|
||||
assert.NotEmpty(t, pkg.MerkleRoot)
|
||||
assert.Equal(t, SHA256Hex(data), pkg.FileSHA256)
|
||||
}
|
||||
|
||||
func TestComputeFile_EmptyFileRejected(t *testing.T) {
|
||||
p := writeTempFile(t, []byte{})
|
||||
_, err := ComputeFile(p, Options{})
|
||||
assert.ErrorIs(t, err, ErrEmptyInput)
|
||||
}
|
||||
|
||||
func TestComputeFile_MissingFile(t *testing.T) {
|
||||
_, err := ComputeFile("/no/such/file.bin", Options{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHashPackage_ValidateMissingFields(t *testing.T) {
|
||||
assert.Error(t, (&HashPackage{MerkleRoot: "x"}).Validate()) // 缺 file_sha256
|
||||
assert.Error(t, (&HashPackage{FileSHA256: "x"}).Validate()) // 缺 merkle_root
|
||||
assert.NoError(t, (&HashPackage{FileSHA256: "a", MerkleRoot: "b"}).Validate())
|
||||
}
|
||||
|
||||
func TestPerceptualHash_IdenticalAndDifferent(t *testing.T) {
|
||||
// 全黑与全白图,aHash/dHash 应可区分
|
||||
black := make([][]uint8, 16)
|
||||
white := make([][]uint8, 16)
|
||||
grad := make([][]uint8, 16)
|
||||
for y := 0; y < 16; y++ {
|
||||
black[y] = make([]uint8, 16)
|
||||
white[y] = make([]uint8, 16)
|
||||
grad[y] = make([]uint8, 16)
|
||||
for x := 0; x < 16; x++ {
|
||||
white[y][x] = 255
|
||||
grad[y][x] = uint8(x * 16) // 水平渐变
|
||||
}
|
||||
}
|
||||
imgBlack := newGrayTestImage(black)
|
||||
imgWhite := newGrayTestImage(white)
|
||||
imgGrad := newGrayTestImage(grad)
|
||||
|
||||
// 同一图的哈希稳定
|
||||
assert.Equal(t, AHash(imgGrad), AHash(imgGrad))
|
||||
assert.Equal(t, DHash(imgGrad), DHash(imgGrad))
|
||||
|
||||
// 渐变图的 dHash 应与纯色不同
|
||||
assert.NotEqual(t, DHash(imgGrad), DHash(imgBlack))
|
||||
|
||||
// 汉明距离:渐变 vs 纯白 应 > 0
|
||||
d, err := HammingDistance(DHash(imgGrad), DHash(imgWhite))
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, d, 0)
|
||||
}
|
||||
|
||||
func TestHammingDistance_LengthMismatch(t *testing.T) {
|
||||
_, err := HammingDistance("ffff", "ffffffff")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user