Files
2026-09-30 15:49:12 +08:00

280 lines
7.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package hexx_test
import (
"bytes"
"encoding/hex"
"strings"
"testing"
"code.yun.ink/pkg/encryptx/hexx"
)
// payloads 覆盖空、单字节、ASCII、UTF-8 与全字节值等输入。
var payloads = [][]byte{
nil,
{},
{0x00},
{0x0f, 0xf0},
[]byte("Hello, World!"),
[]byte("中文与 emoji 🚀"),
bytes.Repeat([]byte{0xde, 0xad, 0xbe, 0xef}, 64),
func() []byte {
b := make([]byte, 256)
for i := range b {
b[i] = byte(i)
}
return b
}(),
}
// TestRoundTrip 校验大小写两种编码都能往返还原。
func TestRoundTrip(t *testing.T) {
for i, payload := range payloads {
lower := hexx.Encode(payload)
upper := hexx.EncodeUpper(payload)
if len(lower) != len(payload)*2 {
t.Errorf("用例 %d: Encode 长度 = %d, 期望 %d", i, len(lower), len(payload)*2)
}
if lower != strings.ToLower(lower) {
t.Errorf("用例 %d: Encode 应输出小写: %q", i, lower)
}
if upper != strings.ToUpper(upper) {
t.Errorf("用例 %d: EncodeUpper 应输出大写: %q", i, upper)
}
if !strings.EqualFold(lower, upper) {
t.Errorf("用例 %d: 大小写编码内容不一致: %q vs %q", i, lower, upper)
}
decodedLower, err := hexx.Decode(lower)
if err != nil {
t.Fatalf("用例 %d: Decode(小写) 失败: %v", i, err)
}
if !bytes.Equal(decodedLower, payload) {
t.Errorf("用例 %d: Decode(小写) 往返不一致", i)
}
decodedUpper, err := hexx.DecodeUpper(upper)
if err != nil {
t.Fatalf("用例 %d: DecodeUpper(大写) 失败: %v", i, err)
}
if !bytes.Equal(decodedUpper, payload) {
t.Errorf("用例 %d: DecodeUpper 往返不一致", i)
}
// 大写编码也必须能被宽松的 Decode 正确解码。
decodedLoose, err := hexx.Decode(upper)
if err != nil {
t.Fatalf("用例 %d: Decode(大写) 失败: %v", i, err)
}
if !bytes.Equal(decodedLoose, payload) {
t.Errorf("用例 %d: Decode(大写) 往返不一致", i)
}
}
}
// TestDecodeMixedCase 校验 Decode 在语义上接受大小写混合输入。
func TestDecodeMixedCase(t *testing.T) {
cases := []struct {
in string
want []byte
}{
{"deadbeef", []byte{0xde, 0xad, 0xbe, 0xef}},
{"DEADBEEF", []byte{0xde, 0xad, 0xbe, 0xef}},
{"DeAdBeEf", []byte{0xde, 0xad, 0xbe, 0xef}},
{"dEaDbEeF", []byte{0xde, 0xad, 0xbe, 0xef}},
{"0a1B2c3D", []byte{0x0a, 0x1b, 0x2c, 0x3d}},
}
for _, c := range cases {
got, err := hexx.Decode(c.in)
if err != nil {
t.Errorf("Decode(%q) 返回错误: %v", c.in, err)
continue
}
if !bytes.Equal(got, c.want) {
t.Errorf("Decode(%q) = %x, 期望 %x", c.in, got, c.want)
}
}
}
// TestEncodeMatchesStdlib 与标准库交叉验证,确保封装未引入行为差异。
func TestEncodeMatchesStdlib(t *testing.T) {
for i, payload := range payloads {
if got, want := hexx.Encode(payload), hex.EncodeToString(payload); got != want {
t.Errorf("用例 %d: Encode = %q, 标准库 = %q", i, got, want)
}
if got, want := hexx.EncodeUpper(payload), strings.ToUpper(hex.EncodeToString(payload)); got != want {
t.Errorf("用例 %d: EncodeUpper = %q, 标准库 = %q", i, got, want)
}
}
}
// TestIsValid 校验 IsValid 对合法与非法输入的判定。
func TestIsValid(t *testing.T) {
valid := []string{
"00", "ff", "FF", "deadbeef", "DEADBEEF", "DeAdBeEf",
"0123456789abcdefABCDEF",
}
for _, s := range valid {
if !hexx.IsValid(s) {
t.Errorf("IsValid(%q) = false, 期望 true", s)
}
}
invalid := []struct {
name string
in string
}{
{"空串", ""},
{"奇数长度", "a"},
{"奇数长度三字符", "abc"},
{"奇数长度长串", strings.Repeat("ab", 20) + "c"},
{"非十六进制字母", "zz"},
{"含 g", "0g"},
{"含 0x 前缀", "0x1f"},
{"含空格", "de ad"},
{"含换行", "dead\n"},
{"含连字符", "de-ad"},
{"非 ASCII 字符", "中文"},
{"emoji", "🚀"},
{"含 tab", "aa\tbb"},
}
for _, c := range invalid {
if hexx.IsValid(c.in) {
t.Errorf("IsValid(%q)(%s)= true, 期望 false", c.in, c.name)
}
}
}
// TestDecodeRejectsInvalid 校验 Decode 对非法输入返回 nil 与 error。
func TestDecodeRejectsInvalid(t *testing.T) {
invalid := []struct {
name string
in string
}{
{"奇数长度", "abc"},
{"奇数长度单字符", "a"},
{"非十六进制字符", "zz"},
{"包含 g", "0g"},
{"0x 前缀", "0x1f"},
{"含空格", "de ad"},
{"含换行", "beef\n"},
{"含连字符", "de-ad"},
{"非 ASCII", "中文"},
{"emoji", "🚀"},
{"长度与内容都非法", "xyz"},
}
for _, c := range invalid {
got, err := hexx.Decode(c.in)
if err == nil {
t.Errorf("Decode(%q)(%s)应当失败,却成功得到 %x", c.in, c.name, got)
}
if got != nil {
t.Errorf("Decode(%q)(%s)失败时应返回 nil,实际 %x", c.in, c.name, got)
}
if err != nil && err.Error() == "" {
t.Errorf("Decode(%q) 返回了空错误信息", c.in)
}
}
}
// TestDecodeUpperStrict 校验 DecodeUpper 在语义上要求输入是大写形式。
func TestDecodeUpperStrict(t *testing.T) {
// 纯大写(含数字)必须成功。
uppers := []struct {
in string
want []byte
}{
{"DEADBEEF", []byte{0xde, 0xad, 0xbe, 0xef}},
{"00FF", []byte{0x00, 0xff}},
{"0A1B2C3D", []byte{0x0a, 0x1b, 0x2c, 0x3d}},
}
for _, c := range uppers {
got, err := hexx.DecodeUpper(c.in)
if err != nil {
t.Errorf("DecodeUpper(%q) 返回错误: %v", c.in, err)
continue
}
if !bytes.Equal(got, c.want) {
t.Errorf("DecodeUpper(%q) = %x, 期望 %x", c.in, got, c.want)
}
}
// 含小写字母的输入必须被拒绝,并返回 nil。
rejected := []string{"deadbeef", "DeadBeef", "DEADbeef", "de"}
for _, in := range rejected {
got, err := hexx.DecodeUpper(in)
if err == nil {
t.Errorf("DecodeUpper(%q) 应当失败(含小写字母),却成功得到 %x", in, got)
}
if got != nil {
t.Errorf("DecodeUpper(%q) 失败时应返回 nil,实际 %x", in, got)
}
}
// 大写但仍非法的输入同样必须失败。
for _, in := range []string{"0X1F", "GG", "ABC", "DE AD"} {
got, err := hexx.DecodeUpper(in)
if err == nil {
t.Errorf("DecodeUpper(%q) 应当失败,却成功得到 %x", in, got)
}
if got != nil {
t.Errorf("DecodeUpper(%q) 失败时应返回 nil,实际 %x", in, got)
}
}
}
// TestDecodeEmpty 校验空串的边界语义。
func TestDecodeEmpty(t *testing.T) {
got, err := hexx.Decode("")
if err != nil {
t.Errorf("Decode(\"\") 应成功: %v", err)
}
if len(got) != 0 {
t.Errorf("Decode(\"\") 得到 %d 字节,期望 0", len(got))
}
// IsValid 明确把空串视为非法。
if hexx.IsValid("") {
t.Error("IsValid(\"\") 应为 false")
}
}
// TestNoPanicOnAnyInput 用一批畸形输入整体验证不 panic。
func TestNoPanicOnAnyInput(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("畸形输入导致 panic: %v", r)
}
}()
inputs := []string{
"", "0", "00", "000", strings.Repeat("f", 511), strings.Repeat("F", 512),
"\x00\x01\x02", "%$#@!", "ff", "🇨🇳", "AB",
}
for _, in := range inputs {
_ = hexx.IsValid(in)
_, _ = hexx.Decode(in)
_, _ = hexx.DecodeUpper(in)
_ = hexx.Encode([]byte(in))
_ = hexx.EncodeUpper([]byte(in))
}
}
// TestEncodeDecodeNil 校验 nil 输入的处理。
func TestEncodeDecodeNil(t *testing.T) {
if got := hexx.Encode(nil); got != "" {
t.Errorf("Encode(nil) = %q, 期望空串", got)
}
if got := hexx.EncodeUpper(nil); got != "" {
t.Errorf("EncodeUpper(nil) = %q, 期望空串", got)
}
got, err := hexx.Decode(hexx.Encode(nil))
if err != nil {
t.Errorf("Decode(Encode(nil)) 失败: %v", err)
}
if len(got) != 0 {
t.Errorf("Decode(Encode(nil)) 得到 %d 字节,期望 0", len(got))
}
}