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

196 lines
5.1 KiB
Go

package drivers
import (
"context"
"errors"
"testing"
"time"
"github.com/jackc/pgx/v5/pgconn"
)
type memoryStore struct {
drivers map[string]Driver
}
func newMemoryStore() *memoryStore {
return &memoryStore{drivers: make(map[string]Driver)}
}
func (m *memoryStore) Exec(ctx context.Context, sql string) (int64, error) {
return 0, nil
}
func (m *memoryStore) Create(ctx context.Context, d Driver) error {
for _, existing := range m.drivers {
if existing.Nickname == d.Nickname {
return &pgconn.PgError{Code: "23505", Message: "duplicate key value violates unique constraint"}
}
}
m.drivers[d.ID] = d
return nil
}
func (m *memoryStore) Get(ctx context.Context, id string) (Driver, error) {
d, ok := m.drivers[id]
if !ok {
return Driver{}, ErrNotFound
}
return d, nil
}
func (m *memoryStore) GetByNickname(ctx context.Context, nick string) (Driver, error) {
for _, d := range m.drivers {
if d.Nickname == nick {
return d, nil
}
}
return Driver{}, ErrNotFound
}
func (m *memoryStore) List(ctx context.Context, limit, offset int, clanID string) ([]Driver, error) {
out := make([]Driver, 0)
for _, d := range m.drivers {
if clanID == "" || d.ClanID == clanID {
out = append(out, d)
}
}
return out, nil
}
func (m *memoryStore) Delete(ctx context.Context, id string) error {
if _, ok := m.drivers[id]; !ok {
return ErrNotFound
}
delete(m.drivers, id)
return nil
}
func (m *memoryStore) Update(ctx context.Context, d Driver) error {
if _, ok := m.drivers[d.ID]; !ok {
return ErrNotFound
}
for id, existing := range m.drivers {
if id != d.ID && existing.Nickname == d.Nickname {
return &pgconn.PgError{Code: "23505", Message: "duplicate key value violates unique constraint"}
}
}
m.drivers[d.ID] = d
return nil
}
func (m *memoryStore) Count(ctx context.Context) (int, error) {
return len(m.drivers), nil
}
func TestDriversService(t *testing.T) {
store := newMemoryStore()
svc := NewService(store)
svc.now = func() time.Time {
return time.Unix(1700000000, 0)
}
ctx := context.Background()
// 1. Create a driver
d1, err := svc.Create(ctx, CreateInput{
ID: "driver-1",
Nickname: "HAM",
Name: "Lewis Hamilton",
AvatarURL: "https://example.com/ham.png",
ClanID: "clan-1",
})
if err != nil {
t.Fatalf("Create failed: %v", err)
}
if d1.ID != "driver-1" || d1.Nickname != "HAM" || d1.Name != "Lewis Hamilton" || d1.ClanID != "clan-1" {
t.Errorf("Unexpected created driver: %+v", d1)
}
// 2. Validate Nickname must be 3 uppercase ASCII letters
_, err = svc.Create(ctx, CreateInput{Nickname: "HAMILTON", Name: "toolong"})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for tag 'HAMILTON', got: %v", err)
}
// 3. Validate empty Name is rejected
_, err = svc.Create(ctx, CreateInput{Nickname: "BOT", Name: ""})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for empty name, got: %v", err)
}
// 4. Duplicate nickname check
_, err = svc.Create(ctx, CreateInput{
ID: "driver-2",
Nickname: "HAM",
Name: "Another Lewis",
})
if !errors.Is(err, ErrAlreadyExists) {
t.Errorf("Expected ErrAlreadyExists for duplicate nickname, got: %v", err)
}
// 5. Get driver
got, err := svc.Get(ctx, "driver-1")
if err != nil {
t.Fatalf("Get failed: %v", err)
}
if got.Nickname != "HAM" {
t.Errorf("Expected HAM, got %s", got.Nickname)
}
// 6. Get by nickname
gotByNick, err := svc.GetByNickname(ctx, "ham") // should normalize to HAM
if err != nil {
t.Fatalf("GetByNickname failed: %v", err)
}
if gotByNick.ID != "driver-1" {
t.Errorf("Expected driver-1, got %s", gotByNick.ID)
}
// 7. Update driver (including nickname)
clanID := "clan-2"
updated, err := svc.Update(ctx, "driver-1", UpdateInput{
Nickname: "VET",
Name: "Lewis Hamilton MBE",
AvatarURL: "https://example.com/ham-mbe.png",
ClanID: &clanID,
})
if err != nil {
t.Fatalf("Update failed: %v", err)
}
if updated.Nickname != "VET" || updated.Name != "Lewis Hamilton MBE" || updated.AvatarURL != "https://example.com/ham-mbe.png" || updated.ClanID != "clan-2" {
t.Errorf("Unexpected updated driver fields: %+v", updated)
}
// 7a. Update with invalid nickname
_, err = svc.Update(ctx, "driver-1", UpdateInput{Nickname: "TOOLONG"})
if !errors.Is(err, ErrInvalidInput) {
t.Errorf("Expected ErrInvalidInput for invalid nickname update, got: %v", err)
}
// 7b. Create driver-2 to test duplicate nickname conflict
_, err = svc.Create(ctx, CreateInput{
ID: "driver-2",
Nickname: "HAM",
Name: "Lewis",
})
if err != nil {
t.Fatalf("Create driver-2 failed: %v", err)
}
// 7c. Update driver-2 to nickname "VET" (should fail, already taken by driver-1)
_, err = svc.Update(ctx, "driver-2", UpdateInput{Nickname: "VET"})
if !errors.Is(err, ErrAlreadyExists) {
t.Errorf("Expected ErrAlreadyExists for duplicate nickname update, got: %v", err)
}
// 8. Delete driver
err = svc.Delete(ctx, "driver-1")
if err != nil {
t.Fatalf("Delete failed: %v", err)
}
_, err = svc.Get(ctx, "driver-1")
if !errors.Is(err, ErrNotFound) {
t.Errorf("Expected ErrNotFound after deletion, got: %v", err)
}
}