143 lines
3.3 KiB
Go
143 lines
3.3 KiB
Go
package billing
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"os"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestPlanIDByNameRequiresName(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
|
svc := &Service{}
|
||
|
|
_, err := svc.PlanIDByName(context.Background(), " ")
|
||
|
|
if !errors.Is(err, ErrPlanNameRequired) {
|
||
|
|
t.Fatalf("got %v, want ErrPlanNameRequired", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAssignPlanIfMissingSkipsExisting(t *testing.T) {
|
||
|
|
dsn := os.Getenv("DATABASE_URL")
|
||
|
|
if dsn == "" {
|
||
|
|
t.Skip("DATABASE_URL not set")
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
pg, err := pgxpool.New(ctx, dsn)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer pg.Close()
|
||
|
|
|
||
|
|
svc := &Service{Pool: pg}
|
||
|
|
if err := svc.EnsureDefaultPlans(ctx); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
freeID, err := svc.PlanIDByName(ctx, "Free")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
starterID, err := svc.PlanIDByName(ctx, "Starter")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
companyID := uuid.New()
|
||
|
|
_, err = pg.Exec(ctx, `INSERT INTO companies (id, name) VALUES ($1, $2)`, companyID, "missing-plans-"+companyID.String()[:8])
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
t.Cleanup(func() {
|
||
|
|
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
|
defer cleanupCancel()
|
||
|
|
_, _ = pg.Exec(cleanupCtx, `DELETE FROM companies WHERE id = $1`, companyID)
|
||
|
|
})
|
||
|
|
|
||
|
|
assigned, err := svc.AssignPlanIfMissing(ctx, companyID, freeID)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if !assigned {
|
||
|
|
t.Fatal("expected first assign to succeed")
|
||
|
|
}
|
||
|
|
|
||
|
|
assigned, err = svc.AssignPlanIfMissing(ctx, companyID, starterID)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if assigned {
|
||
|
|
t.Fatal("must not overwrite an existing active plan")
|
||
|
|
}
|
||
|
|
|
||
|
|
has, err := svc.HasActivePlan(ctx, companyID)
|
||
|
|
if err != nil || !has {
|
||
|
|
t.Fatalf("has active plan: has=%v err=%v", has, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
var planID int64
|
||
|
|
err = pg.QueryRow(ctx, `SELECT plan_id FROM company_plans WHERE company_id = $1 AND is_active = true`, companyID).Scan(&planID)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if planID != freeID {
|
||
|
|
t.Fatalf("active plan_id=%d, want Free id=%d", planID, freeID)
|
||
|
|
}
|
||
|
|
|
||
|
|
missing, err := svc.ListCompaniesWithoutActivePlan(ctx, 500, 0)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
for _, c := range missing {
|
||
|
|
if c.ID == companyID {
|
||
|
|
t.Fatal("company with active plan must not appear in without-plan list")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestListCompaniesWithoutActivePlanIncludesBareCompany(t *testing.T) {
|
||
|
|
dsn := os.Getenv("DATABASE_URL")
|
||
|
|
if dsn == "" {
|
||
|
|
t.Skip("DATABASE_URL not set")
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
pg, err := pgxpool.New(ctx, dsn)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer pg.Close()
|
||
|
|
|
||
|
|
svc := &Service{Pool: pg}
|
||
|
|
companyID := uuid.New()
|
||
|
|
_, err = pg.Exec(ctx, `INSERT INTO companies (id, name) VALUES ($1, $2)`, companyID, "no-plan-"+companyID.String()[:8])
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
t.Cleanup(func() {
|
||
|
|
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
|
defer cleanupCancel()
|
||
|
|
_, _ = pg.Exec(cleanupCtx, `DELETE FROM companies WHERE id = $1`, companyID)
|
||
|
|
})
|
||
|
|
|
||
|
|
found := false
|
||
|
|
rows, err := svc.ListCompaniesWithoutActivePlan(ctx, 500, 0)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
for _, c := range rows {
|
||
|
|
if c.ID == companyID {
|
||
|
|
found = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !found {
|
||
|
|
t.Fatal("bare company must appear in without-plan list")
|
||
|
|
}
|
||
|
|
}
|