Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
package auth
import (
"errors"
"testing"
)
func TestPreferMembershipRoleNeverDemotesAdmin(t *testing.T) {
t.Parallel()
cases := []struct {
name string
existing string
invited string
want string
}{
{name: "admin stays admin on member invite", existing: "admin", invited: "member", want: "admin"},
{name: "admin stays admin on admin invite", existing: "admin", invited: "admin", want: "admin"},
{name: "member promotes to admin", existing: "member", invited: "admin", want: "admin"},
{name: "member stays member", existing: "member", invited: "member", want: "member"},
{name: "unknown invited normalizes to member", existing: "member", invited: "owner", want: "member"},
{name: "empty existing yields invited role", existing: "", invited: "admin", want: "admin"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got := preferMembershipRole(tc.existing, tc.invited)
if got != tc.want {
t.Fatalf("preferMembershipRole(%q, %q)=%q want %q", tc.existing, tc.invited, got, tc.want)
}
})
}
}
func TestHashInviteTokenMatchesAPIKeyHash(t *testing.T) {
t.Parallel()
const raw = "invite-plaintext-secret"
got := HashInviteToken(raw)
if got != HashAPIKey(raw) {
t.Fatalf("HashInviteToken must match HashAPIKey construction")
}
if len(got) != 64 {
t.Fatalf("expected sha256 hex length 64, got %d", len(got))
}
if got == raw {
t.Fatal("invite token must not be stored as plaintext")
}
}
func TestNormalizeInviteRole(t *testing.T) {
t.Parallel()
if normalizeInviteRole("Admin") != "admin" {
t.Fatal("expected admin")
}
if normalizeInviteRole(" MEMBER ") != "member" {
t.Fatal("expected member")
}
if normalizeInviteRole("owner") != "member" {
t.Fatal("unknown roles collapse to member")
}
if NormalizeMembershipRole("Admin") != "admin" {
t.Fatal("NormalizeMembershipRole should accept Admin")
}
}
func TestParseMembershipRole(t *testing.T) {
t.Parallel()
got, err := ParseMembershipRole(" Admin ")
if err != nil || got != "admin" {
t.Fatalf("admin: got %q err=%v", got, err)
}
got, err = ParseMembershipRole("MEMBER")
if err != nil || got != "member" {
t.Fatalf("member: got %q err=%v", got, err)
}
if _, err := ParseMembershipRole("owner"); !errors.Is(err, ErrInvalidMembershipRole) {
t.Fatalf("owner: err=%v want ErrInvalidMembershipRole", err)
}
if _, err := ParseMembershipRole(""); !errors.Is(err, ErrInvalidMembershipRole) {
t.Fatalf("empty: err=%v want ErrInvalidMembershipRole", err)
}
}
func TestIsSyntheticLegacyEmail(t *testing.T) {
t.Parallel()
if !IsSyntheticLegacyEmail("user_abc@legacy.local") {
t.Fatal("expected synthetic")
}
if !IsSyntheticLegacyEmail(" User@Legacy.Local ") {
t.Fatal("expected case-insensitive synthetic")
}
if IsSyntheticLegacyEmail("real@example.com") {
t.Fatal("real email must not be treated as synthetic")
}
if IsSyntheticLegacyEmail("legacy.local@example.com") {
t.Fatal("suffix-only match; local-part must not trigger")
}
if IsSyntheticLegacyEmail("") {
t.Fatal("empty must not be synthetic")
}
}
func TestEmailsEqual(t *testing.T) {
t.Parallel()
if !EmailsEqual("A@Example.COM", " a@example.com ") {
t.Fatal("expected equal after normalize")
}
if EmailsEqual("a@example.com", "b@example.com") {
t.Fatal("expected mismatch")
}
if !EmailsEqual("", "") {
t.Fatal("empty emails should compare equal")
}
}