Files
x0gp/server/internal/clans/service_test.go
T

167 lines
3.9 KiB
Go

package clans
import (
"context"
"errors"
"testing"
"time"
"github.com/jackc/pgx/v5/pgconn"
)
type memoryStore struct {
clans map[string]Clan
}
func newMemoryStore() *memoryStore {
return &memoryStore{clans: make(map[string]Clan)}
}
func (m *memoryStore) Exec(ctx context.Context, sql string) (int64, error) {
return 0, nil
}
func (m *memoryStore) Create(ctx context.Context, c Clan) error {
for _, existing := range m.clans {
if existing.Tag == c.Tag {
return &pgconn.PgError{Code: "23505", Message: "duplicate key value violates unique constraint"}
}
}
m.clans[c.ID] = c
return nil
}
func (m *memoryStore) Get(ctx context.Context, id string) (Clan, error) {
c, ok := m.clans[id]
if !ok {
return Clan{}, ErrNotFound
}
return c, nil
}
func (m *memoryStore) GetByTag(ctx context.Context, tag string) (Clan, error) {
for _, c := range m.clans {
if c.Tag == tag {
return c, nil
}
}
return Clan{}, ErrNotFound
}
func (m *memoryStore) List(ctx context.Context, limit, offset int) ([]Clan, error) {
out := make([]Clan, 0)
for _, c := range m.clans {
out = append(out, c)
}
return out, nil
}
func (m *memoryStore) Delete(ctx context.Context, id string) error {
if _, ok := m.clans[id]; !ok {
return ErrNotFound
}
delete(m.clans, id)
return nil
}
func (m *memoryStore) Update(ctx context.Context, c Clan) error {
if _, ok := m.clans[c.ID]; !ok {
return ErrNotFound
}
m.clans[c.ID] = c
return nil
}
func (m *memoryStore) Count(ctx context.Context) (int, error) {
return len(m.clans), nil
}
func TestClansService(t *testing.T) {
store := newMemoryStore()
svc := NewService(store)
svc.now = func() time.Time {
return time.Unix(1700000000, 0)
}
ctx := context.Background()
// 1. Create a clan with valid inputs
c1, err := svc.Create(ctx, CreateInput{
ID: "clan-1",
Tag: "SPD",
Name: "Speed Demons",
AvatarURL: "https://example.com/spd.png",
})
if err != nil {
t.Fatalf("Create failed: %v", err)
}
if c1.ID != "clan-1" || c1.Tag != "SPD" || c1.Name != "Speed Demons" {
t.Errorf("Unexpected created clan: %+v", c1)
}
// 2. Validate Tag must be 3 uppercase ASCII letters
_, err = svc.Create(ctx, CreateInput{Tag: "SPEED", Name: "toolongtag"})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for tag 'SPEED', got: %v", err)
}
_, err = svc.Create(ctx, CreateInput{Tag: "S1D", Name: "withnumbertag"})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for tag 'S1D', got: %v", err)
}
// 3. Validate empty Name is rejected
_, err = svc.Create(ctx, CreateInput{Tag: "AAA", Name: ""})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for empty name, got: %v", err)
}
// 4. Duplicate tag check
_, err = svc.Create(ctx, CreateInput{
ID: "clan-2",
Tag: "SPD",
Name: "Duplicate SPD Tag",
})
if !errors.Is(err, ErrAlreadyExists) {
t.Errorf("Expected ErrAlreadyExists for duplicate tag, got: %v", err)
}
// 5. Get clan
got, err := svc.Get(ctx, "clan-1")
if err != nil {
t.Fatalf("Get failed: %v", err)
}
if got.Tag != "SPD" {
t.Errorf("Expected SPD, got %s", got.Tag)
}
// 6. Get by tag
gotByTag, err := svc.GetByTag(ctx, "spd") // should normalize to SPD
if err != nil {
t.Fatalf("GetByTag failed: %v", err)
}
if gotByTag.ID != "clan-1" {
t.Errorf("Expected clan-1, got %s", gotByTag.ID)
}
// 7. Update clan
updated, err := svc.Update(ctx, "clan-1", UpdateInput{
Name: "Super Speed Demons",
AvatarURL: "https://example.com/super-spd.png",
})
if err != nil {
t.Fatalf("Update failed: %v", err)
}
if updated.Name != "Super Speed Demons" || updated.AvatarURL != "https://example.com/super-spd.png" {
t.Errorf("Unexpected updated clan fields: %+v", updated)
}
// 8. Delete clan
err = svc.Delete(ctx, "clan-1")
if err != nil {
t.Fatalf("Delete failed: %v", err)
}
_, err = svc.Get(ctx, "clan-1")
if !errors.Is(err, ErrNotFound) {
t.Errorf("Expected ErrNotFound after deletion, got: %v", err)
}
}