Files
go-trustlog/api/model/hash_test.go
ryan fb182adef4 feat: OpType重构为OpCode (int32) - 完整实现
🎯 核心变更:
- OpType (string) → OpCode (int32)
- 20+ OpCode枚举常量 (基于DOIP/IRP标准)
- 类型安全 + 性能优化

📊 影响范围:
- 核心模型: Operation结构体、CBOR序列化
- 数据库: schema.go + SQL DDL (PostgreSQL/MySQL/SQLite)
- 持久化: repository.go查询、cursor_worker.go
- API接口: Protobuf定义 + gRPC客户端
- 测试代码: 60+ 测试文件更新

 测试结果:
- 通过率: 100% (所有87个测试用例)
- 总体覆盖率: 53.7%
- 核心包覆盖率: logger(100%), highclient(95.3%), model(79.1%)

📝 文档:
- 精简README (1056行→489行,减少54%)
- 完整的OpCode枚举说明
- 三种持久化策略示例
- 数据库表结构和架构图

🔧 技术细节:
- 类型转换: string(OpCode) → int32(OpCode)
- SQL参数: 字符串值 → 整数值
- Protobuf: op_type string → op_code int32
- 测试断言: 字符串比较 → 常量比较

🎉 质量保证:
- 零编译错误
- 100%测试通过
- PostgreSQL/Pulsar集成测试验证
- 分布式并发安全测试通过
2025-12-26 13:47:55 +08:00

547 lines
11 KiB
Go

package model_test
import (
"bytes"
"context"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.yandata.net/iod/iod/go-trustlog/api/model"
)
func TestGetHashTool(t *testing.T) {
t.Parallel()
tests := []struct {
name string
hashType model.HashType
}{
{
name: "SHA256",
hashType: model.SHA256,
},
{
name: "SHA256Simd",
hashType: model.Sha256Simd,
},
{
name: "MD5",
hashType: model.MD5,
},
{
name: "SHA1",
hashType: model.SHA1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tool := model.GetHashTool(tt.hashType)
assert.NotNil(t, tool)
// Verify it works
_, err := tool.HashString("test")
require.NoError(t, err)
// Verify hash type
assert.Equal(t, tt.hashType, tool.GetHashType())
})
}
}
func TestNewHashTool(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
assert.NotNil(t, tool)
// Verify it works
_, err := tool.HashString("test")
require.NoError(t, err)
}
func TestHashTool_HashString(t *testing.T) {
t.Parallel()
tests := []struct {
name string
hashType model.HashType
input string
wantErr bool
}{
{
name: "SHA256",
hashType: model.SHA256,
input: "test",
wantErr: false,
},
{
name: "SHA256Simd",
hashType: model.Sha256Simd,
input: "test",
wantErr: false,
},
{
name: "MD5",
hashType: model.MD5,
input: "test",
wantErr: false,
},
{
name: "SHA1",
hashType: model.SHA1,
input: "test",
wantErr: false,
},
{
name: "SHA512",
hashType: model.SHA512,
input: "test",
wantErr: false,
},
{
name: "empty string",
hashType: model.SHA256,
input: "",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(tt.hashType)
result, err := tool.HashString(tt.input)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.NotEmpty(t, result)
}
})
}
}
func TestHashTool_HashBytes(t *testing.T) {
t.Parallel()
tests := []struct {
name string
hashType model.HashType
input []byte
wantErr bool
}{
{
name: "SHA256",
hashType: model.SHA256,
input: []byte("test"),
wantErr: false,
},
{
name: "SHA256Simd",
hashType: model.Sha256Simd,
input: []byte("test"),
wantErr: false,
},
{
name: "empty bytes",
hashType: model.SHA256,
input: []byte{},
wantErr: false,
},
{
name: "large input",
hashType: model.SHA256,
input: make([]byte, 1000),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(tt.hashType)
result, err := tool.HashBytes(tt.input)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.NotEmpty(t, result)
}
})
}
}
func TestHashTool_Deterministic(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
input := "test string"
result1, err1 := tool.HashString(input)
require.NoError(t, err1)
result2, err2 := tool.HashString(input)
require.NoError(t, err2)
// Same input should produce same hash
assert.Equal(t, result1, result2)
}
func TestHashTool_DifferentInputs(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
result1, err1 := tool.HashString("input1")
require.NoError(t, err1)
result2, err2 := tool.HashString("input2")
require.NoError(t, err2)
// Different inputs should produce different hashes
assert.NotEqual(t, result1, result2)
}
func TestHashTool_StringVsBytes(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
input := "test"
stringHash, err1 := tool.HashString(input)
require.NoError(t, err1)
bytesHash, err2 := tool.HashBytes([]byte(input))
require.NoError(t, err2)
// Same data in different formats should produce same hash
assert.Equal(t, stringHash, bytesHash)
}
func TestHashTool_MultipleTypes(t *testing.T) {
t.Parallel()
input := "test"
hashTypes := []model.HashType{
model.MD5,
model.SHA1,
model.SHA256,
model.SHA512,
model.Sha256Simd,
}
results := make(map[model.HashType]string)
for _, hashType := range hashTypes {
tool := model.NewHashTool(hashType)
result, err := tool.HashString(input)
require.NoError(t, err)
results[hashType] = result
}
// All should produce different hashes (except possibly some edge cases)
// At minimum, verify they all produced valid hashes
for hashType, result := range results {
assert.NotEmpty(t, result, "HashType: %v", hashType)
}
}
func TestHashTool_GetHashTool_Caching(t *testing.T) {
t.Parallel()
hashType := model.SHA256
tool1 := model.GetHashTool(hashType)
tool2 := model.GetHashTool(hashType)
// Should return the same instance (cached)
assert.Equal(t, tool1, tool2)
}
func TestHashTool_HashFile(t *testing.T) {
t.Parallel()
// Create a temporary file
tmpFile := t.TempDir() + "/test.txt"
err := os.WriteFile(tmpFile, []byte("test content"), 0o600)
require.NoError(t, err)
tool := model.NewHashTool(model.SHA256)
ctx := context.Background()
result, err := tool.HashFile(ctx, tmpFile)
require.NoError(t, err)
assert.NotEmpty(t, result)
}
func TestHashTool_HashFile_NotExists(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
ctx := context.Background()
_, err := tool.HashFile(ctx, "/nonexistent/file")
require.Error(t, err)
}
func TestHashTool_HashStream(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
reader := bytes.NewReader([]byte("test content"))
result, err := tool.HashStream(reader)
require.NoError(t, err)
assert.NotEmpty(t, result)
}
func TestHashTool_HashStream_Empty(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
reader := bytes.NewReader([]byte{})
result, err := tool.HashStream(reader)
require.NoError(t, err)
assert.NotEmpty(t, result) // Even empty input produces a hash
}
func TestGetSupportedAlgorithms(t *testing.T) {
t.Parallel()
algorithms := model.GetSupportedAlgorithms()
assert.NotEmpty(t, algorithms)
assert.Contains(t, algorithms, string(model.SHA256))
assert.Contains(t, algorithms, string(model.Sha256Simd))
// Verify case-insensitive check
assert.True(t, model.IsAlgorithmSupported("SHA256"))
assert.True(t, model.IsAlgorithmSupported("sha256"))
assert.True(t, model.IsAlgorithmSupported("sha256-simd"))
}
func TestIsAlgorithmSupported(t *testing.T) {
t.Parallel()
tests := []struct {
name string
algorithm string
expected bool
}{
{
name: "SHA256",
algorithm: "SHA256",
expected: true,
},
{
name: "SHA256 lowercase",
algorithm: "sha256",
expected: true,
},
{
name: "Sha256Simd",
algorithm: "sha256-simd",
expected: true,
},
{
name: "Sha256Simd mixed case",
algorithm: "Sha256-Simd",
expected: true,
},
{
name: "unsupported",
algorithm: "UNSUPPORTED",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
result := model.IsAlgorithmSupported(tt.algorithm)
assert.Equal(t, tt.expected, result)
})
}
}
func TestHashTool_GetHashType(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA512)
assert.Equal(t, model.SHA512, tool.GetHashType())
}
func TestHashTool_AllHashTypes(t *testing.T) {
t.Parallel()
hashTypes := []model.HashType{
model.MD5,
model.SHA1,
model.SHA224,
model.SHA256,
model.SHA384,
model.SHA512,
model.Sha256Simd,
model.BLAKE3,
}
for _, hashType := range hashTypes {
tool := model.NewHashTool(hashType)
result, err := tool.HashString("test")
require.NoError(t, err, "HashType: %v", hashType)
assert.NotEmpty(t, result, "HashType: %v", hashType)
assert.Equal(t, hashType, tool.GetHashType())
}
}
func TestHashTool_CompareHash(t *testing.T) {
t.Parallel()
tool := model.NewHashTool(model.SHA256)
data := "test data"
// Generate hash
hash, err := tool.HashString(data)
require.NoError(t, err)
tests := []struct {
name string
data string
expectedHash string
shouldMatch bool
}{
{
name: "匹配的哈希值",
data: data,
expectedHash: hash,
shouldMatch: true,
},
{
name: "大小写不同但内容相同",
data: data,
expectedHash: strings.ToUpper(hash),
shouldMatch: true,
},
{
name: "不匹配的哈希值",
data: data,
expectedHash: "invalid_hash",
shouldMatch: false,
},
{
name: "不同的数据",
data: "different data",
expectedHash: hash,
shouldMatch: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
match, err := tool.CompareHash(tt.data, tt.expectedHash)
require.NoError(t, err)
assert.Equal(t, tt.shouldMatch, match)
})
}
}
func TestHashTool_CompareFileHash(t *testing.T) {
t.Parallel()
// Create a temporary file
tmpFile := t.TempDir() + "/test.txt"
content := []byte("test file content")
err := os.WriteFile(tmpFile, content, 0o600)
require.NoError(t, err)
tool := model.NewHashTool(model.SHA256)
ctx := context.Background()
// Generate expected hash
expectedHash, err := tool.HashFile(ctx, tmpFile)
require.NoError(t, err)
tests := []struct {
name string
filePath string
expectedHash string
shouldMatch bool
wantErr bool
}{
{
name: "匹配的文件哈希",
filePath: tmpFile,
expectedHash: expectedHash,
shouldMatch: true,
wantErr: false,
},
{
name: "大小写不同但内容相同",
filePath: tmpFile,
expectedHash: strings.ToUpper(expectedHash),
shouldMatch: true,
wantErr: false,
},
{
name: "不匹配的文件哈希",
filePath: tmpFile,
expectedHash: "invalid_hash",
shouldMatch: false,
wantErr: false,
},
{
name: "文件不存在",
filePath: "/nonexistent/file",
expectedHash: expectedHash,
shouldMatch: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
match, err := tool.CompareFileHash(ctx, tt.filePath, tt.expectedHash)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.shouldMatch, match)
}
})
}
}
func TestHashList_GetHashType(t *testing.T) {
t.Parallel()
// Create mock hash data
mockHash := &mockHashData{
key: "test-key",
hash: "test-hash",
hashType: model.SHA256,
}
hashList := model.HashList{mockHash}
assert.Equal(t, model.SHA256, hashList.GetHashType())
}
// mockHashData implements HashData interface for testing.
type mockHashData struct {
key string
hash string
hashType model.HashType
}
func (m *mockHashData) Key() string {
return m.key
}
func (m *mockHashData) Hash() string {
return m.hash
}
func (m *mockHashData) Type() model.HashType {
return m.hashType
}