package credential import ( "context" "crypto/md5" "crypto/sha1" "encoding/hex" "strings" "testing" ) func md5hex(s string) string { sum := md5.Sum([]byte(s)); return hex.EncodeToString(sum[:]) } func sha1hex(s string) string { sum := sha1.Sum([]byte(s)); return hex.EncodeToString(sum[:]) } // TestHashArgon2RoundTrip 验证 argon2 哈希往返(新注册/改密/惰性升级入口)。 func TestHashArgon2RoundTrip(t *testing.T) { enc, err := HashArgon2("X#9f2k") if err != nil { t.Fatalf("HashArgon2 失败: %v", err) } if !strings.HasPrefix(enc, "argon2id$v=19$") { t.Errorf("argon2 编码串应前缀 argon2id$v=19$,得 %q", enc) } if !VerifyArgon2(enc, "X#9f2k") { t.Errorf("正确密码应匹配") } if VerifyArgon2(enc, "wrong") { t.Errorf("错误密码应不匹配") } } // TestCheckMD5Salt 验证教务旧算法 md5(password+salt) 路由 + 惰性升级标记。 func TestCheckMD5Salt(t *testing.T) { const pw, salt = "P@ssw0rd", "aBcDeF" stored := md5hex(pw + salt) res, err := Check(context.Background(), pw, stored, salt, HashVersionMD5Salt) if err != nil { t.Fatalf("Check 意外错误: %v", err) } if !res.Match || !res.NeedsUpgrade { t.Errorf("md5_salt 命中应 Match=true 且 NeedsUpgrade=true,得 %+v", res) } // 错误密码 res2, _ := Check(context.Background(), "nope", stored, salt, HashVersionMD5Salt) if res2.Match { t.Errorf("md5 错误密码应不匹配") } } // TestCheckShASalt 验证教学旧算法 sha1(password+salt) 路由。 func TestCheckShASalt(t *testing.T) { const pw, salt = "teach123", "sAlT12" stored := sha1hex(pw + salt) res, err := Check(context.Background(), pw, stored, salt, HashVersionShASalt) if err != nil { t.Fatalf("Check 意外错误: %v", err) } if !res.Match || !res.NeedsUpgrade { t.Errorf("sha_salt 命中应 Match=true 且 NeedsUpgrade=true,得 %+v", res) } } // TestCheckArgon2 验证目标算法 argon2 路由(命中不触发升级)。 func TestCheckArgon2(t *testing.T) { enc, _ := HashArgon2("argon-pw") res, err := Check(context.Background(), "argon-pw", enc, "", HashVersionArgon2) if err != nil { t.Fatalf("Check 意外错误: %v", err) } if !res.Match || res.NeedsUpgrade { t.Errorf("argon2 命中应 Match=true 且 NeedsUpgrade=false(已是终态),得 %+v", res) } } // TestCheckHexCaseFold 验证旧 hex 大写 storedHash 折叠比较(历史脚本大小写差异,数据保真例外)。 func TestCheckHexCaseFold(t *testing.T) { const pw, salt = "fold123", "zz" storedUpper := strings.ToUpper(md5hex(pw + salt)) res, err := Check(context.Background(), pw, storedUpper, salt, HashVersionMD5Salt) if err != nil { t.Fatalf("Check 意外错误: %v", err) } if !res.Match { t.Errorf("大写 hex storedHash 应折叠命中,得 Match=%v", res.Match) } } // TestCheckUnknownVersion 验证未知 hash_version 拒绝(非密码错误,走告警分支)。 func TestCheckUnknownVersion(t *testing.T) { res, err := Check(context.Background(), "pw", "abc", "", "weird_version") if err == nil { t.Fatalf("未知 version 应报错") } if !strings.Contains(err.Error(), "weird_version") { t.Errorf("错误应含实际 version 值,得 %q", err.Error()) } if res.Match { t.Errorf("未知 version 不应 Match") } _ = ErrUnknownVersion } // TestCheckEmptyHash 验证空密码哈希(账号无凭证,需运维重建)。 func TestCheckEmptyHash(t *testing.T) { _, err := Check(context.Background(), "pw", "", "salt", HashVersionMD5Salt) if err == nil { t.Fatalf("空 storedHash 应报错") } if !strings.Contains(err.Error(), "无密码哈希") { t.Errorf("错误应提示无密码哈希,得 %q", err.Error()) } }