210 lines
5.7 KiB
Go
210 lines
5.7 KiB
Go
package waitlist_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/flowy-live/llink/internal/testhelper"
|
|
"github.com/flowy-live/llink/internal/waitlist"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var dbPool *pgxpool.Pool
|
|
|
|
func TestMain(m *testing.M) {
|
|
dbPool = testhelper.SetupTestDB()
|
|
defer testhelper.TeardownTestDB()
|
|
|
|
ret := m.Run()
|
|
os.Exit(ret)
|
|
}
|
|
|
|
func TestWaitlistService_AddAndGet(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
email := "newuser@example.com"
|
|
metadata := map[string]string{"source": "landing-page", "plan": "pro"}
|
|
|
|
// Add to waitlist
|
|
err := svc.AddToWaitlist(ctx, email, metadata)
|
|
assert.NoError(t, err)
|
|
|
|
// Get entry by email
|
|
entry, err := svc.GetWaitlistEntryByEmail(ctx, email)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, email, entry.Email)
|
|
assert.Equal(t, "landing-page", entry.Metadata["source"])
|
|
assert.Equal(t, "pro", entry.Metadata["plan"])
|
|
assert.NotZero(t, entry.CreatedAt)
|
|
assert.Nil(t, entry.InvitedAt)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, email)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_AddDuplicate(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
email := "duplicate@example.com"
|
|
|
|
err := svc.AddToWaitlist(ctx, email, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Adding the same email again should return AlreadyInWaitlistError
|
|
err = svc.AddToWaitlist(ctx, email, nil)
|
|
assert.ErrorIs(t, err, waitlist.AlreadyInWaitlistError)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, email)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_AddNilMetadata(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
email := "nilmeta@example.com"
|
|
|
|
err := svc.AddToWaitlist(ctx, email, nil)
|
|
assert.NoError(t, err)
|
|
|
|
entry, err := svc.GetWaitlistEntryByEmail(ctx, email)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, entry.Metadata)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, email)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_GetEntryNotFound(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
_, err := svc.GetWaitlistEntryByEmail(ctx, "nonexistent@example.com")
|
|
assert.ErrorIs(t, err, waitlist.EntryNotFoundError)
|
|
}
|
|
|
|
func TestWaitlistService_MarkInvited(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
email := "invite-me@example.com"
|
|
|
|
err := svc.AddToWaitlist(ctx, email, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Mark as invited
|
|
err = svc.MarkWaitlistEntryInvited(ctx, email)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify invited_at is set
|
|
entry, err := svc.GetWaitlistEntryByEmail(ctx, email)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, entry.InvitedAt)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, email)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_MarkInvitedNotFound(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
err := svc.MarkWaitlistEntryInvited(ctx, "ghost@example.com")
|
|
assert.ErrorIs(t, err, waitlist.EntryNotFoundError)
|
|
}
|
|
|
|
func TestWaitlistService_GetWaitlistFilters(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
uninvitedEmail := "uninvited-filter@example.com"
|
|
invitedEmail := "invited-filter@example.com"
|
|
|
|
// Add two entries
|
|
err := svc.AddToWaitlist(ctx, uninvitedEmail, nil)
|
|
assert.NoError(t, err)
|
|
err = svc.AddToWaitlist(ctx, invitedEmail, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Mark one as invited
|
|
err = svc.MarkWaitlistEntryInvited(ctx, invitedEmail)
|
|
assert.NoError(t, err)
|
|
|
|
// Filter: all
|
|
entries, err := svc.GetWaitlist(ctx, waitlist.GetWaitlistFilterAll)
|
|
assert.NoError(t, err)
|
|
emails := extractEmails(entries)
|
|
assert.Contains(t, emails, uninvitedEmail)
|
|
assert.Contains(t, emails, invitedEmail)
|
|
|
|
// Filter: invited only
|
|
entries, err = svc.GetWaitlist(ctx, waitlist.GetWaitlistFilterInvitedOnly)
|
|
assert.NoError(t, err)
|
|
emails = extractEmails(entries)
|
|
assert.Contains(t, emails, invitedEmail)
|
|
assert.NotContains(t, emails, uninvitedEmail)
|
|
|
|
// Filter: uninvited only
|
|
entries, err = svc.GetWaitlist(ctx, waitlist.GetWaitlistFilterUninvitedOnly)
|
|
assert.NoError(t, err)
|
|
emails = extractEmails(entries)
|
|
assert.Contains(t, emails, uninvitedEmail)
|
|
assert.NotContains(t, emails, invitedEmail)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email IN ($1, $2)`, uninvitedEmail, invitedEmail)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_EmailNormalization(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
// Add with uppercase email
|
|
err := svc.AddToWaitlist(ctx, "UPPER@EXAMPLE.COM", nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Should find with lowercase
|
|
entry, err := svc.GetWaitlistEntryByEmail(ctx, "upper@example.com")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "upper@example.com", entry.Email)
|
|
|
|
// Adding with different case should be a duplicate
|
|
err = svc.AddToWaitlist(ctx, "Upper@Example.Com", nil)
|
|
assert.ErrorIs(t, err, waitlist.AlreadyInWaitlistError)
|
|
|
|
// Clean up
|
|
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, "upper@example.com")
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestWaitlistService_InvalidEmail(t *testing.T) {
|
|
ctx := context.Background()
|
|
svc := waitlist.NewService(dbPool)
|
|
|
|
err := svc.AddToWaitlist(ctx, "not-an-email", nil)
|
|
assert.Error(t, err)
|
|
|
|
_, err = svc.GetWaitlistEntryByEmail(ctx, "not-an-email")
|
|
assert.Error(t, err)
|
|
|
|
err = svc.MarkWaitlistEntryInvited(ctx, "not-an-email")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func extractEmails(entries []*waitlist.WaitlistEntry) []string {
|
|
emails := make([]string, len(entries))
|
|
for i, e := range entries {
|
|
emails[i] = e.Email
|
|
}
|
|
return emails
|
|
}
|