127 lines
3.3 KiB
Go
127 lines
3.3 KiB
Go
package user
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func writeProfile(t *testing.T, content string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "owner.yaml")
|
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
|
t.Fatalf("write owner profile: %v", err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestLoadOwnerProfile_Valid(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := writeProfile(t, `id: owner
|
|
displayName: 叶酱
|
|
defaultPersona: cyrene
|
|
accounts:
|
|
- platform: qq-obv11
|
|
accountId: "123456789"
|
|
- platform: weixin-ilink
|
|
accountId: wx_owner_abc
|
|
notes: "主人"
|
|
`)
|
|
p, err := LoadOwnerProfile(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadOwnerProfile error: %v", err)
|
|
}
|
|
if p.ID != "owner" || p.DisplayName != "叶酱" || p.DefaultPersona != "cyrene" {
|
|
t.Fatalf("unexpected profile: %+v", p)
|
|
}
|
|
if !p.IsOwner("qq-obv11", "123456789") {
|
|
t.Error("expected (qq-obv11,123456789) to be owner")
|
|
}
|
|
if !p.IsOwner("weixin-ilink", "wx_owner_abc") {
|
|
t.Error("expected (weixin-ilink,wx_owner_abc) to be owner")
|
|
}
|
|
if p.IsOwner("qq-obv11", "999") {
|
|
t.Error("non-owner account should not match")
|
|
}
|
|
if p.IsOwner("telegram", "123456789") {
|
|
t.Error("wrong platform must not match")
|
|
}
|
|
}
|
|
|
|
func TestLoadOwnerProfile_Validation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
substr string
|
|
}{
|
|
{name: "missing id", content: "displayName: 叶酱\naccounts:\n - platform: qq\n accountId: '1'\n", substr: "id"},
|
|
{name: "missing displayName", content: "id: owner\naccounts:\n - platform: qq\n accountId: '1'\n", substr: "displayName"},
|
|
{name: "account missing platform", content: "id: owner\ndisplayName: 叶酱\naccounts:\n - accountId: '1'\n", substr: "platform"},
|
|
{name: "account missing id", content: "id: owner\ndisplayName: 叶酱\naccounts:\n - platform: qq\n", substr: "accountId"},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
path := writeProfile(t, tt.content)
|
|
_, err := LoadOwnerProfile(path)
|
|
if err == nil {
|
|
t.Fatal("expected validation error")
|
|
}
|
|
if !errors.Is(err, ErrRequiredField) {
|
|
t.Fatalf("expected ErrRequiredField, got: %v", err)
|
|
}
|
|
if len(tt.substr) > 0 && !contains(err.Error(), tt.substr) {
|
|
t.Fatalf("error %q should mention %q", err.Error(), tt.substr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadOwnerProfile_EmptyWhitelistDefaultsDeny(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := writeProfile(t, "id: owner\ndisplayName: 叶酱\naccounts: []\n")
|
|
p, err := LoadOwnerProfile(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadOwnerProfile error: %v", err)
|
|
}
|
|
if p.IsOwner("qq-obv11", "123") {
|
|
t.Error("empty whitelist must default to deny")
|
|
}
|
|
if len(p.AccountIdentifiers()) != 0 {
|
|
t.Errorf("expected no account identifiers, got %v", p.AccountIdentifiers())
|
|
}
|
|
}
|
|
|
|
func TestOwnerProfile_AccountIdentifiers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
p := &OwnerProfile{
|
|
Accounts: []OwnerAccount{
|
|
{Platform: "qq", AccountID: "111"},
|
|
{Platform: "wx", AccountID: "222"},
|
|
{Platform: "qq", AccountID: "111"}, // duplicate
|
|
},
|
|
}
|
|
ids := p.AccountIdentifiers()
|
|
if len(ids) != 2 {
|
|
t.Fatalf("expected 2 unique identifiers, got %d: %v", len(ids), ids)
|
|
}
|
|
}
|
|
|
|
func contains(s, sub string) bool {
|
|
return len(sub) > 0 && len(s) >= len(sub) && (func() bool {
|
|
for i := 0; i+len(sub) <= len(s); i++ {
|
|
if s[i:i+len(sub)] == sub {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})()
|
|
}
|