| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- // tenants_test.go — Pure-Go tests for the validation helpers and
- // the input structs on the tenants store. The DB-backed paths
- // (Create, Update, SetStatus, List) are tested in
- // tenants_pg_test.go under the 'postgres' build tag.
- package authd
- import (
- "errors"
- "testing"
- )
- func TestValidSlug(t *testing.T) {
- cases := []struct {
- in string
- want bool
- }{
- // valid
- {"a", false},
- {"ab", true},
- {"acme", true},
- {"acme-corp", true},
- {"a-1-b-2", true},
- {"x" + repeat("y", 62) + "z", true}, // exactly 64 chars
- // invalid
- {"", false},
- {"A", false}, // uppercase
- {"-acme", false}, // leading dash
- {"acme-", false}, // trailing dash
- {"acme--corp", true}, // double dash is allowed by the regex
- {"acme_corp", false}, // underscore
- {"acme.corp", false}, // dot
- {"acme corp", false}, // space
- {repeat("a", 65), false}, // too long
- }
- for _, c := range cases {
- if got := validSlug(c.in); got != c.want {
- t.Errorf("validSlug(%q) = %v, want %v", c.in, got, c.want)
- }
- }
- }
- func TestLooksLikeEmail(t *testing.T) {
- cases := []struct {
- in string
- want bool
- }{
- {"a@b.c", true},
- {"ops@acme.test", true},
- {"first.last@sub.acme.test", true},
- {"a@b", false}, // no dot in domain
- {"@b.c", false}, // no local part
- {"a@.c", false}, // dot at start of domain
- {"a@b.", false}, // dot at end of domain
- {"", false},
- {" a@b.c ", true}, // trimmed
- {repeat("a", 251) + "@b.c", false}, // 255 chars (RFC max 254)
- }
- for _, c := range cases {
- if got := looksLikeEmail(c.in); got != c.want {
- t.Errorf("looksLikeEmail(%q) = %v, want %v", c.in, got, c.want)
- }
- }
- }
- func TestCreateTenantInput_Validate(t *testing.T) {
- good := CreateTenantInput{
- Slug: "acme", DisplayName: "Acme", ContactEmail: "ops@acme.test",
- RateLimitPerSec: 1000, FCMShared: nil,
- }
- if err := good.Validate(); err != nil {
- t.Errorf("good input rejected: %v", err)
- }
- cases := []struct {
- name string
- in CreateTenantInput
- ok bool
- }{
- {"empty slug", CreateTenantInput{Slug: "", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
- {"bad slug", CreateTenantInput{Slug: "Bad Slug", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
- {"empty display name", CreateTenantInput{Slug: "acme", DisplayName: "", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
- {"bad email", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "not-an-email", RateLimitPerSec: 100}, false},
- {"rl=0", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 0}, false},
- {"rl too high", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 2_000_000}, false},
- {"rl at max", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 1_000_000}, true},
- {"rl at min", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 1}, true},
- }
- for _, c := range cases {
- err := c.in.Validate()
- if c.ok && err != nil {
- t.Errorf("%s: expected ok, got %v", c.name, err)
- }
- if !c.ok && err == nil {
- t.Errorf("%s: expected error, got nil", c.name)
- }
- if !c.ok && err != nil && !errors.Is(err, ErrTenantInvalid) {
- t.Errorf("%s: expected ErrTenantInvalid, got %v", c.name, err)
- }
- }
- }
- func TestUpdateTenantInput_Validate(t *testing.T) {
- // empty patch → ok
- empty := &UpdateTenantInput{}
- if err := empty.Validate(); err != nil {
- t.Errorf("empty patch rejected: %v", err)
- }
- dn := "Acme Corp"
- ce := "ops@acme.test"
- rl := 500
- good := &UpdateTenantInput{DisplayName: &dn, ContactEmail: &ce, RateLimitPerSec: &rl}
- if err := good.Validate(); err != nil {
- t.Errorf("good patch rejected: %v", err)
- }
- badName := ""
- if err := (&UpdateTenantInput{DisplayName: &badName}).Validate(); err == nil {
- t.Error("empty display_name should fail")
- }
- badEmail := "not-an-email"
- if err := (&UpdateTenantInput{ContactEmail: &badEmail}).Validate(); err == nil {
- t.Error("bad contact_email should fail")
- }
- badRl := 0
- if err := (&UpdateTenantInput{RateLimitPerSec: &badRl}).Validate(); err == nil {
- t.Error("rl=0 should fail")
- }
- }
- func repeat(s string, n int) string {
- out := make([]byte, 0, len(s)*n)
- for i := 0; i < n; i++ {
- out = append(out, s...)
- }
- return string(out)
- }
|