feat(orion): add rest api for waitlist
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
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 := "[email protected]"
|
||||
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 := "[email protected]"
|
||||
|
||||
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 := "[email protected]"
|
||||
|
||||
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, "[email protected]")
|
||||
assert.ErrorIs(t, err, waitlist.EntryNotFoundError)
|
||||
}
|
||||
|
||||
func TestWaitlistService_MarkInvited(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := waitlist.NewService(dbPool)
|
||||
|
||||
email := "[email protected]"
|
||||
|
||||
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, "[email protected]")
|
||||
assert.ErrorIs(t, err, waitlist.EntryNotFoundError)
|
||||
}
|
||||
|
||||
func TestWaitlistService_GetWaitlistFilters(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := waitlist.NewService(dbPool)
|
||||
|
||||
uninvitedEmail := "[email protected]"
|
||||
invitedEmail := "[email protected]"
|
||||
|
||||
// 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, "[email protected]", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Should find with lowercase
|
||||
entry, err := svc.GetWaitlistEntryByEmail(ctx, "[email protected]")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "[email protected]", entry.Email)
|
||||
|
||||
// Adding with different case should be a duplicate
|
||||
err = svc.AddToWaitlist(ctx, "[email protected]", nil)
|
||||
assert.ErrorIs(t, err, waitlist.AlreadyInWaitlistError)
|
||||
|
||||
// Clean up
|
||||
_, err = dbPool.Exec(ctx, `DELETE FROM waitlist_entries WHERE email = $1`, "[email protected]")
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user