Seed and bootstrap users via user_login.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
4f0abceb20
commit
4eed6b2e87
9 files changed
+65
-43
No files matched your search
@@ -8,13 +8,13 @@ class ManagedByRequestTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.buh = User.objects.create_user(
|
||||
user_name="buh1", password="x", status=User.STATUS_BUH
|
||||
user_login="buh1", password="x", status=User.STATUS_BUH
|
||||
)
|
||||
self.buh_other = User.objects.create_user(
|
||||
user_name="buh2", password="x", status=User.STATUS_BUH
|
||||
user_login="buh2", password="x", status=User.STATUS_BUH
|
||||
)
|
||||
self.gm = User.objects.create_user(
|
||||
user_name="gm1", password="x", status=User.STATUS_GM
|
||||
user_login="gm1", password="x", status=User.STATUS_GM
|
||||
)
|
||||
|
||||
def test_gm_requests_and_buh_approves(self):
|
||||
@@ -60,7 +60,7 @@ class ManagedByRequestTests(TestCase):
|
||||
|
||||
def test_gm_lists_buhs_only(self):
|
||||
User.objects.create_user(
|
||||
user_name="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
user_login="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
)
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.get("/api/v1/users/buhs/")
|
||||
@@ -70,7 +70,7 @@ class ManagedByRequestTests(TestCase):
|
||||
|
||||
def test_gm_admin_cannot_request_buh(self):
|
||||
gm_admin = User.objects.create_user(
|
||||
user_name="gma1", password="x", status=User.STATUS_GM_ADMIN
|
||||
user_login="gma1", password="x", status=User.STATUS_GM_ADMIN
|
||||
)
|
||||
self.client.force_authenticate(gm_admin)
|
||||
response = self.client.post(
|
||||
@@ -80,7 +80,7 @@ class ManagedByRequestTests(TestCase):
|
||||
|
||||
def test_gm_cannot_request_bu_admin(self):
|
||||
bu_admin = User.objects.create_user(
|
||||
user_name="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
user_login="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
)
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
@@ -90,7 +90,7 @@ class ManagedByRequestTests(TestCase):
|
||||
|
||||
def test_bu_admin_cannot_approve(self):
|
||||
bu_admin = User.objects.create_user(
|
||||
user_name="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
user_login="buadmin1", password="x", status=User.STATUS_BU_ADMIN
|
||||
)
|
||||
self.gm.managed_by_pending = bu_admin
|
||||
self.gm.save(update_fields=["managed_by_pending", "updated_at"])
|
||||
|
||||
@@ -10,19 +10,19 @@ class VisibilityScopeTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.director = User.objects.create_user(
|
||||
user_name="dir1", password="x", status=User.STATUS_DIRECTOR
|
||||
user_login="dir1", password="x", status=User.STATUS_DIRECTOR
|
||||
)
|
||||
self.buh = User.objects.create_user(
|
||||
user_name="buh1", password="x", status=User.STATUS_BUH
|
||||
user_login="buh1", password="x", status=User.STATUS_BUH
|
||||
)
|
||||
self.buh_other = User.objects.create_user(
|
||||
user_name="buh2", password="x", status=User.STATUS_BUH
|
||||
user_login="buh2", password="x", status=User.STATUS_BUH
|
||||
)
|
||||
self.gm = User.objects.create_user(
|
||||
user_name="gm1", password="x", status=User.STATUS_GM, managed_by=self.buh
|
||||
user_login="gm1", password="x", status=User.STATUS_GM, managed_by=self.buh
|
||||
)
|
||||
self.gm_other = User.objects.create_user(
|
||||
user_name="gm2",
|
||||
user_login="gm2",
|
||||
password="x",
|
||||
status=User.STATUS_GM,
|
||||
managed_by=self.buh_other,
|
||||
|
||||
+27
-14
@@ -7,18 +7,32 @@ from rest_framework.test import APIClient
|
||||
from apps.accounts.models import User
|
||||
from apps.farms.models import Cycle, Flock, Kandang, Site
|
||||
from apps.operations.models import FeedSacks, ManualInput
|
||||
from apps.sync.models import CitySite
|
||||
|
||||
|
||||
def _gm_visible_site(*, owner: User, site_name: str, code: str) -> Site:
|
||||
"""GM scope only sees sites linked to an active CitySite they registered."""
|
||||
city = CitySite.objects.create(
|
||||
code=code,
|
||||
name=site_name,
|
||||
api_base_url=f"https://{code}.example/api/v1",
|
||||
registered_by=owner,
|
||||
)
|
||||
return Site.objects.create(site_name=site_name, user=owner, city_site=city)
|
||||
|
||||
|
||||
class FarmAPITests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(user_name="tester", password="secret")
|
||||
self.user = User.objects.create_user(user_login="tester", password="secret")
|
||||
self.admin = User.objects.create_user(
|
||||
user_name="admin",
|
||||
user_login="admin",
|
||||
password="secret",
|
||||
status=User.STATUS_DIRECTOR_ADMIN,
|
||||
)
|
||||
self.client = APIClient()
|
||||
self.site = Site.objects.create(site_name="Sukawarna", user=self.user)
|
||||
self.site = _gm_visible_site(
|
||||
owner=self.user, site_name="Sukawarna", code="sukawarna"
|
||||
)
|
||||
self.kandang = Kandang.objects.create(kandang_name="Kandang 1", site=self.site)
|
||||
|
||||
def test_create_flock_under_kandang_requires_director_admin(self):
|
||||
@@ -73,12 +87,9 @@ class FarmAPITests(TestCase):
|
||||
doc_in_count=9000,
|
||||
status=Cycle.STATUS_ACTIVE,
|
||||
)
|
||||
new_end = start + timedelta(days=39)
|
||||
response = self.client.patch(
|
||||
f"/api/v1/cycles/{cycle.pk}/",
|
||||
{
|
||||
"end_date": new_end.isoformat(),
|
||||
"total_days": 40,
|
||||
"doc_in_count": 9500,
|
||||
"chick_in_weight": 42,
|
||||
},
|
||||
@@ -86,8 +97,8 @@ class FarmAPITests(TestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 200, response.data)
|
||||
cycle.refresh_from_db()
|
||||
self.assertEqual(cycle.end_date, new_end)
|
||||
self.assertEqual(cycle.total_days, 40)
|
||||
# end_date is not writable via PATCH (close flow uses approve-close).
|
||||
self.assertEqual(cycle.end_date, start + timedelta(days=34))
|
||||
self.assertEqual(cycle.doc_in_count, 9500)
|
||||
self.assertEqual(cycle.doc_in_weight, 42)
|
||||
|
||||
@@ -113,8 +124,8 @@ class FarmAPITests(TestCase):
|
||||
self.assertEqual(cycle.status, Cycle.STATUS_ACTIVE)
|
||||
|
||||
def test_gm_can_approve_cycle_close(self):
|
||||
gm = User.objects.create_user(user_name="gm_approver", password="x", status=User.STATUS_GM)
|
||||
site = Site.objects.create(site_name="GM Site", user=gm)
|
||||
gm = User.objects.create_user(user_login="gm_approver", password="x", status=User.STATUS_GM)
|
||||
site = _gm_visible_site(owner=gm, site_name="GM Site", code="gm-approve")
|
||||
kandang = Kandang.objects.create(kandang_name="Kandang GM", site=site)
|
||||
self.client.force_authenticate(gm)
|
||||
start = date(2026, 8, 1)
|
||||
@@ -143,8 +154,8 @@ class FarmAPITests(TestCase):
|
||||
self.assertEqual(cycle.end_date, proposed)
|
||||
|
||||
def test_gm_can_reject_cycle_close(self):
|
||||
gm = User.objects.create_user(user_name="gm_rejector", password="x", status=User.STATUS_GM)
|
||||
site = Site.objects.create(site_name="GM Reject Site", user=gm)
|
||||
gm = User.objects.create_user(user_login="gm_rejector", password="x", status=User.STATUS_GM)
|
||||
site = _gm_visible_site(owner=gm, site_name="GM Reject Site", code="gm-reject")
|
||||
kandang = Kandang.objects.create(kandang_name="Kandang R", site=site)
|
||||
self.client.force_authenticate(gm)
|
||||
start = date(2026, 8, 1)
|
||||
@@ -170,9 +181,11 @@ class FarmAPITests(TestCase):
|
||||
|
||||
def test_gm_admin_cannot_approve_cycle_close(self):
|
||||
gm_admin = User.objects.create_user(
|
||||
user_name="gm_admin_user", password="x", status=User.STATUS_GM_ADMIN
|
||||
user_login="gm_admin_user", password="x", status=User.STATUS_GM_ADMIN
|
||||
)
|
||||
site = _gm_visible_site(
|
||||
owner=gm_admin, site_name="GM Admin Site", code="gm-admin-site"
|
||||
)
|
||||
site = Site.objects.create(site_name="GM Admin Site", user=gm_admin)
|
||||
kandang = Kandang.objects.create(kandang_name="Kandang GMA", site=site)
|
||||
self.client.force_authenticate(gm_admin)
|
||||
start = date(2026, 8, 1)
|
||||
|
||||
@@ -7,7 +7,7 @@ from apps.accounts.models import ApiKey, User
|
||||
|
||||
def ensure_bootstrap_user(
|
||||
*,
|
||||
user_name: str,
|
||||
user_login: str,
|
||||
password: str,
|
||||
status: str,
|
||||
is_superuser: bool,
|
||||
@@ -15,8 +15,9 @@ def ensure_bootstrap_user(
|
||||
stdout,
|
||||
) -> User:
|
||||
user, created = User.objects.get_or_create(
|
||||
user_name=user_name,
|
||||
user_login=user_login,
|
||||
defaults={
|
||||
"user_name": user_login,
|
||||
"status": status,
|
||||
"is_superuser": is_superuser,
|
||||
},
|
||||
@@ -24,7 +25,7 @@ def ensure_bootstrap_user(
|
||||
|
||||
if not created and user.status != status:
|
||||
stdout.write(
|
||||
f"Updating {user_name!r} status {user.status!r} → {status!r}"
|
||||
f"Updating {user_login!r} status {user.status!r} → {status!r}"
|
||||
)
|
||||
|
||||
should_set_password = created or not user.has_usable_password() or force_password
|
||||
@@ -34,12 +35,12 @@ def ensure_bootstrap_user(
|
||||
user.is_superuser = is_superuser
|
||||
user.save()
|
||||
action = "Created" if created else "Updated password for"
|
||||
stdout.write(f"{action} {status} user {user_name}")
|
||||
stdout.write(f"{action} {status} user {user_login}")
|
||||
else:
|
||||
user.status = status
|
||||
user.is_superuser = is_superuser
|
||||
user.save(update_fields=["status", "is_superuser", "updated_at"])
|
||||
stdout.write(f"User {user_name} already exists (password unchanged)")
|
||||
stdout.write(f"User {user_login} already exists (password unchanged)")
|
||||
|
||||
return user
|
||||
|
||||
@@ -70,7 +71,7 @@ class Command(BaseCommand):
|
||||
)
|
||||
|
||||
admin = ensure_bootstrap_user(
|
||||
user_name=admin_user_name,
|
||||
user_login=admin_user_name,
|
||||
password=admin_password,
|
||||
status=User.STATUS_SUPERUSER,
|
||||
is_superuser=True,
|
||||
@@ -90,7 +91,7 @@ class Command(BaseCommand):
|
||||
"BOOTSTRAP_STAFF_PASSWORD is required when BOOTSTRAP_STAFF_USER is set."
|
||||
)
|
||||
ensure_bootstrap_user(
|
||||
user_name=staff_user_name,
|
||||
user_login=staff_user_name,
|
||||
password=staff_password,
|
||||
status=User.STATUS_GM,
|
||||
is_superuser=False,
|
||||
|
||||
@@ -75,8 +75,11 @@ class Command(BaseCommand):
|
||||
defaults["managed_by"] = managed_by
|
||||
|
||||
user, created = User.objects.get_or_create(
|
||||
user_name=spec["user_name"],
|
||||
defaults=defaults,
|
||||
user_login=spec["user_name"],
|
||||
defaults={
|
||||
**defaults,
|
||||
"user_name": spec["user_name"],
|
||||
},
|
||||
)
|
||||
user.status = spec["status"]
|
||||
user.is_superuser = spec.get("is_superuser", False)
|
||||
@@ -98,7 +101,10 @@ class Command(BaseCommand):
|
||||
return user
|
||||
|
||||
def _remove_legacy_staff(self, gm: User) -> None:
|
||||
staff = User.objects.filter(user_name="staff").first()
|
||||
staff = (
|
||||
User.objects.filter(user_login="staff").first()
|
||||
or User.objects.filter(user_name="staff").first()
|
||||
)
|
||||
if not staff:
|
||||
return
|
||||
moved_sites = Site.objects.filter(user=staff).update(user=gm)
|
||||
|
||||
@@ -15,7 +15,8 @@ from django.core.management import call_command
|
||||
class BootstrapAdminTests(TestCase):
|
||||
def test_creates_break_glass_superuser_without_demo_data(self):
|
||||
call_command("bootstrap_admin")
|
||||
user = User.objects.get(user_name="admin")
|
||||
user = User.objects.get(user_login="admin")
|
||||
self.assertEqual(user.user_name, "admin")
|
||||
self.assertEqual(user.status, User.STATUS_SUPERUSER)
|
||||
self.assertTrue(user.is_superuser)
|
||||
self.assertTrue(user.check_password("Pr04dm1n"))
|
||||
@@ -33,7 +34,8 @@ class BootstrapAdminTests(TestCase):
|
||||
)
|
||||
def test_creates_gm_when_configured(self):
|
||||
call_command("bootstrap_admin")
|
||||
staff = User.objects.get(user_name="staff")
|
||||
staff = User.objects.get(user_login="staff")
|
||||
self.assertEqual(staff.user_name, "staff")
|
||||
self.assertEqual(staff.status, User.STATUS_GM)
|
||||
self.assertFalse(staff.is_superuser)
|
||||
self.assertTrue(staff.check_password("St4ff!"))
|
||||
|
||||
@@ -12,7 +12,7 @@ from apps.operations.models import ChickenWeight, FeedSacks, IotPanel
|
||||
@override_settings(DASHBOARD_PUBLISH_HOUR=0)
|
||||
class OperationsAPITests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(user_name="tester", password="secret")
|
||||
self.user = User.objects.create_user(user_login="tester", password="secret")
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(self.user)
|
||||
site = Site.objects.create(site_name="Sukawarna", user=self.user)
|
||||
@@ -311,7 +311,7 @@ class OperationsAPITests(TestCase):
|
||||
@override_settings(DASHBOARD_PUBLISH_HOUR=17, TIME_ZONE="Asia/Jakarta")
|
||||
class DashboardVisibilityTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(user_name="tester", password="secret")
|
||||
self.user = User.objects.create_user(user_login="tester", password="secret")
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(self.user)
|
||||
site = Site.objects.create(site_name="Sukawarna", user=self.user)
|
||||
|
||||
@@ -62,7 +62,7 @@ class AggregationTests(TestCase):
|
||||
)
|
||||
class SyncChickenCountingTests(TestCase):
|
||||
def setUp(self):
|
||||
user = User.objects.create_user(user_name="sync-user", password="secret")
|
||||
user = User.objects.create_user(user_login="sync-user", password="secret")
|
||||
site = Site.objects.create(site_name="Sukawarna", user=user)
|
||||
self.kandang = Kandang.objects.create(kandang_name="Kandang 1", site=site)
|
||||
self.cycle = Cycle.objects.create(
|
||||
@@ -130,7 +130,7 @@ class SyncChickenCountingTests(TestCase):
|
||||
)
|
||||
class ChickenCountingSyncApiTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(user_name="tester", password="secret")
|
||||
self.user = User.objects.create_user(user_login="tester", password="secret")
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(self.user)
|
||||
site = Site.objects.create(site_name="Sukawarna", user=self.user)
|
||||
|
||||
@@ -156,7 +156,7 @@ class KarungWebCombinedTests(TestCase):
|
||||
@override_settings(KARUNG_WEB_ADMIN_BASE_URL="http://karung.internal")
|
||||
class RequestKarungTests(TestCase):
|
||||
def setUp(self):
|
||||
user = User.objects.create_user(user_name="tester", password="secret")
|
||||
user = User.objects.create_user(user_login="tester", password="secret")
|
||||
site = Site.objects.create(site_name="Sukawarna", user=user)
|
||||
self.kandang1 = Kandang.objects.create(kandang_name="Kandang 1", site=site)
|
||||
self.kandang2 = Kandang.objects.create(kandang_name="Kandang 2", site=site)
|
||||
|
||||
Reference in new issue
Block a user