diff --git a/src/GildedRose.php b/src/GildedRose.php index 533411b..96f039c 100644 --- a/src/GildedRose.php +++ b/src/GildedRose.php @@ -4,6 +4,19 @@ class GildedRose { + private const AGED_BRIE = 'Aged Brie'; + private const BACKSTAGE_PASSES = 'Backstage passes to a TAFKAL80ETC concert'; + private const SULFURAS = 'Sulfuras, Hand of Ragnaros'; + + private const MAX_QUALITY = 50; + private const MIN_QUALITY = 0; + + private const QUALITY_INCREASE = 1; + private const QUALITY_DECREASE = 1; + + private const BACKSTAGE_FIRST_THRESHOLD = 11; + private const BACKSTAGE_SECOND_THRESHOLD = 6; + /** * @var Item[] */ @@ -20,52 +33,101 @@ public function __construct(array $items) public function updateQuality(): void { foreach ($this->items as $item) { - if ($item->name != 'Aged Brie' && $item->name != 'Backstage passes to a TAFKAL80ETC concert') { - if ($item->quality > 0) { - if ($item->name != 'Sulfuras, Hand of Ragnaros') { - $item->quality = $item->quality - 1; - } - } - } else { - if ($item->quality < 50) { - $item->quality = $item->quality + 1; - if ($item->name == 'Backstage passes to a TAFKAL80ETC concert') { - if ($item->sellIn < 11) { - if ($item->quality < 50) { - $item->quality = $item->quality + 1; - } - } - if ($item->sellIn < 6) { - if ($item->quality < 50) { - $item->quality = $item->quality + 1; - } - } - } - } - } - - if ($item->name != 'Sulfuras, Hand of Ragnaros') { - $item->sellIn = $item->sellIn - 1; - } - - if ($item->sellIn < 0) { - if ($item->name != 'Aged Brie') { - if ($item->name != 'Backstage passes to a TAFKAL80ETC concert') { - if ($item->quality > 0) { - if ($item->name != 'Sulfuras, Hand of Ragnaros') { - $item->quality = $item->quality - 1; - } - } - } else { - $item->quality = $item->quality - $item->quality; - } - } else { - if ($item->quality < 50) { - $item->quality = $item->quality + 1; - } - } - } + $this->updateItemQuality($item); } } + + private function updateItemQuality(Item $item): void + { + if ($this->isSulfuras($item)) { + return; + } + + $this->updateQualityBeforeSellIn($item); + $this->decreaseSellIn($item); + $this->updateQualityAfterSellIn($item); + } + + private function updateQualityBeforeSellIn(Item $item): void + { + if ($this->isAgedBrie($item)) { + $this->increaseQuality($item); + } elseif ($this->isBackstagePass($item)) { + $this->updateBackstagePassQuality($item); + } else { + $this->decreaseQuality($item); + } + } + + private function updateQualityAfterSellIn(Item $item): void + { + if ($item->sellIn >= 0) { + return; + } + + if ($this->isAgedBrie($item)) { + $this->increaseQuality($item); + return; + } + + if ($this->isBackstagePass($item)) { + $this->resetQuality($item); + return; + } + + $this->decreaseQuality($item); + } + + private function updateBackstagePassQuality(Item $item): void + { + $this->increaseQuality($item); + + if ($item->sellIn < self::BACKSTAGE_FIRST_THRESHOLD) { + $this->increaseQuality($item); + } + + if ($item->sellIn < self::BACKSTAGE_SECOND_THRESHOLD) { + $this->increaseQuality($item); + } + } + + private function increaseQuality(Item $item): void + { + if ($item->quality < self::MAX_QUALITY) { + $item->quality += self::QUALITY_INCREASE; + } + } + + private function decreaseQuality(Item $item): void + { + if ($item->quality > self::MIN_QUALITY) { + $item->quality -= self::QUALITY_DECREASE; + } + } + + private function decreaseSellIn(Item $item): void + { + $item->sellIn--; + } + + private function resetQuality(Item $item): void + { + $item->quality = self::MIN_QUALITY; + } + + private function isSulfuras(Item $item): bool + { + return $item->name === self::SULFURAS; + } + + private function isAgedBrie(Item $item): bool + { + return $item->name === self::AGED_BRIE; + } + + private function isBackstagePass(Item $item): bool + { + return $item->name === self::BACKSTAGE_PASSES; + } } diff --git a/tests/GildedRoseTest.php b/tests/GildedRoseTest.php new file mode 100644 index 0000000..647ac2a --- /dev/null +++ b/tests/GildedRoseTest.php @@ -0,0 +1,278 @@ +updateQuality(); + + $this->assertSame(19, $items[0]->quality); + $this->assertSame(9, $items[0]->sellIn); + } + + public function testNormalItemQualityDecreasesBy2AfterSellDate(): void + { + $items = [new Item('Normal Item', 0, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(18, $items[0]->quality); + $this->assertSame(-1, $items[0]->sellIn); + } + + public function testNormalItemQualityNeverNegative(): void + { + $items = [new Item('Normal Item', 5, 0)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(0, $items[0]->quality); + $this->assertSame(4, $items[0]->sellIn); + } + + public function testNormalItemQualityDoesNotGoNegativeAfterSellDate(): void + { + $items = [new Item('Normal Item', -1, 1)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(0, $items[0]->quality); + $this->assertSame(-2, $items[0]->sellIn); + } + + // ======================================== + // AGED BRIE TESTS + // ======================================== + + public function testAgedBrieIncreasesInQuality(): void + { + $items = [new Item('Aged Brie', 10, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(21, $items[0]->quality); + $this->assertSame(9, $items[0]->sellIn); + } + + public function testAgedBrieIncreasesBy2AfterSellDate(): void + { + $items = [new Item('Aged Brie', 0, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(22, $items[0]->quality); + $this->assertSame(-1, $items[0]->sellIn); + } + + public function testAgedBrieQualityNeverExceeds50(): void + { + $items = [new Item('Aged Brie', 10, 50)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(50, $items[0]->quality); + $this->assertSame(9, $items[0]->sellIn); + } + + public function testAgedBrieQualityNeverExceeds50AfterSellDate(): void + { + $items = [new Item('Aged Brie', -1, 49)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(50, $items[0]->quality); + $this->assertSame(-2, $items[0]->sellIn); + } + + // ======================================== + // BACKSTAGE PASSES TESTS + // ======================================== + + public function testBackstagePassesIncreaseBy1WhenMoreThan10Days(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 15, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(21, $items[0]->quality); + $this->assertSame(14, $items[0]->sellIn); + } + + public function testBackstagePassesIncreaseBy2When10DaysOrLess(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 10, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(22, $items[0]->quality); + $this->assertSame(9, $items[0]->sellIn); + } + + public function testBackstagePassesIncreaseBy3When5DaysOrLess(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 5, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(23, $items[0]->quality); + $this->assertSame(4, $items[0]->sellIn); + } + + public function testBackstagePassesDropTo0AfterConcert(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 0, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(0, $items[0]->quality); + $this->assertSame(-1, $items[0]->sellIn); + } + + public function testBackstagePassesQualityNeverExceeds50(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 5, 49)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(50, $items[0]->quality); + $this->assertSame(4, $items[0]->sellIn); + } + + // ======================================== + // SULFURAS TESTS + // ======================================== + + public function testSulfurasNeverChangesQuality(): void + { + $items = [new Item('Sulfuras, Hand of Ragnaros', 10, 80)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(80, $items[0]->quality); + $this->assertSame(10, $items[0]->sellIn); + } + + public function testSulfurasNeverChangesSellIn(): void + { + $items = [new Item('Sulfuras, Hand of Ragnaros', 0, 80)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(80, $items[0]->quality); + $this->assertSame(0, $items[0]->sellIn); + } + + public function testSulfurasNeverChangesEvenWithNegativeSellIn(): void + { + $items = [new Item('Sulfuras, Hand of Ragnaros', -1, 80)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(80, $items[0]->quality); + $this->assertSame(-1, $items[0]->sellIn); + } + + // ======================================== + // EDGE CASE TESTS + // ======================================== + + public function testMultipleItemsUpdateCorrectly(): void + { + $items = [ + new Item('Normal Item', 5, 10), + new Item('Aged Brie', 5, 10), + new Item('Sulfuras, Hand of Ragnaros', 5, 80), + new Item('Backstage passes to a TAFKAL80ETC concert', 5, 10), + ]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(9, $items[0]->quality); + $this->assertSame(11, $items[1]->quality); + $this->assertSame(80, $items[2]->quality); + $this->assertSame(13, $items[3]->quality); + } + + public function testMultipleDaysSimulation(): void + { + $items = [new Item('Normal Item', 2, 10)]; + $gildedRose = new GildedRose($items); + + // Day 1: sellIn=1, quality=9 + $gildedRose->updateQuality(); + $this->assertSame(9, $items[0]->quality); + $this->assertSame(1, $items[0]->sellIn); + + // Day 2: sellIn=0, quality=8 + $gildedRose->updateQuality(); + $this->assertSame(8, $items[0]->quality); + $this->assertSame(0, $items[0]->sellIn); + + // Day 3: sellIn=-1, quality=6 (degrades by 2 after sell date) + $gildedRose->updateQuality(); + $this->assertSame(6, $items[0]->quality); + $this->assertSame(-1, $items[0]->sellIn); + } + + public function testBackstagePassesAt6Days(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 6, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(22, $items[0]->quality); + $this->assertSame(5, $items[0]->sellIn); + } + + public function testBackstagePassesAt11Days(): void + { + $items = [new Item('Backstage passes to a TAFKAL80ETC concert', 11, 20)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(21, $items[0]->quality); + $this->assertSame(10, $items[0]->sellIn); + } + + public function testAgedBrieAt49Quality(): void + { + $items = [new Item('Aged Brie', 5, 49)]; + $gildedRose = new GildedRose($items); + + $gildedRose->updateQuality(); + + $this->assertSame(50, $items[0]->quality); + $this->assertSame(4, $items[0]->sellIn); + } +}