Files
LokiStack/backend/pkg/crypto/crypto_test.go
T
Logan.LiandClaude 327e282356 refactor: 前后端目录结构模块化 + v2 优化移植与缺陷修复
前端:
- src/srv/api → src/api,按域分文件夹(auth/cluster/host/storage/deployment/collector)
- 新增 http.ts 引擎(http.get/post/put/delete),域模块改用语义化调用
- index.html 增加 favicon(匹配应用 logo 的 emerald + Activity 脉冲)

后端分层增强(保留包名):
- 新增 repository/:从 models 拆出 JsonDB 持久化层(InitDB/GetDB/Shutdown/RequestSave)
- 新增 dto/:从 controllers 抽出各域请求契约(带 binding 校验)
- 新增 pkg/crypto、pkg/jwt:从 utils 拆出安全原语(外部 jwt 别名 jwtv5 避免冲突)
- models 只留领域实体 + sanitizer;utils 只留 gin/应用相关(response/errors)

v2 优化移植与缺陷修复:
- 移植密钥分离/SSH TOFU/并发写安全/Loki 生产级配置/Docker 加固
- 修复 deployment.go 并发丢更新、Helm values 重复键、EncryptField 哨兵、loki-gateway 端点等审计缺陷
- 实现真正的优雅关停(main.go http.Server + signal + repository.Shutdown)
- 修复登录契约:后端返回 accessToken,前端 auth.ts/Login.tsx 对齐
- 补 crypto/jwt/repository 单元测试(含并发写回归)

验证: go build/vet/test 全绿;npm run build 通过;登录→鉴权→优雅关停冒烟通过

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-30 10:46:26 +08:00

79 lines
2.4 KiB
Go

package crypto
import (
"strings"
"testing"
)
// TestEncryptDecryptRoundTrip 覆盖 AES-GCM 加解密往返(含空串与 Unicode)。
func TestEncryptDecryptRoundTrip(t *testing.T) {
cases := []string{"", "hello", "a-much-longer-secret-with-unicode-密码-🔑"}
for _, plain := range cases {
enc, err := Encrypt(plain)
if err != nil {
t.Fatalf("Encrypt(%q) error: %v", plain, err)
}
dec, err := Decrypt(enc)
if err != nil {
t.Fatalf("Decrypt(%q) error: %v", plain, err)
}
if dec != plain {
t.Fatalf("round-trip mismatch: got %q want %q", dec, plain)
}
}
}
// TestDecryptTamperedFails 验证 GCM 认证:篡改密文必须解密失败。
func TestDecryptTamperedFails(t *testing.T) {
enc, err := Encrypt("secret-value")
if err != nil {
t.Fatal(err)
}
if len(enc) < 4 {
t.Fatal("ciphertext too short")
}
// 翻转末尾两个 base64 字符,破坏 GCM 认证标签
tampered := enc[:len(enc)-2] + "AA"
if _, err := Decrypt(tampered); err == nil {
t.Fatal("expected Decrypt to fail on tampered ciphertext, got nil")
}
}
// TestEncryptFieldContract 是 M2 的回归保护:
// EncryptField 绝不能返回 "ENC_ERR:" 哨兵(否则 DecryptField 会把它当明文凭证用),
// 只能返回 ""(空输入)或 "ENC:" 前缀;且与 DecryptField 可往返。
func TestEncryptFieldContract(t *testing.T) {
enc, err := EncryptField("super-secret")
if err != nil {
t.Fatalf("EncryptField error: %v", err)
}
if !strings.HasPrefix(enc, "ENC:") {
t.Fatalf("EncryptField must return ENC:-prefixed value, got %q", enc)
}
if enc == "ENC_ERR:" {
t.Fatal("EncryptField returned legacy sentinel ENC_ERR: (M2 regression)")
}
if got := DecryptField(enc); got != "super-secret" {
t.Fatalf("DecryptField round-trip: got %q want super-secret", got)
}
if !IsEncryptedField(enc) {
t.Fatal("IsEncryptedField should be true for ENC: value")
}
if IsEncryptedField("plain-value") {
t.Fatal("IsEncryptedField should be false for plain value")
}
// 空输入约定
if enc, err := EncryptField(""); enc != "" || err != nil {
t.Fatalf("EncryptField(\"\") = (%q,%v), want (\"\",nil)", enc, err)
}
}
// TestEncryptFieldRandomNonce 同一明文两次加密应产生不同密文(随机 nonce)。
func TestEncryptFieldRandomNonce(t *testing.T) {
a, _ := EncryptField("same-secret")
b, _ := EncryptField("same-secret")
if a == b {
t.Fatal("expected different ciphertexts due to random nonce")
}
}