Repository: AndreWohnsland/CocktailBerry

Issue:
[Feature Request] Allow virgin cocktails to be prepared without connected non virgin ingredients
Currently, a cocktail is only possible if all ingredients are within either available or machine connected ingredients. Therefore, when only the alcoholic parts of virgin available cocktails are missing, the whole cocktail will not be shown, because the logic flags it as not possible.
Especially for none alcoholic drinkers, it would be very beneficial that virgin cocktails are still shown and can be prepared. This would result in more drinks for those users, and enable a complete alcohol-free setup for non-alcoholic parties. This requires a tweaking in not only the evaluation, but also the representation of a cocktail.

## AC
- [x] Allow a virgin flagged cocktail to be prepared even if there are alcoholic ingredients not available
- [x] Ensure that it is not possible to select the non-virgin version
- [x] Maybe add the virgin prefix there, so the UI is clear this is virgin only
- [ ] ~~Optional: If it makes sense, make this feature opt-out (e.g. a flag in option) but users might just disable virgin on a cocktail completely instead~~

Hints from issue comments:
@AndreWohnsland: We will currently not consider the opt-out feature, since the user can already just set a cocktail as not virgin possible. The increased complexity does not justify this small benefit.

Solution patch:
diff --git a/src/api/internal/utils.py b/src/api/internal/utils.py
index f690060..8c54ac6 100644
--- a/src/api/internal/utils.py
+++ b/src/api/internal/utils.py
@@ -28,6 +28,7 @@ def map_cocktail(cocktail: Optional[DBCocktail], scale: bool = True) -> Optional
         amount=cocktail.adjusted_amount,
         enabled=cocktail.enabled,
         virgin_available=cocktail.virgin_available,
+        only_virgin=cocktail.only_virgin,
         # ingredient hand is if it is currently not on a bottle
         ingredients=[
             CocktailIngredient(
diff --git a/src/api/models.py b/src/api/models.py
index e0ae627..d2c9b41 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -39,6 +39,7 @@ class Cocktail(BaseModel):
     amount: int
     enabled: bool
     virgin_available: bool
+    only_virgin: bool
     ingredients: list[CocktailIngredient]
     image: str
     default_image: str
diff --git a/src/api/routers/cocktails.py b/src/api/routers/cocktails.py
index 3622f5c..8c8e920 100644
--- a/src/api/routers/cocktails.py
+++ b/src/api/routers/cocktails.py
@@ -66,12 +66,15 @@ async def prepare_cocktail(
     if cocktail is None:
         message = DH.get_translation("element_not_found", element_name=f"Cocktail (id={cocktail_id})")
         raise HTTPException(status_code=404, detail=message)
+    cocktail.scale_cocktail(request.volume, factor)
     # need to check if the cocktail is possible
     # this can happen if there is no ui guidance, e.g. only a direct post of an id
+    # the cocktail might only be possible in the virgin version, but the user requested a non-virgin version
     hand_ids = DBC.get_available_ids()
-    if not cocktail.is_possible(hand_ids, cfg.MAKER_MAX_HAND_INGREDIENTS):
+    if not cocktail.is_possible(hand_ids, cfg.MAKER_MAX_HAND_INGREDIENTS) or (
+        cocktail.only_virgin and not cocktail.is_virgin
+    ):
         raise HTTPException(status_code=400, detail=DH.get_translation("cocktail_not_possible"))
-    cocktail.scale_cocktail(request.volume, factor)
     result, msg, ingredient = maker.validate_cocktail(cocktail)
     if result != PrepareResult.VALIDATION_OK:
         # we need also provide the frontend with the ingredient id that caused the error
diff --git a/src/models.py b/src/models.py
index f5a8798..52c3433 100644
--- a/src/models.py
+++ b/src/models.py
@@ -64,6 +64,7 @@ class Cocktail:
     enabled: bool
     virgin_available: bool
     ingredients: list[Ingredient]
+    only_virgin: bool = False
     adjusted_alcohol: float = 0
     adjusted_amount: int = 0
     adjusted_ingredients: list[Ingredient] = field(default_factory=list, init=False)
@@ -80,12 +81,22 @@ class Cocktail:
     @property
     def handadds(self):
         """Returns a list of all handadd Ingredients."""
-        return [x for x in self.adjusted_ingredients if x.bottle is None]
+        return [x for x in self.adjusted_ingredients if x.bottle is None and x.amount > 0]
 
     @property
     def machineadds(self):
         """Returns a list of all machine Ingredients."""
-        return [x for x in self.adjusted_ingredients if x.bottle is not None]
+        return [x for x in self.adjusted_ingredients if x.bottle is not None and x.amount > 0]
+
+    @property
+    def virgin_handadds(self):
+        """Returns a list of all non-alcoholic handadd Ingredients."""
+        return [x for x in self.handadds if x.alcohol == 0]
+
+    @property
+    def virgin_machineadds(self):
+        """Returns a list of all non-alcoholic machine Ingredients."""
+        return [x for x in self.machineadds if x.alcohol == 0]
 
     @property
     def is_virgin(self):
@@ -94,21 +105,53 @@ class Cocktail:
 
     def is_possible(self, hand_available: list[int], max_hand_ingredients: int):
         """Return if the recipe is possible with given additional hand add ingredients."""
-        machine = self.machineadds
+        self.only_virgin = False
+        if self._is_normal_cocktail_possible(hand_available, max_hand_ingredients):
+            return True
+        if self.virgin_available and self._is_virgin_cocktail_possible(hand_available, max_hand_ingredients):
+            self.only_virgin = True
+            return True
+        return False
+
+    def _has_all_ingredients(
+        self,
+        hand_available: list[int],
+        max_hand_ingredients: int,
+        machine_adds: list[Ingredient],
+        hand_adds: list[Ingredient],
+    ) -> bool:
+        """Return if the recipe is possible with given additional hand add ingredients."""
         # If machine got not at least 1 add (=all handadd) return false
         # We don't want to let the user do all the work
-        if len(machine) < 1:
+        if len(machine_adds) < 1:
             return False
-        hand = self.handadds
         # if the number of hand adds is higher than the allowed hand adds, return false
-        if len(hand) > max_hand_ingredients:
+        if len(hand_adds) > max_hand_ingredients:
             return False
-        for ing in machine:
+        for ing in machine_adds:
             if ing.bottle is None:
                 return False
-        hand_id = {x.id for x in hand}
+        hand_id = {x.id for x in hand_adds}
         return not hand_id - set(hand_available)
 
+    def _is_normal_cocktail_possible(self, hand_available: list[int], max_hand_ingredients: int):
+        """Check if the normal (alcoholic) cocktail is possible."""
+        return self._has_all_ingredients(
+            hand_available,
+            max_hand_ingredients,
+            self.machineadds,
+            self.handadds,
+        )
+
+    def _is_virgin_cocktail_possible(self, hand_available: list[int], max_hand_ingredients: int):
+        """Check if the virgin cocktail is possible."""
+        return self._has_all_ingredients(
+            hand_available,
+            max_hand_ingredients,
+            self.virgin_machineadds,
+            self.virgin_handadds,
+        )
+
     def enough_fill_level(self) -> Optional[Ingredient]:
         """Check if the needed volume is there.
 
diff --git a/src/ui/cocktail_view.py b/src/ui/cocktail_view.py
index 7a34b19..54cbe6c 100644
--- a/src/ui/cocktail_view.py
+++ b/src/ui/cocktail_view.py
@@ -46,7 +46,10 @@ def generate_image_block(cocktail: Cocktail | None, mainscreen: MainScreen):
     header_font_size = round(square_size / 15.8)
     header_height = round(square_size / 6.3)
     single_ingredient_label = UI_LANGUAGE.get_translation("label_single_ingredient", "main_window")
-    name_label = cocktail.name if cocktail is not None else single_ingredient_label
+    name_label = single_ingredient_label
+    if cocktail is not None:
+        prefix = "V. " if cocktail.only_virgin else ""
+        name_label = f"{prefix}{cocktail.name}"
     button = create_button(
         name_label,
         font_size=header_font_size,
diff --git a/src/ui/setup_cocktail_selection.py b/src/ui/setup_cocktail_selection.py
index 589f2aa..de28a72 100644
--- a/src/ui/setup_cocktail_selection.py
+++ b/src/ui/setup_cocktail_selection.py
@@ -72,6 +72,8 @@ class CocktailSelection(QDialog, Ui_CocktailSelection):
         # and when it changes, the gui elements will not update
         db_cocktail = DB_COMMANDER.get_cocktail(cocktail.id)
         if db_cocktail is not None:
+            # need to revaluate if there is only the virgin version available
+            db_cocktail.is_possible(DB_COMMANDER.get_available_ids(), cfg.MAKER_MAX_HAND_INGREDIENTS)
             cocktail = db_cocktail
         self.cocktail = cocktail
         self._set_image()
@@ -108,21 +110,14 @@ class CocktailSelection(QDialog, Ui_CocktailSelection):
         self.prepare_button.setText(
             UI_LANGUAGE.get_translation("prepare_button", "cocktail_selection", amount=amount, unit=cfg.EXP_MAKER_UNIT)
         )
-        self.LAlkoholname.setText(self.cocktail.name)
+        virgin_prefix = "V. " if self.cocktail.is_virgin else ""
+        self.LAlkoholname.setText(f"{virgin_prefix}{self.cocktail.name}")
         display_volume = self._decide_rounding(amount * cfg.EXP_MAKER_FACTOR, 20)
         self.LMenge.setText(f"{display_volume} {cfg.EXP_MAKER_UNIT}")
         self.LAlkoholgehalt.setText(f"{self.cocktail.adjusted_alcohol:.1f}%")
         display_data = self.cocktail.machineadds
         hand = self.cocktail.handadds
-        # remove ingredients that have amount of 0
-        hand = [ing for ing in hand if ing.amount > 0]
-        display_data = [ing for ing in display_data if ing.amount > 0]
-        # Activates or deactivates the virgin checkbox, depending on the virgin flag
-        self.virgin_checkbox.setEnabled(self.cocktail.virgin_available)
-        # Styles does not work on strikeout, so we use internal qt things
-        # To be precise, they do work at start, but does not support dynamic changes
-        set_strike_through(self.virgin_checkbox, not self.cocktail.virgin_available)
-        # when there is handadd, also build some additional data
+        self._apply_virgin_setting()
         if hand:
             display_data.extend([Ingredient(-1, "", 0, 0, 0, False, 100, 100), *hand])
         fields_ingredient = self.get_labels_maker_ingredients()
@@ -153,6 +148,16 @@ class CocktailSelection(QDialog, Ui_CocktailSelection):
                 ingredient_name = ing.name
             field_ingredient.setText(f"{ingredient_name} ")
 
+    def _apply_virgin_setting(self):
+        # hide the strong/weak buttons, since they are not needed
+        self.increase_alcohol.setVisible(not self.cocktail.only_virgin)
+        self.decrease_alcohol.setVisible(not self.cocktail.only_virgin)
+        can_change_virgin = self.cocktail.virgin_available and not self.cocktail.only_virgin
+        self.virgin_checkbox.setEnabled(can_change_virgin)
+        # Styles does not work on strikeout, so we use internal qt things
+        # To be precise, they do work at start, but does not support dynamic changes
+        set_strike_through(self.virgin_checkbox, not can_change_virgin)
+
     def _decide_rounding(self, val: float, threshold=8):
         """Return the right rounding for numbers displayed to the user."""
         if val >= threshold:
@@ -165,14 +170,17 @@ class CocktailSelection(QDialog, Ui_CocktailSelection):
         self.LAlkoholgehalt.setText("")
         self.LAlkoholname.setText(UI_LANGUAGE.get_cocktail_dummy())
         self.LMenge.setText("")
-        self.virgin_checkbox.setChecked(False)
+        self.virgin_checkbox.setChecked(self.cocktail.only_virgin)
         for field_ingredient, field_volume in zip(self.get_labels_maker_ingredients(), self.get_labels_maker_volume()):
             field_ingredient.setText("")
             field_volume.setText("")
 
     def reset_alcohol_factor(self):
         """Set the alcohol slider to default (100%) value."""
-        shared.alcohol_factor = 1.0
+        if self.cocktail.only_virgin:
+            shared.alcohol_factor = 0.0
+        else:
+            shared.alcohol_factor = 1.0
 
     def adjust_maker_label_size_cocktaildata(self):
         """Adjust the font size for larger screens."""
diff --git a/web_client/src/api/cocktails.ts b/web_client/src/api/cocktails.ts
index 42153f5..38eb0bb 100644
--- a/web_client/src/api/cocktails.ts
+++ b/web_client/src/api/cocktails.ts
@@ -38,14 +38,12 @@ export const prepareCocktail = async (
   cocktail: Cocktail,
   volume: number,
   alcohol_factor: number,
-  is_virgin: boolean,
   teamName?: string,
 ): Promise<CocktailStatus> => {
   return axiosInstance
     .post<CocktailStatus>(`${cocktail_url}/prepare/${cocktail.id}`, {
       volume,
       alcohol_factor,
-      is_virgin,
       selected_team: teamName,
     })
     .then((res) => res.data);
diff --git a/web_client/src/components/cocktail/CocktailList.tsx b/web_client/src/components/cocktail/CocktailList.tsx
index 8042326..1b90776 100644
--- a/web_client/src/components/cocktail/CocktailList.tsx
+++ b/web_client/src/components/cocktail/CocktailList.tsx
@@ -55,6 +55,7 @@ const CocktailList: React.FC = () => {
             >
               <h2 className='text-center py-1 flex items-center justify-center'>
                 {cocktail.virgin_available && <MdNoDrinks className='mr-2' />}
+                {cocktail.only_virgin && 'V. '}
                 {cocktail.name}
               </h2>
               <div className='relative w-full' style={{ paddingTop: '100%' }}>
diff --git a/web_client/src/components/cocktail/CocktailSelection.tsx b/web_client/src/components/cocktail/CocktailSelection.tsx
index 43f54a5..b181f6e 100644
--- a/web_client/src/components/cocktail/CocktailSelection.tsx
+++ b/web_client/src/components/cocktail/CocktailSelection.tsx
@@ -33,7 +33,7 @@ const alcoholFactor = {
 
 const CocktailSelection: React.FC<CocktailModalProps> = ({ selectedCocktail, handleCloseModal }) => {
   const originalCocktail = JSON.parse(JSON.stringify(selectedCocktail));
-  const [alcohol, setAlcohol] = useState<alcoholState>('normal');
+  const [alcohol, setAlcohol] = useState<alcoholState>(selectedCocktail.only_virgin ? 'virgin' : 'normal');
   const [displayCocktail, setDisplayCocktail] = useState<Cocktail>(selectedCocktail);
   const [isProgressModalOpen, setIsProgressModalOpen] = useState(false);
   // Refill state
@@ -68,7 +68,7 @@ const CocktailSelection: React.FC<CocktailModalProps> = ({ selectedCocktail, han
 
   const handlePrepareCocktail = async (amount: number, teamName: string | undefined = undefined) => {
     const factor = alcoholFactor[alcohol];
-    prepareCocktail(displayCocktail, amount, factor, alcohol === 'virgin', teamName)
+    prepareCocktail(displayCocktail, amount, factor, teamName)
       .then(() => {
         setIsProgressModalOpen(true);
       })
@@ -112,13 +112,19 @@ const CocktailSelection: React.FC<CocktailModalProps> = ({ selectedCocktail, han
             <div className='flex space-x-2'>
               <button
                 onClick={() => handleAlcoholState('high')}
-                className={`w-8 p-2 rounded-full ${alcohol === 'high' ? 'bg-secondary' : 'bg-primary'}`}
+                className={`w-8 p-2 rounded-full ${alcohol === 'high' ? 'bg-secondary' : 'bg-primary'} ${
+                  selectedCocktail.only_virgin && 'disabled'
+                }`}
+                disabled={selectedCocktail.only_virgin}
               >
                 <FaSkullCrossbones className='text-background' />
               </button>
               <button
                 onClick={() => handleAlcoholState('low')}
-                className={`w-8 p-2 rounded-full ${alcohol === 'low' ? 'bg-secondary' : 'bg-primary'}`}
+                className={`w-8 p-2 rounded-full ${alcohol === 'low' ? 'bg-secondary' : 'bg-primary'} ${
+                  selectedCocktail.only_virgin && 'disabled'
+                }`}
+                disabled={selectedCocktail.only_virgin}
               >
                 <IoIosHappy className='text-background' />
               </button>
@@ -126,6 +132,7 @@ const CocktailSelection: React.FC<CocktailModalProps> = ({ selectedCocktail, han
                 <button
                   onClick={() => handleAlcoholState('virgin')}
                   className={`w-8 p-2 rounded-full ${alcohol === 'virgin' ? 'bg-secondary' : 'bg-primary'}`}
+                  disabled={selectedCocktail.only_virgin}
                 >
                   <MdNoDrinks className='text-background' />
                 </button>
@@ -136,7 +143,10 @@ const CocktailSelection: React.FC<CocktailModalProps> = ({ selectedCocktail, han
             </button>
           </div>
           <div>
-            <h2 className='text-2xl font-bold text-center text-neutral underline'>{selectedCocktail.name}</h2>
+            <h2 className='text-2xl font-bold text-center text-neutral underline'>
+              {alcohol === 'virgin' && 'V. '}
+              {selectedCocktail.name}
+            </h2>
             <div className='mt-2'>
               <ul className='text-center'>
                 {machineIngredients.map((ingredient) => (
diff --git a/web_client/src/index.css b/web_client/src/index.css
index 3b29bac..81cfc20 100644
--- a/web_client/src/index.css
+++ b/web_client/src/index.css
@@ -222,7 +222,5 @@ input[type='time']::-webkit-calendar-picker-indicator {
 /* Class added to body when modal is open */
 .ReactModal__Body--open {
   overflow: hidden;
-  position: fixed;
-  width: 100%;
-  height: 100%;
+  padding-right: var(--scrollbar-width, 15px); /* Prevent layout shift when scrollbar disappears */
 }
diff --git a/web_client/src/types/models.ts b/web_client/src/types/models.ts
index 391e086..9d741d9 100644
--- a/web_client/src/types/models.ts
+++ b/web_client/src/types/models.ts
@@ -15,6 +15,7 @@ export interface Cocktail {
   amount: number;
   enabled: boolean;
   virgin_available: boolean;
+  only_virgin: boolean;
   ingredients: CocktailIngredient[];
   image: string;
   default_image: string;


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 52b5612..a111e51 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -16,6 +16,8 @@ def db_commander():
     db_commander.insert_new_ingredient("Tequila", 38, 750, False, 100, 200, "ml")  # id 5
     # This one will not be added to the machine / available
     db_commander.insert_new_ingredient("Fanta", 0, 1000, False, 100, 50, "ml")  # id 6
+    # Adding a new alcoholic ingredient that is NOT available (neither via machine nor hand)
+    db_commander.insert_new_ingredient("Vodka", 40, 1000, False, 100, 150, "ml")  # id 7
 
     # create bottle 1-24
     with db_commander.session_scope() as session:
@@ -31,6 +33,8 @@ def db_commander():
     db_commander.insert_new_recipe("With Handadd", 20, 250, True, False, [(1, 50, 1), (4, 200, 2)])  # id 3
     # not all ingredients are available
     db_commander.insert_new_recipe("Not Available", 10, 250, True, False, [(4, 50, 1), (6, 200, 2)])  # id 4
+    # This one should be possible only in its virgin form
+    db_commander.insert_new_recipe("Virgin Only Possible", 12, 300, True, True, [(7, 100, 1), (2, 200, 2)])  # id 5
 
     # Assign ingredients to bottles
     db_commander.set_bottle_at_slot("White Rum", 1)
diff --git a/tests/test_database_commander.py b/tests/test_database_commander.py
index 27e2b49..5d22b8b 100644
--- a/tests/test_database_commander.py
+++ b/tests/test_database_commander.py
@@ -32,26 +32,52 @@ class TestCocktail:
     def test_get_all_cocktails(self, db_commander: DatabaseCommander):
         """Test the get_all_cocktails method."""
         cocktails = db_commander.get_all_cocktails()
-        assert len(cocktails) == 4
-        cocktail = cocktails[0]
-        assert cocktail.name == "Cuba Libre"
-        assert cocktail.alcohol == 11
-        assert cocktail.amount == 290
-        assert cocktail.enabled is True
-        assert cocktail.virgin_available is False
-        assert len(cocktail.ingredients) == 2
+        assert len(cocktails) == 5
+
+        cuba_libre = next((c for c in cocktails if c.name == "Cuba Libre"), None)
+        assert cuba_libre is not None
+        assert cuba_libre.alcohol == 11
+        assert cuba_libre.amount == 290
+        assert cuba_libre.enabled is True
+        assert cuba_libre.virgin_available is False
+        assert len(cuba_libre.ingredients) == 2
 
     def test_get_possible_cocktails(self, db_commander: DatabaseCommander):
         """Test the get_possible_cocktails method."""
         possible_cocktails = db_commander.get_possible_cocktails(max_hand_ingredients=1)
-        assert len(possible_cocktails) == 2
-        assert possible_cocktails[0].name == "Cuba Libre"
+        # Now we should have 3 possible cocktails: Cuba Libre, With Handadd, and Virgin Only Possible
+        assert len(possible_cocktails) == 3
+        # Check that Cuba Libre is in the list
+        cuba_libre = next((c for c in possible_cocktails if c.name == "Cuba Libre"), None)
+        assert cuba_libre is not None
+        assert cuba_libre.only_virgin is False
+
+    def test_get_possible_cocktail_virgin_only(self, db_commander: DatabaseCommander):
+        """Test that a cocktail that can only be made in virgin form is properly flagged."""
+        possible_cocktails = db_commander.get_possible_cocktails(max_hand_ingredients=1)
+
+        virgin_only = next((c for c in possible_cocktails if c.name == "Virgin Only Possible"), None)
+        assert virgin_only is not None
+        assert virgin_only.only_virgin is True
+
+        # Verify that this cocktail has an alcoholic ingredient that's not available
+        alcoholic_ingredients = [ing for ing in virgin_only.ingredients if ing.alcohol > 0]
+        assert len(alcoholic_ingredients) > 0
+
+        # Check that the virgin ingredients are available either via machine or hand
+        virgin_ingredients = [ing for ing in virgin_only.ingredients if ing.alcohol == 0]
+        assert len(virgin_ingredients) > 0
+
+        for ing in virgin_ingredients:
+            # Either the ingredient is connected to a bottle or it's in the available handadd list
+            assert ing.bottle is not None or ing.id in db_commander.get_available_ids()
 
     def test_get_disabled_cocktails(self, db_commander: DatabaseCommander):
         """Test that we can get only not enabled cocktails."""
         disabled_cocktails = db_commander.get_all_cocktails(status="disabled")
         assert len(disabled_cocktails) == 1
-        assert disabled_cocktails[0].name == "Tequila Sunrise"
+        tequila_sunrise = next((c for c in disabled_cocktails if c.name == "Tequila Sunrise"), None)
+        assert tequila_sunrise is not None
 
     def test_increment_recipe_counter(self, db_commander: DatabaseCommander):
         """Test the increment_recipe_counter method."""
@@ -157,28 +183,31 @@ class TestIngredient:
     def test_get_all_ingredients(self, db_commander: DatabaseCommander):
         """Test the get_all_ingredients method."""
         ingredients = db_commander.get_all_ingredients()
-        assert len(ingredients) == 6
-        ingredient = ingredients[1]
-        assert ingredient.name == "Cola"
-        assert ingredient.alcohol == 0
-        assert ingredient.bottle_volume == 1000
-        assert ingredient.fill_level == 0
-        assert ingredient.hand is False
-        assert ingredient.pump_speed == 100
+        assert len(ingredients) == 7
+
+        cola = next((i for i in ingredients if i.name == "Cola"), None)
+        assert cola is not None
+        assert cola.alcohol == 0
+        assert cola.bottle_volume == 1000
+        assert cola.fill_level == 0
+        assert cola.hand is False
+        assert cola.pump_speed == 100
 
     def test_get_all_machine_ingredients(self, db_commander: DatabaseCommander):
         """Test the get_all_machine_ingredients method."""
         ingredients = db_commander.get_all_ingredients(get_hand=False)
-        assert len(ingredients) == 5
-        ingredient = ingredients[1]
-        assert ingredient.name == "Cola"
+        assert len(ingredients) == 6
+
+        cola = next((i for i in ingredients if i.name == "Cola"), None)
+        assert cola is not None
 
     def test_get_all_hand_ingredients(self, db_commander: DatabaseCommander):
         """Test the get_all_hand_ingredients method."""
         ingredients = db_commander.get_all_ingredients(get_machine=False)
         assert len(ingredients) == 1
-        ingredient = ingredients[0]
-        assert ingredient.name == "Blue Curacao"
+
+        blue_curacao = next((i for i in ingredients if i.name == "Blue Curacao"), None)
+        assert blue_curacao is not None
 
     def test_get_all_ingredients_return_empty_if_both_false(self, db_commander: DatabaseCommander):
         """Test the get_all_ingredients method."""