Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
9eae086
test(factories): add shared factories and traits for the test refactor
claude Oct 6, 2026
6b7635d
test: move shared test helpers and base test cases into buzz.tests
claude Oct 6, 2026
8afce5b
test(events): build tax test fixtures with factories
claude Oct 6, 2026
2b1fb60
style(tests): format test_factories with ruff 0.8.1
claude Oct 6, 2026
3ad72a4
test: switch base test cases to the set_user context manager
claude Oct 6, 2026
7b502ff
test(factories): smoke-test every factory the package exports
claude Oct 6, 2026
f8f5ff0
test: pass event names as strings to the whitelisted APIs
claude Oct 6, 2026
7d763ad
test(factories): add percentage, flat and free_tickets coupon traits
claude Oct 6, 2026
f3e7fe7
test(coupons): build fixtures with factories and split by behaviour
claude Oct 6, 2026
ea70b1b
test(tickets): build fixtures with factories
claude Oct 6, 2026
b15ae0b
test(report): build fixtures with factories and drop helper tests
claude Oct 6, 2026
b4152dc
test(factories): add EventPaymentFactory and IntegrationRequestFactory
claude Oct 6, 2026
00a226e
test(bookings): build fixtures with factories and split by behaviour
claude Oct 6, 2026
643c07a
test(factories): add campaign, cancellation request and refund factories
claude Oct 6, 2026
7d62dd8
test(api): drop status-code and shape tests from account, auth, campa…
claude Oct 6, 2026
c57ef6a
test(api): build map and communication fixtures with factories
claude Oct 6, 2026
da1cfbb
test(forms): build fixtures with factories and split by behaviour
claude Oct 6, 2026
070ea50
test(tickets): build fixtures with factories and split out windows
claude Oct 6, 2026
d9fb42b
test(telemetry): build fixtures with factories and split out event ca…
claude Oct 6, 2026
a56fb24
test(factories): add UserInvitationFactory
claude Oct 6, 2026
5ac969a
test(teams): build team fixtures with factories
claude Oct 6, 2026
1f06ace
test(permissions): split by behaviour and build fixtures with factories
claude Oct 6, 2026
1ebf66e
test(teams): build team API fixtures with factories
claude Oct 6, 2026
d4c026c
test(factories): add EventTemplateFactory, BuzzThemeFactory and Addit…
claude Oct 6, 2026
859d97f
test(events): split buzz event tests by behaviour and build fixtures …
claude Oct 6, 2026
8f48fa4
test(events): keep one copy of each event template test
claude Oct 6, 2026
ee8879b
test(events): build venue, host, theme, tier and og image fixtures wi…
claude Oct 6, 2026
94726c3
test(factories): add EventProposalFactory and EventSponsorFactory
claude Oct 6, 2026
2c42283
test(sponsorships): build fixtures with factories
claude Oct 6, 2026
437b0e5
test(proposals): build fixtures with factories
claude Oct 6, 2026
aedc5f3
test(factories): add SpeakerProfileFactory and EventTalkFactory
claude Oct 6, 2026
fed662a
test(events): split event API tests by endpoint and use factories
claude Oct 6, 2026
4efff8a
test(events): build ticket type and Zoom fixtures with factories
claude Oct 6, 2026
c8184fd
test(api): build booking, payment and check-in fixtures with factories
claude Oct 6, 2026
e08daf5
test(www): build public page fixtures with factories
claude Oct 6, 2026
271528f
test(api): fix outsider and sender fixtures in maps and comms tests
claude Oct 6, 2026
f29ab63
test(factories): create fixed users regardless of the session user
claude Oct 6, 2026
f90f730
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
07a20d1
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
b0fbb84
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
5384ece
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
b2ce896
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
daded91
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
ca0a886
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
2f8d639
style(tickets): simplify names and trim comments
claude Oct 6, 2026
361b58b
style(tests): trim factory docstrings to the traps worth noting
claude Oct 6, 2026
57406a0
test(events): fix event page schedule and speaker fixtures
claude Oct 6, 2026
459132a
style(bookings): simplify names and trim comments
claude Oct 6, 2026
08037f9
style(proposals): simplify names and trim comments
claude Oct 6, 2026
6356aa0
style(events): simplify names and trim comments
claude Oct 6, 2026
75ba67c
style(factories): cache the event factory's team with cached_property
claude Oct 6, 2026
4a9004a
style(tests): simplify names and trim comments
claude Oct 6, 2026
83b8236
test(teams): fix the Administrator team and stranger fixtures
claude Oct 6, 2026
16673ee
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
57b1c8d
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
9bf7587
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
a5e1885
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
4bd902a
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
f999175
style(events): simplify names and trim comments
claude Oct 6, 2026
31281bc
style(teams): simplify names and trim comments
claude Oct 6, 2026
31b46f5
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
7b461bc
refactor(sponsorships): cut test boilerplate
claude Oct 6, 2026
aab31a2
refactor(tests): cut test boilerplate
claude Oct 6, 2026
4f01bbe
refactor(events): cut test boilerplate
claude Oct 6, 2026
5369547
refactor(coupons): cut test boilerplate
claude Oct 6, 2026
8341a45
refactor(events): cut test boilerplate
claude Oct 6, 2026
386180f
fix(install): check for existing teams with a real query
claude Oct 6, 2026
a848522
refactor(bookings): cut test boilerplate
claude Oct 6, 2026
33d50f7
Merge remote-tracking branch 'origin/refactor/test-to-factories-1' in…
claude Oct 6, 2026
25c2a06
refactor(teams): cut test boilerplate
claude Oct 6, 2026
4006049
Merge remote-tracking branch 'origin/refactor/test-to-factories-3' in…
claude Oct 6, 2026
81d916a
Merge remote-tracking branch 'origin/refactor/test-to-factories-4' in…
claude Oct 6, 2026
36acd83
Merge remote-tracking branch 'origin/refactor/test-to-factories-5' in…
claude Oct 6, 2026
a3c3fb7
Merge remote-tracking branch 'origin/refactor/test-to-factories-6' in…
claude Oct 6, 2026
b103eab
Merge remote-tracking branch 'origin/refactor/test-to-factories-7' in…
claude Oct 6, 2026
7e7380f
Merge remote-tracking branch 'origin/refactor/test-to-factories-8' in…
claude Oct 6, 2026
b252c30
test(factories): export every lane factory from the package
claude Oct 6, 2026
2b3c184
test: delete the cross-module helpers the lanes stopped using
claude Oct 6, 2026
97845b6
Merge pull request #543 from bwhtech/refactor/test-to-factories-2
harshtandiya Oct 6, 2026
b3337a0
Merge pull request #542 from bwhtech/refactor/test-to-factories-3
harshtandiya Oct 6, 2026
5d53ba5
Merge pull request #546 from bwhtech/refactor/test-to-factories-4
harshtandiya Oct 6, 2026
9f661a6
Merge pull request #547 from bwhtech/refactor/test-to-factories-5
harshtandiya Oct 6, 2026
0801b38
Merge pull request #549 from bwhtech/refactor/test-to-factories-6
harshtandiya Oct 6, 2026
b272dd4
Merge pull request #548 from bwhtech/refactor/test-to-factories-7
harshtandiya Oct 6, 2026
cee6ea9
Merge pull request #544 from bwhtech/refactor/test-to-factories-8
harshtandiya Oct 6, 2026
d58a103
Merge branch 'refactor/test-to-factories-9' into refactor/test-to-fac…
claude Oct 6, 2026
b95a084
test: fold the lane support modules into the shared test files
claude Oct 6, 2026
7158b81
test: swap the lane 5 stand-in factories for the shared ones
claude Oct 6, 2026
0466342
test(factories): add TalkProposalFactory.create_as_guest
claude Oct 6, 2026
069d637
test: reduce setup_test_records to the statuses and the Administrator…
claude Oct 6, 2026
85b0b82
test: share the Alice/Bob permission fixture through TeamPermissionTe…
claude Oct 6, 2026
f867cef
docs(skills): update writing-tests for the finished factory refactor
claude Oct 6, 2026
9e3ce50
Merge remote-tracking branch 'origin/develop' into refactor/test-to-f…
claude Oct 6, 2026
bfce9b4
test(factories): patch sendmail in the factory smoke test
claude Oct 6, 2026
2749fbc
test(factories): keep one flat folder, one file per doctype
claude Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions .claude/skills/writing-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ Faker comes with frappe. Do not add it to `pyproject.toml`.
1. **Never** build a fixture with `frappe.get_doc({...}).insert()` or `frappe.new_doc(...)`.
Use a factory. If the doctype has none, write it first.
2. One factory per doctype: `buzz/tests/factories/<snake_case_doctype>_factory.py`, class
`<PascalCaseDocType>Factory(BaseFactory[<DocClass>])`, re-exported from `__init__.py`.
The generic is the real controller class, so the result is typed.
`<PascalCaseDocType>Factory(BaseFactory[<DocClass>])`, re-exported from
`buzz/tests/factories/__init__.py`; import it from `buzz.tests.factories`. The generic is
the real controller class, so the result is typed. `buzz/tests/test_factories.py` creates
every factory in `__all__`, so a new factory is smoke-tested once it is exported.
3. `default_attributes` sets only what `.insert()` needs. Leave out fields with a DocType
default or a controller fallback (`Event Booking.status`, the INR 0 price row
`Event Ticket Type.before_validate` adds).
Expand Down Expand Up @@ -109,12 +111,24 @@ the session user.

## Traps

**Administrator must not own throwaway teams.** `create_default_team_for` picks the first
enabled Owner membership, and `process_booking` commits, so its fixtures survive rollback.
An Administrator-owned test team becomes Administrator's default team on that site, and
`setup_test_records()` fails with `Venue Test Venue belongs to another team.` Use
**Administrator must not own throwaway teams.** `setup_test_records()` gives Administrator
exactly one team, and Event Template and event proposal submissions fill `team` from a sole
membership. `process_booking` commits, so its fixtures survive rollback. A second
Administrator team leaks across runs and breaks that lookup. Use
`BuzzTeamFactory.create_owned_by()`.

**`Buzz Event` names are autoincrement integers.** `.name` is an `int` in memory, and the
whitelisted APIs type-check `event: str`. Pass `str(event.name)`, and compare against `str(...)`
when the other side came from the database.

**`get_fullname` caches per process.** A user rebuilt in a later class keeps the earlier
name in that cache. Set the name the test asserts, and read it from the field the code reads.

**`frappe.set_user` is allowed only in `test_*.py`.** The `frappe-setuser` semgrep rule
fails CI anywhere else, for example in `buzz/tests/base_test_cases.py` or a factory. Use
`with self.set_user(user):`, `self.enterContext(self.set_user(user))` in `setUp`, or
`frappe.tests.classes.context_managers.set_user` outside a test case.

**User creation is throttled** at 60 new users an hour (`User.throttle_user_creation`), and
test users leak. For a fixed identity use `UserFactory.create_once(email)`; use `create()`
only for a distinct user.
Expand Down Expand Up @@ -150,10 +164,17 @@ class BookingTestCase(IntegrationTestCase):

`buzz/api/booking/test_booking.py` is the reference conversion.

Older modules still export ad-hoc helpers — `create_user` / `create_owned_team` /
`payload_for` in `test_buzz_team.py`, `ensure_prompt_named_record` in `test_forms.py`,
`create_event` / `create_ticket` in `test_permissions.py`. They retire module by module. Do
not add new callers.
Never import from another `test_*.py`. Shared base classes (`BookingTestCase`,
`CouponTestCase`, `BookingRefundTestCase`, `SponsorshipTestCase`, `TeamPermissionTestCase`)
live in `buzz/tests/base_test_cases.py`. Shared non-fixture helpers (email queue readers,
telemetry capture, `clear_map_link_cache`) live in `buzz/tests/utils.py`.

Use the Frappe context managers rather than manual cleanup: `self.set_user`,
`self.change_settings` (Singles only), `self.freeze_time`, `self.patch_hooks`.
`Buzz Team Settings` is not a Single, so use `BuzzTeamFactory.set_settings(team, values)`.

CI formats with ruff 0.8.1. Run `uvx ruff@0.8.1 format buzz && uvx ruff@0.8.1 check buzz`
before pushing; a newer ruff wraps lines differently.

## Running tests

Expand Down
88 changes: 27 additions & 61 deletions buzz/api/account/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from frappe.utils import set_request

from buzz.api.account import (
get_enabled_languages,
get_translations,
get_user_info,
update_user_language,
Expand All @@ -17,14 +16,7 @@


class LanguageTestCase(IntegrationTestCase):
"""Base for the tests that exercise language resolution.

`get_language` reads cookies and headers off the live request, which a test
process does not have until one is installed.
"""

def tearDown(self):
frappe.set_user("Administrator")
"""`get_language` reads cookies and headers off the live request, which a test process lacks."""

def install_request(self, cookies: dict[str, str] | None = None, accept_language: str = ""):
headers = {}
Expand All @@ -37,11 +29,7 @@ def install_request(self, cookies: dict[str, str] | None = None, accept_language
self.addCleanup(setattr, frappe.local, "request", None)

def other_enabled_language(self) -> str:
"""An enabled language that is not the site default.

Plain codes only: a regional one like `pt-BR` comes back as a different
string once werkzeug has parsed the Accept-Language header.
"""
"""Plain codes only: werkzeug rewrites a regional one like `pt-BR` when it parses the header."""
for language in get_all_languages():
if language != get_default_language() and language.isalpha():
return language
Expand All @@ -57,24 +45,24 @@ def set_user_language(self, language: str | None):
class TestGetUserInfo(LanguageTestCase):
def test_guest_payload_keys(self):
self.install_request()
frappe.set_user("Guest")
info = get_user_info().__json__()
with self.set_user("Guest"):
info = get_user_info().__json__()

self.assertEqual(set(info), {"is_logged_in", "brand_image", "language"})
self.assertFalse(info["is_logged_in"])

def test_guest_language_follows_the_preferred_language_cookie(self):
language = self.other_enabled_language()
self.install_request(cookies={"preferred_language": language})
frappe.set_user("Guest")

self.assertEqual(get_user_info().__json__()["language"], language)
with self.set_user("Guest"):
self.assertEqual(get_user_info().__json__()["language"], language)

def test_guest_language_falls_back_to_the_site_default(self):
self.install_request()
frappe.set_user("Guest")

self.assertEqual(get_user_info().__json__()["language"], get_default_language())
with self.set_user("Guest"):
self.assertEqual(get_user_info().__json__()["language"], get_default_language())

def test_logged_in_payload_shape(self):
info = get_user_info().__json__()
Expand All @@ -99,20 +87,8 @@ def test_logged_in_payload_shape(self):
self.assertTrue(info["is_logged_in"])
self.assertEqual(info["name"], "Administrator")

def test_roles_stay_child_table_rows(self):
roles = get_user_info().__json__()["roles"]

self.assertTrue(roles)
self.assertTrue(any(row.role == "Administrator" for row in roles))


class TestLanguages(LanguageTestCase):
def test_enabled_languages_shape(self):
languages = [language.__json__() for language in get_enabled_languages()]

self.assertTrue(languages)
self.assertEqual(set(languages[0]), {"name", "language_name", "language_code"})

def test_update_rejects_unknown_language(self):
frappe.clear_messages()

Expand All @@ -123,9 +99,6 @@ def test_update_rejects_unknown_language(self):
self.assertEqual(message["title"], "Language Not Available")
self.assertIn("not-a-language", message["message"])

def test_unknown_language_maps_to_400(self):
self.assertEqual(UnknownLanguage.http_status_code, 400)

def test_update_persists_language(self):
self.set_user_language(None)

Expand All @@ -139,14 +112,6 @@ def test_update_stays_closed_to_guests(self):


class TestTimezones(IntegrationTestCase):
def tearDown(self):
frappe.set_user("Administrator")

def set_user_timezone(self, time_zone: str | None):
original = frappe.db.get_value("User", "Administrator", "time_zone")
self.addCleanup(frappe.db.set_value, "User", "Administrator", "time_zone", original)
frappe.db.set_value("User", "Administrator", "time_zone", time_zone)

def test_update_rejects_unknown_timezone(self):
frappe.clear_messages()

Expand All @@ -157,9 +122,6 @@ def test_update_rejects_unknown_timezone(self):
self.assertEqual(message["title"], "Timezone Not Available")
self.assertIn("Mars/Olympus_Mons", message["message"])

def test_unknown_timezone_maps_to_400(self):
self.assertEqual(UnknownTimezone.http_status_code, 400)

def test_update_persists_timezone(self):
self.set_user_timezone(None)

Expand All @@ -168,7 +130,6 @@ def test_update_persists_timezone(self):
self.assertEqual(frappe.db.get_value("User", "Administrator", "time_zone"), "Asia/Kolkata")

def test_update_accepts_utc(self):
"""The dashboard's picker puts UTC at the top of the list."""
self.set_user_timezone(None)

update_user_timezone("UTC")
Expand All @@ -179,35 +140,32 @@ def test_update_stays_closed_to_guests(self):
self.assertIn(update_user_timezone, frappe.whitelisted)
self.assertNotIn(update_user_timezone, frappe.guest_methods)

def set_user_timezone(self, time_zone: str | None):
original = frappe.db.get_value("User", "Administrator", "time_zone")
self.addCleanup(frappe.db.set_value, "User", "Administrator", "time_zone", original)
frappe.db.set_value("User", "Administrator", "time_zone", time_zone)

class TestGetTranslations(LanguageTestCase):
def resolved_language(self) -> str:
"""The language get_translations picked, without loading any."""
with patch("buzz.api.account.get_all_translations", return_value={}) as translations:
get_translations()

translations.assert_called_once()
return translations.call_args.args[0]

class TestGetTranslations(LanguageTestCase):
def test_guest_translations_follow_the_preferred_language_cookie(self):
language = self.other_enabled_language()
self.install_request(cookies={"preferred_language": language})
frappe.set_user("Guest")

self.assertEqual(self.resolved_language(), language)
with self.set_user("Guest"):
self.assertEqual(self.resolved_language(), language)

def test_guest_translations_fall_back_to_the_accept_language_header(self):
language = self.other_enabled_language()
self.install_request(accept_language=language)
frappe.set_user("Guest")

self.assertEqual(self.resolved_language(), language)
with self.set_user("Guest"):
self.assertEqual(self.resolved_language(), language)

def test_guest_translations_fall_back_to_the_site_default(self):
self.install_request()
frappe.set_user("Guest")

self.assertEqual(self.resolved_language(), get_default_language())
with self.set_user("Guest"):
self.assertEqual(self.resolved_language(), get_default_language())

def test_logged_in_translations_follow_the_user_document(self):
language = self.other_enabled_language()
Expand All @@ -219,3 +177,11 @@ def test_a_user_without_a_language_gets_the_site_default(self):
self.set_user_language(None)

self.assertEqual(self.resolved_language(), get_default_language())

def resolved_language(self) -> str:
"""The language get_translations picked, without loading any."""
with patch("buzz.api.account.get_all_translations", return_value={}) as translations:
get_translations()

translations.assert_called_once()
return translations.call_args.args[0]
38 changes: 6 additions & 32 deletions buzz/api/auth/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
import frappe
from frappe.tests import IntegrationTestCase

from buzz.api.auth import get_login_context


class TestGetLoginContext(IntegrationTestCase):
def tearDown(self):
frappe.set_user("Administrator")

def test_returns_expected_keys(self):
context = get_login_context().__json__()
self.assertEqual(
set(context),
{
"disable_signup",
"disable_user_pass_login",
"login_with_email_link",
"login_banner",
"provider_logins",
},
)

def test_settings_flags_stay_integers(self):
context = get_login_context().__json__()
for key in ("disable_signup", "disable_user_pass_login", "login_with_email_link"):
self.assertIsInstance(context[key], int)

def test_banner_is_rendered_as_html(self):
frappe.db.set_single_value("Buzz Settings", "login_banner", "# Welcome")
self.addCleanup(frappe.db.set_single_value, "Buzz Settings", "login_banner", None)

self.assertIn("<h1", get_login_context().__json__()["login_banner"])
with self.change_settings("Buzz Settings", login_banner="# Welcome"):
self.assertIn("<h1", get_login_context().__json__()["login_banner"])

def test_banner_is_none_when_unset(self):
frappe.db.set_single_value("Buzz Settings", "login_banner", None)

self.assertIsNone(get_login_context().__json__()["login_banner"])
with self.change_settings("Buzz Settings", login_banner=None):
self.assertIsNone(get_login_context().__json__()["login_banner"])

def test_available_to_guest(self):
frappe.set_user("Guest")

self.assertIsInstance(get_login_context().__json__()["provider_logins"], list)
with self.set_user("Guest"):
self.assertIsInstance(get_login_context().__json__()["provider_logins"], list)
44 changes: 1 addition & 43 deletions buzz/api/booking/test_booking.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from unittest.mock import patch

import frappe
from frappe.tests import IntegrationTestCase

from buzz.api.booking import (
get_booking_details,
Expand All @@ -12,19 +11,16 @@
validate_coupon,
)
from buzz.api.booking.exceptions import AddOnNotForEvent, InvalidAddOnValue, RegistrationsClosed
from buzz.api.booking.schemas import BookingRequest
from buzz.tests.base_test_cases import BOOKER, OUTSIDER, BookingTestCase
from buzz.tests.factories import (
BuzzCouponCodeFactory,
BuzzCustomFieldFactory,
BuzzEventFactory,
EventTicketTypeFactory,
OfflinePaymentMethodFactory,
TicketAddOnFactory,
UserFactory,
)

BOOKER = "booking-owner@example.com"
OUTSIDER = "booking-outsider@example.com"
VALID_PHONE = "+91-9000090000"

EVENT_DATA_FIELDS = {
Expand Down Expand Up @@ -97,44 +93,6 @@
}


class BookingTestCase(IntegrationTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.booker = UserFactory.create_once(BOOKER).name
cls.outsider = UserFactory.create_once(OUTSIDER).name
cls.event = BuzzEventFactory.create()
cls.event.reload()

def setUp(self):
frappe.set_user("Administrator")
frappe.clear_messages()
self.addCleanup(frappe.clear_document_cache, "Buzz Event", self.event.name)
self.set_event({"is_published": 1, "registrations_close_at": None, "allow_guest_booking": 0})
self.free_ticket_type = EventTicketTypeFactory.create(event=self.event.name)

def set_event(self, values):
frappe.db.set_value("Buzz Event", self.event.name, values)
frappe.clear_document_cache("Buzz Event", self.event.name)

def enable_phone_otp(self):
self.set_event({"allow_guest_booking": 1, "guest_verification_method": "Phone OTP"})

def booking_request(self, **overrides):
values = {
"attendees": [
{
"first_name": "Booker",
"email": "booker@example.com",
"ticket_type": str(self.free_ticket_type.name),
}
],
"event": str(self.event.name),
}
values.update(overrides)
return BookingRequest(**values)


class TestSendGuestBookingOtp(BookingTestCase):
def test_disabled_guest_booking_is_refused(self):
with self.assertRaises(frappe.ValidationError):
Expand Down
Loading
Loading