Files
iceking2nd cb4d88ebdd test: OTP 算法 + 加密 + Config + QR 导出 单元测试
- authenticator: HOTP RFC 4226 向量、Base32 往返、SecretData 往返
- crypto: WAGO1 加密解密往返、错误密码、空密码、无效载荷
- config: YAML 明文/加密读写、密码错误、.bak 轮换
- qr: EntryToOtpAuth 各 vendor、URI 往返、默认值省略
2026-06-12 10:50:25 +08:00

113 lines
2.8 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
)
func TestSaveLoadPlaintext(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.yaml")
cfg := &Config{
Version: 1,
Language: "en",
Theme: "dark",
Entries: []Entry{
{Name: "Google", Vendor: "google", SecretRaw: "AABB\t6\tSHA1\t30"},
{Name: "HOTP", Vendor: "hotp", SecretRaw: "CCDD\t6\tSHA1\t30|5"},
},
}
if err := SaveYAML(cfg, path, nil); err != nil {
t.Fatalf("SaveYAML: %v", err)
}
loaded, err := LoadYAML(path, nil)
if err != nil {
t.Fatalf("LoadYAML: %v", err)
}
if loaded.Version != 1 {
t.Errorf("Version = %d, want 1", loaded.Version)
}
if len(loaded.Entries) != 2 {
t.Fatalf("entries = %d, want 2", len(loaded.Entries))
}
if loaded.Entries[0].Name != "Google" {
t.Errorf("Entry[0].Name = %q, want %q", loaded.Entries[0].Name, "Google")
}
if loaded.Entries[1].Vendor != "hotp" {
t.Errorf("Entry[1].Vendor = %q, want %q", loaded.Entries[1].Vendor, "hotp")
}
}
func TestSaveLoadEncrypted(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.yaml")
pw := []byte("s3cr3t")
cfg := &Config{
Version: 1,
Encrypted: true,
Entries: []Entry{
{Name: "Steam", Vendor: "steam", SecretRaw: "EEFF\t5\tSHA1\t30|SERIAL|DEVICE"},
},
}
if err := SaveYAML(cfg, path, pw); err != nil {
t.Fatalf("SaveYAML: %v", err)
}
// Load without password → ErrPasswordRequired
if _, err := LoadYAML(path, nil); err != ErrPasswordRequired {
t.Errorf("expected ErrPasswordRequired, got %v", err)
}
// Load with wrong password → ErrPasswordWrong
if _, err := LoadYAML(path, []byte("wrong")); err != ErrPasswordWrong {
t.Errorf("expected ErrPasswordWrong, got %v", err)
}
// Load with correct password
loaded, err := LoadYAML(path, pw)
if err != nil {
t.Fatalf("LoadYAML: %v", err)
}
if len(loaded.Entries) != 1 {
t.Fatalf("entries = %d, want 1", len(loaded.Entries))
}
if loaded.Entries[0].Name != "Steam" {
t.Errorf("Entry[0].Name = %q, want %q", loaded.Entries[0].Name, "Steam")
}
}
func TestBackupRotation(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.yaml")
// First save: no .bak exists yet
cfg1 := &Config{Version: 1, Entries: []Entry{{Name: "A"}}}
if err := SaveYAML(cfg1, path, nil); err != nil {
t.Fatalf("SaveYAML #1: %v", err)
}
// Second save: .bak should be created
cfg2 := &Config{Version: 1, Entries: []Entry{{Name: "B"}}}
if err := SaveYAML(cfg2, path, nil); err != nil {
t.Fatalf("SaveYAML #2: %v", err)
}
bak := path + ".bak"
if _, err := os.Stat(bak); os.IsNotExist(err) {
t.Error("expected .bak file to exist after second save")
}
}
func TestLoadNonexistent(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "nonexistent.yaml")
_, err := LoadYAML(path, nil)
if err == nil {
t.Error("expected error for nonexistent file")
}
}