package signutil import ( "strconv" "testing" "time" ) // TestHMACSHA256HexKnownVector 用公开测试向量锁定算法,避免以后「优化」时改坏。 func TestHMACSHA256HexKnownVector(t *testing.T) { got := HMACSHA256Hex("key", "The quick brown fox jumps over the lazy dog") const want = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8" if got != want { t.Fatalf("HMAC-SHA256 向量不符:期望 %s,实际 %s", want, got) } } // TestHMACSHA256HexBytesMatchesStringVersion 分段写入必须与整体写入等价。 func TestHMACSHA256HexBytesMatchesStringVersion(t *testing.T) { whole := HMACSHA256Hex("secret", "prefix-body-suffix") parts := HMACSHA256HexBytes("secret", []byte("prefix-"), []byte("body"), []byte("-suffix")) if whole != parts { t.Fatalf("分段写入结果不一致:%s vs %s", whole, parts) } } func TestHMACSHA256HexBytesNoParts(t *testing.T) { // 空内容等价于对空串做 HMAC。 if got, want := HMACSHA256HexBytes("secret"), HMACSHA256Hex("secret", ""); got != want { t.Fatalf("无分段应等价于空内容:%s vs %s", got, want) } } func TestSHA256Hex(t *testing.T) { if got := SHA256Hex(nil); got != EmptyBodySHA256 { t.Fatalf("空内容应返回空串摘要常量,实际 %s", got) } if got := SHA256Hex([]byte{}); got != EmptyBodySHA256 { t.Fatalf("空切片应返回空串摘要常量,实际 %s", got) } const wantAbc = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" if got := SHA256Hex([]byte("abc")); got != wantAbc { t.Fatalf("SHA256(abc) 不符:期望 %s,实际 %s", wantAbc, got) } } func TestTimestampWithinSkew(t *testing.T) { now := time.Now().Unix() cases := []struct { name string timestamp string skew time.Duration want bool }{ {"当前时间在窗口内", itoa(now), 300 * time.Second, true}, {"边界上刚好等于 skew", itoa(now - 300), 300 * time.Second, true}, {"超出 skew 一秒", itoa(now - 301), 300 * time.Second, false}, {"未来时间同样按绝对值判断", itoa(now + 301), 300 * time.Second, false}, {"未来时间在窗口内", itoa(now + 60), 300 * time.Second, true}, {"非法时间戳", "not-a-number", 300 * time.Second, false}, {"空时间戳", "", 300 * time.Second, false}, {"不裁剪空白(保持既有行为)", " " + itoa(now) + " ", 300 * time.Second, false}, {"skew 为 0 时只有同一秒通过", itoa(now), 0, true}, {"skew 为 0 时上一秒不通过", itoa(now - 1), 0, false}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if got := TimestampWithinSkew(tc.timestamp, tc.skew); got != tc.want { t.Fatalf("期望 %v,实际 %v", tc.want, got) } }) } } func TestTimestampWithinSkewSecondsMatchesDuration(t *testing.T) { now := itoa(time.Now().Unix()) if TimestampWithinSkewSeconds(now, 300) != TimestampWithinSkew(now, 300*time.Second) { t.Fatal("秒版本与 Duration 版本应一致") } } func TestEqualSignatures(t *testing.T) { if !EqualSignatures("abc123", "abc123") { t.Fatal("相同签名应通过") } if EqualSignatures("abc123", "abc124") { t.Fatal("不同签名不应通过") } if EqualSignatures("abc", "abcd") { t.Fatal("长度不同的签名不应通过") } // 该函数不做大小写归一化,这是刻意为之(乐玩签名带 v1= 前缀,需原样比较)。 if EqualSignatures("ABC123", "abc123") { t.Fatal("未归一化的版本应区分大小写") } } func TestEqualHexSignatures(t *testing.T) { if !EqualHexSignatures("ABC123DEF", "abc123def") { t.Fatal("十六进制签名应大小写不敏感") } if EqualHexSignatures("abc123", "abc124") { t.Fatal("不同签名不应通过") } } func itoa(v int64) string { return strconv.FormatInt(v, 10) }