diff --git a/app/Support/Nutrients.php b/app/Support/Nutrients.php
index 346e39d..4ea4e89 100644
--- a/app/Support/Nutrients.php
+++ b/app/Support/Nutrients.php
@@ -71,12 +71,6 @@ class Nutrients
/**
* Calculate a nutrient multiplier for a Food.
- *
- * @param \App\Models\Food $food
- * @param float $amount
- * @param string|null $fromUnit
- *
- * @return float
*/
public static function calculateFoodNutrientMultiplier(
Food $food,
@@ -93,6 +87,7 @@ class Nutrients
return $amount / $food->serving_weight;
}
+ // @todo Determine if `empty($food->serving_unit)` case makes sense.
if (
empty($fromUnit)
|| empty($food->serving_unit)
@@ -130,13 +125,6 @@ class Nutrients
/**
* Calculate a nutrient amount for a recipe.
- *
- * @param \App\Models\Recipe $recipe
- * @param string $nutrient
- * @param float $amount
- * @param string $fromUnit
- *
- * @return float
*/
public static function calculateRecipeNutrientAmount(
Recipe $recipe,
diff --git a/database/factories/FoodFactory.php b/database/factories/FoodFactory.php
index 4cbd4cf..5e45f68 100644
--- a/database/factories/FoodFactory.php
+++ b/database/factories/FoodFactory.php
@@ -8,21 +8,81 @@ use Illuminate\Database\Eloquent\Factories\Factory;
class FoodFactory extends Factory
{
/**
- * The name of the factory's corresponding model.
- *
- * @var string
+ * {@inheritdoc}
*/
protected $model = Food::class;
/**
- * Define the model's default state.
- *
- * @return array
+ * {@inheritdoc}
*/
- public function definition()
+ public function definition(): array
{
return [
- //
+ 'name' => $this->faker->word,
+ 'detail' => $this->faker->sentence(2),
+ 'brand' => $this->faker->word,
+ 'source' => $this->faker->url,
+ 'serving_size' => $this->faker->randomFloat(2, 1/2, 5),
+ 'serving_unit' => $this->faker->randomElement(['tsp', 'tbsp', 'cup']),
+ 'serving_weight' => $this->faker->numberBetween(5, 500),
+ 'calories' => $this->faker->randomFloat(2, 0, 100),
+ 'fat' => $this->faker->randomFloat(2, 0, 10),
+ 'cholesterol' => $this->faker->randomFloat(2, 0, 100),
+ 'sodium' => $this->faker->randomFloat(2, 0, 500),
+ 'carbohydrates' => $this->faker->randomFloat(2, 0, 20),
+ 'protein' => $this->faker->randomFloat(2, 0, 20),
];
}
+
+ /**
+ * Make instance with "tsp" serving unit.
+ */
+ public function tspServingUnit()
+ {
+ return $this->state(function (array $attributes) {
+ return [
+ 'serving_unit' => 'tsp',
+ 'serving_size' => 1,
+ ];
+ });
+ }
+
+ /**
+ * Make instance with "tbsp" serving unit.
+ */
+ public function tbspServingUnit()
+ {
+ return $this->state(function (array $attributes) {
+ return [
+ 'serving_unit' => 'tbsp',
+ 'serving_size' => 1,
+ ];
+ });
+ }
+
+ /**
+ * Make instance with "cup" serving unit.
+ */
+ public function cupServingUnit()
+ {
+ return $this->state(function (array $attributes) {
+ return [
+ 'serving_unit' => 'cup',
+ 'serving_size' => 1,
+ ];
+ });
+ }
+
+ /**
+ * Make instance with no" serving unit.
+ */
+ public function noServingUnit()
+ {
+ return $this->state(function (array $attributes) {
+ return [
+ 'serving_unit' => null,
+ 'serving_unit_name' => 'head'
+ ];
+ });
+ }
}
diff --git a/phpunit.xml b/phpunit.xml
index 4ae4d97..76a606b 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -21,8 +21,8 @@
-
-
+
+
diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php
deleted file mode 100644
index cdb5111..0000000
--- a/tests/Feature/ExampleTest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-get('/');
-
- $response->assertStatus(200);
- }
-}
diff --git a/tests/Feature/RegistrationTest.php b/tests/Feature/RegistrationTest.php
deleted file mode 100644
index 6dd5ff8..0000000
--- a/tests/Feature/RegistrationTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-get('/register');
-
- $response->assertStatus(200);
- }
-
- public function test_new_users_can_register()
- {
- $response = $this->post('/register', [
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- 'password' => 'password',
- 'password_confirmation' => 'password',
- ]);
-
- $this->assertAuthenticated();
- $response->assertRedirect(RouteServiceProvider::HOME);
- }
-}
diff --git a/tests/Feature/Support/NutrientsTest.php b/tests/Feature/Support/NutrientsTest.php
new file mode 100644
index 0000000..81cc776
--- /dev/null
+++ b/tests/Feature/Support/NutrientsTest.php
@@ -0,0 +1,104 @@
+expectException(\DomainException::class);
+ Nutrients::calculateFoodNutrientMultiplier($food, $amount, $fromUnit);
+ }
+
+ /**
+ * Test valid Food nutrient multiplier calculation.
+ *
+ * @dataProvider foodsValidNutrientMultipliersProvider
+ */
+ public function testCalculateFoodValidNutrientMultiplier(
+ Food $food,
+ float $amount,
+ string $fromUnit,
+ float $expectedMultiplier
+ ): void {
+ $this->assertEquals(
+ Nutrients::calculateFoodNutrientMultiplier($food, $amount, $fromUnit),
+ $expectedMultiplier
+ );
+ }
+
+ /**
+ * Data providers.
+ */
+
+ /**
+ * Provide example foods and expected nutrient multipliers.
+ */
+ public function foodsInvalidNutrientMultipliersProvider(): array {
+ $this->refreshApplication();
+
+ /** @var \App\Models\Food $foodInvalidUnit */
+ $foodInvalidUnit = Food::factory()->make(['serving_unit' => 'invalid']);
+
+ return [
+ [$foodInvalidUnit, 1, 'tsp'],
+ [$foodInvalidUnit, 1, 'tbsp'],
+ [$foodInvalidUnit, 1, 'cup'],
+ [Food::factory()->tspServingUnit()->make(), 1, 'invalid'],
+ ];
+ }
+
+ /**
+ * Provide example foods and expected nutrient multipliers.
+ */
+ public function foodsValidNutrientMultipliersProvider(): array {
+ $this->refreshApplication();
+
+ /** @var \App\Models\Food[] $foods */
+ $foods = [
+ 'tsp' => Food::factory()->tspServingUnit()->make(),
+ 'tbsp' => Food::factory()->tbspServingUnit()->make(),
+ 'cup' => Food::factory()->cupServingUnit()->make(),
+ 'none' => Food::factory()->noServingUnit()->make(),
+ ];
+
+ return [
+ [$foods['tsp'], $foods['tsp']->serving_weight, 'oz', Nutrients::$gramsPerOunce],
+ [$foods['tsp'], 1, 'serving', 1],
+ [$foods['tsp'], $foods['tsp']->serving_weight * 1.5, 'gram', 1.5],
+ [$foods['tsp'], 2, 'tsp', 2],
+ [$foods['tsp'], 1, 'tbsp', 3],
+ [$foods['tsp'], 1, 'cup', 48],
+ [$foods['tbsp'], $foods['tbsp']->serving_weight, 'oz', Nutrients::$gramsPerOunce],
+ [$foods['tbsp'], 1, 'serving', 1],
+ [$foods['tbsp'], $foods['tbsp']->serving_weight * 2, 'gram', 2],
+ [$foods['tbsp'], 2, 'tsp', 2/3],
+ [$foods['tbsp'], 1, 'tbsp', 1],
+ [$foods['tbsp'], 2, 'cup', 32],
+ [$foods['cup'], $foods['cup']->serving_weight, 'oz', Nutrients::$gramsPerOunce],
+ [$foods['cup'], 1, 'serving', 1],
+ [$foods['cup'], $foods['cup']->serving_weight * 2.25, 'gram', 2.25],
+ [$foods['cup'], 3, 'tsp', 1/16],
+ [$foods['cup'], 2, 'tbsp', 1/8],
+ [$foods['cup'], 5, 'cup', 5],
+ [$foods['none'], $foods['none']->serving_weight, 'oz', Nutrients::$gramsPerOunce],
+ [$foods['none'], 1, 'serving', 1],
+ [$foods['none'], $foods['none']->serving_weight * 3.0125, 'gram', 3.0125],
+ ];
+ }
+}
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index 358cfc8..0000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-assertTrue(true);
- }
-}
diff --git a/tests/Unit/Support/ArrayFormatTest.php b/tests/Unit/Support/ArrayFormatTest.php
new file mode 100644
index 0000000..8753126
--- /dev/null
+++ b/tests/Unit/Support/ArrayFormatTest.php
@@ -0,0 +1,40 @@
+ [
+ 0 => 'ingredient-0',
+ 1 => 'ingredient-1',
+ 2 => 'ingredient-2',
+ 3 => 'ingredient-3',
+ ],
+ 'amount' => [
+ 0 => 'amount-0',
+ 1 => 'amount-1',
+ 2 => 'amount-2',
+ 3 => 'amount-3',
+ ],
+ ];
+ $expected = [
+ ['ingredient' => 'ingredient-0', 'amount' => 'amount-0'],
+ ['ingredient' => 'ingredient-1', 'amount' => 'amount-1'],
+ ['ingredient' => 'ingredient-2', 'amount' => 'amount-2'],
+ ['ingredient' => 'ingredient-3', 'amount' => 'amount-3'],
+ ];
+ $this->assertEqualsCanonicalizing($expected, ArrayFormat::flipTwoDimensionalKeys($input));
+ }
+}
diff --git a/tests/Unit/Support/NumberTest.php b/tests/Unit/Support/NumberTest.php
new file mode 100644
index 0000000..df39737
--- /dev/null
+++ b/tests/Unit/Support/NumberTest.php
@@ -0,0 +1,70 @@
+assertIsFloat($result);
+ $this->assertEquals($expectedFloat, $result);
+ }
+
+ /**
+ * Test (fraction) string to float conversion.
+ *
+ * @dataProvider fractionStringFloatsProvider
+ *
+ * @see \App\Support\Number::fractionStringFromFloat()
+ */
+ public function testFractionStringFromFloat(string $expectedString, float $float): void
+ {
+ $result = Number::fractionStringFromFloat($float);
+ $this->assertIsString($result);
+ $this->assertEquals($expectedString, $result);
+ }
+
+ /**
+ * Data providers.
+ */
+
+ /**
+ * Provide decimal string and float data.
+ *
+ * @see \Tests\Unit\Support\NumberTest::testFloatFromString()
+ */
+ public function decimalStringFloatsProvider(): array {
+ return [
+ ['0.0', 0.0], ['0.125', 1/8], ['0.25', 1/4], ['0.5', 1/2],
+ ['0.75', 3/4], ['1.0', 1.0], ['1.25', 1.25], ['1.5', 1.5],
+ ['2.5', 2.5], ['2.75', 2.75],
+ ];
+ }
+
+ /**
+ * Provide fraction string and float data.
+ *
+ * @see \Tests\Unit\Support\NumberTest::testFloatFromString()
+ * @see \Tests\Unit\Support\NumberTest::testFractionStringFromFloat()
+ */
+ public function fractionStringFloatsProvider(): array {
+ return [
+ ['0', 0.0], ['1/8', 1/8], ['1/4', 1/4], ['1/3', 1/3], ['1/2', 1/2],
+ ['2/3', 2/3], ['3/4', 3/4], ['1', 1.0], ['1 1/4', 1.25],
+ ['1 1/2', 1.5], ['2 1/2', 2.5], ['2 3/4', 2.75],
+ ];
+ }
+}