Skip to content

Commit

Permalink
feat: Add new users-developer plan (#494)
Browse files Browse the repository at this point in the history
* Add new developer plan

* fix enum issue

* Resolve tests

* create a new default const for plans

* fix lint issues

* remove developer new enum key

* remove deprecated
  • Loading branch information
RulaKhaled authored Feb 4, 2025
1 parent c8dca74 commit 7ba099f
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 4.2.16 on 2025-01-30 13:24

from django.db import migrations, models


class Migration(migrations.Migration):
"""
BEGIN;
--
-- Alter field plan on account
--
-- (no-op)
--
-- Raw SQL operation
--
ALTER TYPE plans ADD VALUE IF NOT EXISTS 'users-developer';
--
-- Alter field plan on owner
--
-- (no-op)
COMMIT;
"""

dependencies = [
("codecov_auth", "0064_plan_stripe_id"),
]

operations = [
migrations.AlterField(
model_name="account",
name="plan",
field=models.CharField(
choices=[
("users-basic", "BASIC_PLAN_NAME"),
("users-trial", "TRIAL_PLAN_NAME"),
("users-pr-inappm", "CODECOV_PRO_MONTHLY"),
("users-pr-inappy", "CODECOV_PRO_YEARLY"),
("users-sentrym", "SENTRY_MONTHLY"),
("users-sentryy", "SENTRY_YEARLY"),
("users-teamm", "TEAM_MONTHLY"),
("users-teamy", "TEAM_YEARLY"),
("users", "GHM_PLAN_NAME"),
("users-free", "FREE_PLAN_NAME"),
("users-inappm", "CODECOV_PRO_MONTHLY_LEGACY"),
("users-inappy", "CODECOV_PRO_YEARLY_LEGACY"),
("users-enterprisem", "ENTERPRISE_CLOUD_MONTHLY"),
("users-enterprisey", "ENTERPRISE_CLOUD_YEARLY"),
("users-developer", "USERS_DEVELOPER"),
],
default="users-developer",
max_length=50,
),
),
migrations.RunSQL(
"ALTER TYPE plans ADD VALUE IF NOT EXISTS 'users-developer';"
),
migrations.AlterField(
model_name="owner",
name="plan",
field=models.TextField(blank=True, default="users-developer", null=True),
),
]
4 changes: 2 additions & 2 deletions shared/django_apps/codecov_auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class Account(BaseModel):
max_length=50,
choices=PlanName.choices(),
null=False,
default=PlanName.BASIC_PLAN_NAME.value,
default=PlanName.USERS_DEVELOPER.value,
)
plan_seat_count = models.SmallIntegerField(default=1, null=False, blank=True)
free_seat_count = models.SmallIntegerField(default=0, null=False, blank=True)
Expand Down Expand Up @@ -328,7 +328,7 @@ class Meta:
cache = models.JSONField(null=True)
# Really an ENUM in db
plan = models.TextField(
null=True, default=PlanName.BASIC_PLAN_NAME.value, blank=True
null=True, default=PlanName.USERS_DEVELOPER.value, blank=True
)
plan_provider = models.TextField(
null=True, choices=PlanProviders.choices, blank=True
Expand Down
25 changes: 23 additions & 2 deletions shared/plan/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class MonthlyUploadLimits(enum.Enum):
CODECOV_BASIC_PLAN = 250
CODECOV_FREE_PLAN = 250
CODECOV_TEAM_PLAN = 2500


Expand All @@ -23,6 +23,9 @@ class PlanMarketingName(enum.Enum):
TEAM = "Team"


DEFAULT_FREE_PLAN = "users-developer"


class PlanName(enum.Enum):
# If you add or remove, make a migration for Account table
BASIC_PLAN_NAME = "users-basic"
Expand All @@ -39,6 +42,7 @@ class PlanName(enum.Enum):
CODECOV_PRO_YEARLY_LEGACY = "users-inappy"
ENTERPRISE_CLOUD_MONTHLY = "users-enterprisem"
ENTERPRISE_CLOUD_YEARLY = "users-enterprisey"
USERS_DEVELOPER = "users-developer"

@classmethod
def choices(cls):
Expand Down Expand Up @@ -292,7 +296,7 @@ def convert_to_DTO(self) -> dict:
"Unlimited private repositories",
],
tier_name=TierName.BASIC.value,
monthly_uploads_limit=MonthlyUploadLimits.CODECOV_BASIC_PLAN.value,
monthly_uploads_limit=MonthlyUploadLimits.CODECOV_FREE_PLAN.value,
trial_days=None,
)

Expand All @@ -311,9 +315,25 @@ def convert_to_DTO(self) -> dict:
monthly_uploads_limit=None,
)

DEVELOPER_PLAN = PlanData(
marketing_name=PlanMarketingName.FREE.value,
value=DEFAULT_FREE_PLAN,
billing_rate=None,
base_unit_price=PlanPrice.CODECOV_FREE.value,
benefits=[
"Up to 1 user",
"Unlimited public repositories",
"Unlimited private repositories",
],
tier_name=TierName.TEAM.value,
trial_days=None,
monthly_uploads_limit=250,
)

FREE_PLAN_REPRESENTATIONS = {
PlanName.FREE_PLAN_NAME.value: FREE_PLAN,
PlanName.BASIC_PLAN_NAME.value: BASIC_PLAN,
DEFAULT_FREE_PLAN: DEVELOPER_PLAN,
}

TEAM_PLAN_REPRESENTATIONS = {
Expand Down Expand Up @@ -389,6 +409,7 @@ def convert_to_DTO(self) -> dict:
}

PLANS_THAT_CAN_TRIAL = [
DEFAULT_FREE_PLAN,
PlanName.FREE_PLAN_NAME.value,
PlanName.BASIC_PLAN_NAME.value,
PlanName.CODECOV_PRO_MONTHLY.value,
Expand Down
12 changes: 7 additions & 5 deletions shared/plan/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from shared.django_apps.codecov.commands.exceptions import ValidationError
from shared.django_apps.codecov_auth.models import Owner, Plan, Service
from shared.plan.constants import (
DEFAULT_FREE_PLAN,
TEAM_PLAN_MAX_USERS,
TRIAL_PLAN_SEATS,
PlanBillingRate,
Expand Down Expand Up @@ -69,9 +70,11 @@ def current_org(self) -> Owner:
return self.current_org

def set_default_plan_data(self) -> None:
"""Sets the organization to the default basic plan."""
log.info(f"Setting plan to users-basic for owner {self.current_org.ownerid}")
self.current_org.plan = PlanName.BASIC_PLAN_NAME.value
"""Sets the organization to the default developer plan."""
log.info(
f"Setting plan to {DEFAULT_FREE_PLAN} for owner {self.current_org.ownerid}"
)
self.current_org.plan = DEFAULT_FREE_PLAN
self.current_org.plan_activated_users = None
self.current_org.plan_user_count = 1
self.current_org.stripe_subscription_id = None
Expand Down Expand Up @@ -155,9 +158,8 @@ def tier_name(self) -> TierName:
def available_plans(self, owner: Owner) -> List[Plan]:
"""Returns the available plans for the owner and organization."""
available_plans = {
Plan.objects.select_related("tier").get(name=PlanName.BASIC_PLAN_NAME.value)
Plan.objects.select_related("tier").get(name=DEFAULT_FREE_PLAN)
}

curr_plan = self.plan_data
if not curr_plan.paid_plan:
available_plans.add(curr_plan)
Expand Down
27 changes: 16 additions & 11 deletions tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from shared.django_apps.codecov_auth.models import BillingRate
from shared.django_apps.codecov_auth.tests.factories import PlanFactory, TierFactory
from shared.plan.constants import (
DEFAULT_FREE_PLAN,
PlanName,
PlanPrice,
TierName,
Expand Down Expand Up @@ -72,17 +73,6 @@ def mock_all_plans_and_tiers():
)

basic_tier = TierFactory(tier_name=TierName.BASIC.value)
PlanFactory(
name=PlanName.BASIC_PLAN_NAME.value,
tier=basic_tier,
marketing_name="Developer",
benefits=[
"Up to 1 user",
"Unlimited public repositories",
"Unlimited private repositories",
],
monthly_uploads_limit=250,
)
PlanFactory(
name=PlanName.FREE_PLAN_NAME.value,
tier=basic_tier,
Expand Down Expand Up @@ -217,3 +207,18 @@ def mock_all_plans_and_tiers():
"Priority Support",
],
)

PlanFactory(
name=DEFAULT_FREE_PLAN,
tier=team_tier,
marketing_name="Developer",
billing_rate=None,
base_unit_price=0,
paid_plan=False,
monthly_uploads_limit=250,
benefits=[
"Up to 1 user",
"Unlimited public repositories",
"Unlimited private repositories",
],
)
23 changes: 12 additions & 11 deletions tests/unit/django_apps/codecov_auth/test_codecov_auth_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
)
from shared.django_apps.core.tests.factories import RepositoryFactory
from shared.plan.constants import (
BASIC_PLAN,
DEFAULT_FREE_PLAN,
DEVELOPER_PLAN,
ENTERPRISE_CLOUD_USER_PLAN_REPRESENTATIONS,
PlanName,
)
Expand Down Expand Up @@ -380,11 +381,11 @@ def test_can_activate_user_cannot_activate_account(self):
def test_fields_that_account_overrides(self):
mock_all_plans_and_tiers()
to_activate = OwnerFactory()
self.owner.plan = PlanName.BASIC_PLAN_NAME.value
self.owner.plan = DEFAULT_FREE_PLAN
self.owner.plan_user_count = 1
self.owner.save()
self.assertTrue(self.owner.can_activate_user(to_activate))
org_pretty_plan = asdict(BASIC_PLAN)
org_pretty_plan = asdict(DEVELOPER_PLAN)
org_pretty_plan.update({"quantity": 1})
self.assertEqual(self.owner.pretty_plan, org_pretty_plan)

Expand Down Expand Up @@ -527,7 +528,7 @@ def test_has_yaml(self):

class TestOrganizationLevelTokenModel(TestCase):
def test_can_save_org_token_for_org_basic_plan(self):
owner = OwnerFactory(plan="users-basic")
owner = OwnerFactory(plan=DEFAULT_FREE_PLAN)
owner.save()
token = OrganizationLevelToken(owner=owner)
token.save()
Expand All @@ -545,7 +546,7 @@ def test_token_is_deleted_when_changing_user_plan(
owner.save()
org_token.save()
assert OrganizationLevelToken.objects.filter(owner=owner).count() == 1
owner.plan = "users-basic"
owner.plan = DEFAULT_FREE_PLAN
owner.save()
assert OrganizationLevelToken.objects.filter(owner=owner).count() == 0

Expand Down Expand Up @@ -759,7 +760,7 @@ def test_account_with_users(self):
self.assertEqual(account.activated_student_count, 0)
self.assertEqual(account.total_seat_count, 1)
self.assertEqual(account.available_seat_count, 0)
pretty_plan = asdict(BASIC_PLAN)
pretty_plan = asdict(DEVELOPER_PLAN)
pretty_plan.update({"quantity": 1})
self.assertEqual(account.pretty_plan, pretty_plan)

Expand All @@ -772,21 +773,21 @@ def test_create_account_for_enterprise_experience(self):
user_for_owner_1 = UserFactory(email="[email protected]", name="Luigi")
owner_1 = OwnerFactory(
username="codecov-1",
plan=PlanName.BASIC_PLAN_NAME.value,
plan=DEFAULT_FREE_PLAN,
plan_user_count=1,
organizations=[],
user_id=user_for_owner_1.id, # has user
)
owner_2 = OwnerFactory(
username="codecov-sentry",
plan=PlanName.BASIC_PLAN_NAME.value,
plan=DEFAULT_FREE_PLAN,
plan_user_count=1,
organizations=[],
user_id=None, # no user
)
owner_3 = OwnerFactory(
username="sentry-1",
plan=PlanName.BASIC_PLAN_NAME.value,
plan=DEFAULT_FREE_PLAN,
plan_user_count=1,
organizations=[],
user_id=None, # no user
Expand All @@ -810,7 +811,7 @@ def test_create_account_for_enterprise_experience(self):
username="codecov-org",
stripe_customer_id=stripe_customer_id,
stripe_subscription_id=stripe_subscription_id,
plan=PlanName.BASIC_PLAN_NAME.value,
plan=DEFAULT_FREE_PLAN,
plan_user_count=50,
plan_activated_users=[owner_1.ownerid, owner_2.ownerid],
free=10,
Expand Down Expand Up @@ -930,7 +931,7 @@ def test_create_account_for_enterprise_experience(self):
self.assertEqual(enterprise_account.activated_student_count, 0)
self.assertEqual(enterprise_account.total_seat_count, 60)
self.assertEqual(enterprise_account.available_seat_count, 57)
pretty_plan = asdict(BASIC_PLAN)
pretty_plan = asdict(DEVELOPER_PLAN)
pretty_plan.update({"quantity": 50})
self.assertEqual(enterprise_account.pretty_plan, pretty_plan)

Expand Down
Loading

0 comments on commit 7ba099f

Please sign in to comment.