From 8e911491f69d36fc7c0004440d905b567480760f Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Sat, 24 Jun 2023 23:33:54 -0300 Subject: [PATCH 1/8] Add tests for Navigation It is responsible for calculating and manipulating Dates in financial organization is a very important feature, and requires significant coverage. The first test case is just to create an understanding of how it works. --- tests/Support/NavigationTest.php | 124 +++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 tests/Support/NavigationTest.php diff --git a/tests/Support/NavigationTest.php b/tests/Support/NavigationTest.php new file mode 100644 index 0000000000..1f4d36a54f --- /dev/null +++ b/tests/Support/NavigationTest.php @@ -0,0 +1,124 @@ +navigation = new Navigation(); + } + + public static function providePeriods(): array + { + return [ + '1D' => ['frequency' => '1D', 'from' => Carbon::now(), 'expected' => Carbon::tomorrow()], + 'daily' => ['frequency' => 'daily', 'from' => Carbon::now(), 'expected' => Carbon::tomorrow()], + '1W' => ['frequency' => '1W', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(1)], + 'weekly' => ['frequency' => 'weekly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(1)], + 'week' => ['frequency' => 'week', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(1)], + '3M' => ['frequency' => '3M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + 'quarter' => ['frequency' => 'quarter', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + 'quarterly' => ['frequency' => 'quarterly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + '6M' => ['frequency' => '6M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'half-year' => ['frequency' => 'half-year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'year' => ['frequency' => 'year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + 'yearly' => ['frequency' => 'yearly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + '1Y' => ['frequency' => '1Y', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + 'last7' => ['frequency' => 'last7', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(7)], + 'last30' => ['frequency' => 'last30', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(1)], + 'last90' => ['frequency' => 'last90', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + 'last365' => ['frequency' => 'last365', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + 'MTD' => ['frequency' => 'MTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(1)], + 'QTD' => ['frequency' => 'QTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + 'YTD' => ['frequency' => 'YTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + ]; + } + + /** + * @dataProvider providePeriods + */ + public function testGivenAFrequencyWhenCalculateTheDateThenReturnsTheExpectedDateSuccessful(string $frequency, Carbon $from, Carbon $expected) + { + $period = $this->navigation->addPeriod($from, $frequency, 0); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } + + public static function provideMonthPeriods(): array + { + return [ + '1M' => ['frequency' => '1M', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(1)], + 'month' => ['frequency' => 'month', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(1)], + 'monthly' => ['frequency' => 'monthly', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(1)], + '2019-01-29 to 2019-02-28' => ['frequency' => 'monthly', 'from' => Carbon::parse('2019-01-29'), 'expected' => Carbon::parse('2019-02-28')], + '2019-01-30 to 2019-02-28' => ['frequency' => 'monthly', 'from' => Carbon::parse('2019-01-30'), 'expected' => Carbon::parse('2019-02-28')], + '2019-01-31 to 2019-02-28' => ['frequency' => 'monthly', 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-02-28')], + '2023-03-31 to 2023-04-30' => ['frequency' => 'monthly', 'from' => Carbon::parse('2023-03-31'), 'expected' => Carbon::parse('2023-04-30')], + '2023-05-31 to 2023-06-30' => ['frequency' => 'monthly', 'from' => Carbon::parse('2023-05-31'), 'expected' => Carbon::parse('2023-06-30')], + '2023-08-31 to 2023-09-30' => ['frequency' => 'monthly', 'from' => Carbon::parse('2023-08-31'), 'expected' => Carbon::parse('2023-09-30')], + '2023-10-31 to 2023-11-30' => ['frequency' => 'monthly', 'from' => Carbon::parse('2023-10-31'), 'expected' => Carbon::parse('2023-11-30')], + ]; + } + + /** + * @dataProvider provideMonthPeriods + */ + public function testGivenAMonthFrequencyWhenCalculateTheDateThenReturnsTheLastDayOfMonthSuccessful(string $frequency, Carbon $from, Carbon $expected) + { + $period = $this->navigation->addPeriod($from, $frequency, 0); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } + + public static function providePeriodsWithSkippingParam(): array + { + return [ + '1D' => ['skip' => 1, 'frequency' => '1D', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(2)], + 'daily' => ['skip' => 1, 'frequency' => 'daily', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(2)], + '1W' => ['skip' => 1, 'frequency' => '1W', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(2)], + 'weekly' => ['skip' => 1, 'frequency' => 'weekly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(2)], + 'week' => ['skip' => 1, 'frequency' => 'week', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(2)], + '1M' => ['skip' => 1, 'frequency' => '1M', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(2)], + 'month' => ['skip' => 1, 'frequency' => 'month', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(2)], + 'monthly' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-06-25'), 'expected' => Carbon::parse('2023-06-25')->addMonths(2)], + '2019-01-29 to 2019-03-29' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2019-01-29'), 'expected' => Carbon::parse('2019-03-29')], + '2019-01-30 to 2019-03-30' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2019-01-30'), 'expected' => Carbon::parse('2019-03-30')], + '2019-01-31 to 2019-03-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-03-31')], + '2023-03-31 to 2023-05-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-03-31'), 'expected' => Carbon::parse('2023-05-31')], + '2023-05-31 to 2023-07-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-05-31'), 'expected' => Carbon::parse('2023-07-31')], + '2023-08-31 to 2023-10-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-08-31'), 'expected' => Carbon::parse('2023-10-31')], + '2023-10-31 to 2023-12-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-10-31'), 'expected' => Carbon::parse('2023-12-31')], + '2023-01-31 to 2023-03-30' => ['skip' => 2, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-01-31'), 'expected' => Carbon::parse('2023-05-1')], + '3M' => ['skip' => 1, 'frequency' => '3M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'quarter' => ['skip' => 1, 'frequency' => 'quarter', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'quarterly' => ['skip' => 1, 'frequency' => 'quarterly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + '6M' => ['skip' => 1, 'frequency' => '6M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(12)], + 'half-year' => ['skip' => 1, 'frequency' => 'half-year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(12)], + 'year' => ['skip' => 1, 'frequency' => 'year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + 'yearly' => ['skip' => 1, 'frequency' => 'yearly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + '1Y' => ['skip' => 1, 'frequency' => '1Y', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + 'last7' => ['skip' => 1, 'frequency' => 'last7', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(14)], + 'last30' => ['skip' => 1, 'frequency' => 'last30', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(2)], + 'last90' => ['skip' => 1, 'frequency' => 'last90', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'last365' => ['skip' => 1, 'frequency' => 'last365', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + 'MTD' => ['skip' => 1, 'frequency' => 'MTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(2)], + 'QTD' => ['skip' => 1, 'frequency' => 'QTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + 'YTD' => ['skip' => 1, 'frequency' => 'YTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + ]; + } + + /** + * @dataProvider providePeriodsWithSkippingParam + */ + public function testGivenAFrequencyWhenCalculateTheDateThenReturnsTheSkippedDateSuccessful(int $skip, string $frequency, Carbon $from, Carbon $expected) + { + $period = $this->navigation->addPeriod($from, $frequency, $skip); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } +} From 4e3c2ba72c330ff2c63677893bac84a7e62b40d7 Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Sun, 2 Jul 2023 23:33:11 -0300 Subject: [PATCH 2/8] Calculate the next date using periodicity strategies. All these strategies encapsulate how the Carbon library adds the interval to the current date. Monthly, Quarterly, Half-Yearly, and Yearly explicitly use the overflow control to guarantee the end of the next month or year adequately. --- app/Support/Calendar/Periodicity.php | 33 ++++++++++++ app/Support/Calendar/Periodicity/Daily.php | 32 ++++++++++++ .../Calendar/Periodicity/Fortnightly.php | 27 ++++++++++ .../Calendar/Periodicity/HalfYearly.php | 27 ++++++++++ .../Calendar/Periodicity/Interspacable.php | 29 +++++++++++ app/Support/Calendar/Periodicity/Interval.php | 32 ++++++++++++ app/Support/Calendar/Periodicity/Monthly.php | 32 ++++++++++++ .../Calendar/Periodicity/Quarterly.php | 27 ++++++++++ app/Support/Calendar/Periodicity/Weekly.php | 32 ++++++++++++ app/Support/Calendar/Periodicity/Yearly.php | 32 ++++++++++++ .../Calendar/Periodicity/DailyTest.php | 42 +++++++++++++++ .../Calendar/Periodicity/FortnightlyTest.php | 42 +++++++++++++++ .../Calendar/Periodicity/HalfYearlyTest.php | 50 ++++++++++++++++++ .../Calendar/Periodicity/IntervalProvider.php | 38 ++++++++++++++ .../Calendar/Periodicity/IntervalTestCase.php | 52 +++++++++++++++++++ .../Calendar/Periodicity/MonthlyTest.php | 52 +++++++++++++++++++ .../Calendar/Periodicity/QuarterlyTest.php | 49 +++++++++++++++++ .../Calendar/Periodicity/WeeklyTest.php | 42 +++++++++++++++ .../Calendar/Periodicity/YearlyTest.php | 43 +++++++++++++++ 19 files changed, 713 insertions(+) create mode 100644 app/Support/Calendar/Periodicity.php create mode 100644 app/Support/Calendar/Periodicity/Daily.php create mode 100644 app/Support/Calendar/Periodicity/Fortnightly.php create mode 100644 app/Support/Calendar/Periodicity/HalfYearly.php create mode 100644 app/Support/Calendar/Periodicity/Interspacable.php create mode 100644 app/Support/Calendar/Periodicity/Interval.php create mode 100644 app/Support/Calendar/Periodicity/Monthly.php create mode 100644 app/Support/Calendar/Periodicity/Quarterly.php create mode 100644 app/Support/Calendar/Periodicity/Weekly.php create mode 100644 app/Support/Calendar/Periodicity/Yearly.php create mode 100644 tests/Support/Calendar/Periodicity/DailyTest.php create mode 100644 tests/Support/Calendar/Periodicity/FortnightlyTest.php create mode 100644 tests/Support/Calendar/Periodicity/HalfYearlyTest.php create mode 100644 tests/Support/Calendar/Periodicity/IntervalProvider.php create mode 100644 tests/Support/Calendar/Periodicity/IntervalTestCase.php create mode 100644 tests/Support/Calendar/Periodicity/MonthlyTest.php create mode 100644 tests/Support/Calendar/Periodicity/QuarterlyTest.php create mode 100644 tests/Support/Calendar/Periodicity/WeeklyTest.php create mode 100644 tests/Support/Calendar/Periodicity/YearlyTest.php diff --git a/app/Support/Calendar/Periodicity.php b/app/Support/Calendar/Periodicity.php new file mode 100644 index 0000000000..05825e19a1 --- /dev/null +++ b/app/Support/Calendar/Periodicity.php @@ -0,0 +1,33 @@ +. + */ + +namespace FireflyIII\Support\Calendar; + +enum Periodicity +{ + case Daily; + case Weekly; + case Fortnightly; + case Monthly; + case Quarterly; + case HalfYearly; + case Yearly; +} diff --git a/app/Support/Calendar/Periodicity/Daily.php b/app/Support/Calendar/Periodicity/Daily.php new file mode 100644 index 0000000000..39fc34196d --- /dev/null +++ b/app/Support/Calendar/Periodicity/Daily.php @@ -0,0 +1,32 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +final class Daily extends Interval +{ + public function nextDate(Carbon $date, int $interval = 1): Carbon + { + return ($date->clone())->addDays($this->skip($interval)); + } +} diff --git a/app/Support/Calendar/Periodicity/Fortnightly.php b/app/Support/Calendar/Periodicity/Fortnightly.php new file mode 100644 index 0000000000..a64880a058 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Fortnightly.php @@ -0,0 +1,27 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +final class Fortnightly extends Weekly +{ + const INTERVAL = 2; +} diff --git a/app/Support/Calendar/Periodicity/HalfYearly.php b/app/Support/Calendar/Periodicity/HalfYearly.php new file mode 100644 index 0000000000..e8d6242bb2 --- /dev/null +++ b/app/Support/Calendar/Periodicity/HalfYearly.php @@ -0,0 +1,27 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +final class HalfYearly extends Monthly +{ + const INTERVAL = 6; +} diff --git a/app/Support/Calendar/Periodicity/Interspacable.php b/app/Support/Calendar/Periodicity/Interspacable.php new file mode 100644 index 0000000000..a10658cdfc --- /dev/null +++ b/app/Support/Calendar/Periodicity/Interspacable.php @@ -0,0 +1,29 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +interface Interspacable +{ + public function nextDate(Carbon $date, int $interval = 1): Carbon; +} diff --git a/app/Support/Calendar/Periodicity/Interval.php b/app/Support/Calendar/Periodicity/Interval.php new file mode 100644 index 0000000000..73db1f0409 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Interval.php @@ -0,0 +1,32 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +abstract class Interval implements Interspacable +{ + const INTERVAL = 1; + + public function skip(int $skip): int + { + return static::INTERVAL * $skip; + } +} diff --git a/app/Support/Calendar/Periodicity/Monthly.php b/app/Support/Calendar/Periodicity/Monthly.php new file mode 100644 index 0000000000..34cc9bf3e6 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Monthly.php @@ -0,0 +1,32 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +class Monthly extends Interval +{ + public function nextDate(Carbon $date, int $interval = 1): Carbon + { + return ($date->clone())->addMonthsNoOverflow($this->skip($interval)); + } +} diff --git a/app/Support/Calendar/Periodicity/Quarterly.php b/app/Support/Calendar/Periodicity/Quarterly.php new file mode 100644 index 0000000000..b20dc60250 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Quarterly.php @@ -0,0 +1,27 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +final class Quarterly extends Monthly +{ + const INTERVAL = 3; +} diff --git a/app/Support/Calendar/Periodicity/Weekly.php b/app/Support/Calendar/Periodicity/Weekly.php new file mode 100644 index 0000000000..519ea143d8 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Weekly.php @@ -0,0 +1,32 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +class Weekly extends Interval +{ + public function nextDate(Carbon $date, int $interval = 1): Carbon + { + return ($date->clone())->addWeeks($this->skip($interval)); + } +} diff --git a/app/Support/Calendar/Periodicity/Yearly.php b/app/Support/Calendar/Periodicity/Yearly.php new file mode 100644 index 0000000000..768f3d87b3 --- /dev/null +++ b/app/Support/Calendar/Periodicity/Yearly.php @@ -0,0 +1,32 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +final class Yearly extends Interval +{ + public function nextDate(Carbon $date, int $interval = 1): Carbon + { + return ($date->clone())->addYearsNoOverflow($this->skip($interval)); + } +} diff --git a/tests/Support/Calendar/Periodicity/DailyTest.php b/tests/Support/Calendar/Periodicity/DailyTest.php new file mode 100644 index 0000000000..b4f25f2e5e --- /dev/null +++ b/tests/Support/Calendar/Periodicity/DailyTest.php @@ -0,0 +1,42 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class DailyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Daily(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::tomorrow()), + new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-01')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/FortnightlyTest.php b/tests/Support/Calendar/Periodicity/FortnightlyTest.php new file mode 100644 index 0000000000..e28b818e92 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/FortnightlyTest.php @@ -0,0 +1,42 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class FortnightlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Fortnightly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addWeeks(2)), + new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-14')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/HalfYearlyTest.php b/tests/Support/Calendar/Periodicity/HalfYearlyTest.php new file mode 100644 index 0000000000..08da7027c7 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/HalfYearlyTest.php @@ -0,0 +1,50 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class HalfYearlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\HalfYearly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(6)), + new IntervalProvider(Carbon::parse('2019-01-29'), Carbon::parse('2019-07-29')), + new IntervalProvider(Carbon::parse('2019-01-30'), Carbon::parse('2019-07-30')), + new IntervalProvider(Carbon::parse('2019-01-31'), Carbon::parse('2019-07-31')), + new IntervalProvider(Carbon::parse('2018-11-01'), Carbon::parse('2019-05-01')), + new IntervalProvider(Carbon::parse('2019-08-29'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2019-08-30'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2019-08-31'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2020-08-29'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2020-08-30'), Carbon::parse('2021-02-28')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/IntervalProvider.php b/tests/Support/Calendar/Periodicity/IntervalProvider.php new file mode 100644 index 0000000000..4725885d26 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/IntervalProvider.php @@ -0,0 +1,38 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; + +readonly class IntervalProvider +{ + public Carbon $epoch; + public Carbon $expected; + public string $label; + + public function __construct(Carbon $epoch, Carbon $expected) + { + $this->epoch = $epoch; + $this->expected = $expected; + $this->label = "given {$epoch->toDateString()} expects {$expected->toDateString()}"; + } +} diff --git a/tests/Support/Calendar/Periodicity/IntervalTestCase.php b/tests/Support/Calendar/Periodicity/IntervalTestCase.php new file mode 100644 index 0000000000..d5db1d901e --- /dev/null +++ b/tests/Support/Calendar/Periodicity/IntervalTestCase.php @@ -0,0 +1,52 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use FireflyIII\Support\Calendar\Periodicity\Interval; +use Tests\TestCase; + +abstract class IntervalTestCase extends TestCase +{ + abstract public static function factory(): Interval; + + public abstract static function provideIntervals(): array; + + public static function provider(): \Generator + { + $intervals = static::provideIntervals(); + /** @var IntervalProvider $interval */ + foreach ($intervals as $interval) { + yield "{$interval->label}" => [$interval]; + } + } + + /** + * @dataProvider provider + * @param IntervalProvider $provider + * @return void + */ + public function testGivenAnEpochWhenCallTheNextDateThenReturnsTheExpectedDateSuccessful(IntervalProvider $provider): void + { + $period = static::factory()->nextDate($provider->epoch); + $this->assertEquals($provider->expected->toDateString(), $period->toDateString()); + } +} diff --git a/tests/Support/Calendar/Periodicity/MonthlyTest.php b/tests/Support/Calendar/Periodicity/MonthlyTest.php new file mode 100644 index 0000000000..fab8785a4a --- /dev/null +++ b/tests/Support/Calendar/Periodicity/MonthlyTest.php @@ -0,0 +1,52 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class MonthlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Monthly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addMonth(1)), + new IntervalProvider(Carbon::parse('2019-01-01'), Carbon::parse('2019-02-01')), + new IntervalProvider(Carbon::parse('2020-01-29'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2020-01-30'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2020-01-31'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2021-01-29'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2021-01-30'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2021-01-31'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2023-03-31'), Carbon::parse('2023-04-30')), + new IntervalProvider(Carbon::parse('2023-05-31'), Carbon::parse('2023-06-30')), + new IntervalProvider(Carbon::parse('2023-08-31'), Carbon::parse('2023-09-30')), + new IntervalProvider(Carbon::parse('2023-10-31'), Carbon::parse('2023-11-30')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/QuarterlyTest.php b/tests/Support/Calendar/Periodicity/QuarterlyTest.php new file mode 100644 index 0000000000..22a527e109 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/QuarterlyTest.php @@ -0,0 +1,49 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class QuarterlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Quarterly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addMonths(3)), + new IntervalProvider(Carbon::parse('2019-01-29'), Carbon::parse('2019-04-29')), + new IntervalProvider(Carbon::parse('2019-01-30'), Carbon::parse('2019-04-30')), + new IntervalProvider(Carbon::parse('2019-01-31'), Carbon::parse('2019-04-30')), + new IntervalProvider(Carbon::parse('2018-11-01'), Carbon::parse('2019-02-01')), + new IntervalProvider(Carbon::parse('2019-11-29'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2019-11-30'), Carbon::parse('2020-02-29')), + new IntervalProvider(Carbon::parse('2020-11-29'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2020-11-30'), Carbon::parse('2021-02-28')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/WeeklyTest.php b/tests/Support/Calendar/Periodicity/WeeklyTest.php new file mode 100644 index 0000000000..b889e977cd --- /dev/null +++ b/tests/Support/Calendar/Periodicity/WeeklyTest.php @@ -0,0 +1,42 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class WeeklyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Weekly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addWeek()), + new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-07')), + ]; + } +} diff --git a/tests/Support/Calendar/Periodicity/YearlyTest.php b/tests/Support/Calendar/Periodicity/YearlyTest.php new file mode 100644 index 0000000000..b7f143fd62 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/YearlyTest.php @@ -0,0 +1,43 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class YearlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Yearly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addYears(1)), + new IntervalProvider(Carbon::parse('2019-01-29'), Carbon::parse('2020-01-29')), + new IntervalProvider(Carbon::parse('2020-02-29'), Carbon::parse('2021-02-28')), + ]; + } +} From dbb7ed3d5dd9b9f09e08237408df810edac07e17 Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Mon, 3 Jul 2023 00:08:14 -0300 Subject: [PATCH 3/8] Add the Calendar Calculator It encapsulates some date operations like sum. The result will be the calculated date when calling the nextDateByInterval method, given the date, periodicity, and skipInterval parameters. For example, given a date of 2019-12-31, monthly periodicity, and skip interval 0, the results will be 2020-01-31. Also, if the skip interval is 1, the result is 2020-02-29. This is because the next date will add another month to the current range. --- app/Support/Calendar/Calculator.php | 81 ++++++++++++ .../Calendar/Exceptions/IntervalException.php | 28 ++++ tests/Support/Calendar/CalculatorProvider.php | 120 ++++++++++++++++++ tests/Support/Calendar/CalculatorTest.php | 97 ++++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 app/Support/Calendar/Calculator.php create mode 100644 app/Support/Calendar/Exceptions/IntervalException.php create mode 100644 tests/Support/Calendar/CalculatorProvider.php create mode 100644 tests/Support/Calendar/CalculatorTest.php diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php new file mode 100644 index 0000000000..a26fb5ea18 --- /dev/null +++ b/app/Support/Calendar/Calculator.php @@ -0,0 +1,81 @@ +. + */ + +namespace FireflyIII\Support\Calendar; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Exceptions\IntervalException; + +class Calculator +{ + const DEFAULT_INTERVAL = 1; + private static array $intervals = []; + private static ?\SplObjectStorage $intervalMap = null; + + private static function loadIntervalMap(): \SplObjectStorage + { + if (self::$intervalMap != null) { + return self::$intervalMap; + } + self::$intervalMap = new \SplObjectStorage(); + foreach (Periodicity::cases() as $interval) { + $periodicityClass = __NAMESPACE__ . "\\Periodicity\\{$interval->name}"; + self::$intervals[] = $interval->name; + self::$intervalMap->attach($interval, new $periodicityClass()); + } + return self::$intervalMap; + } + + private static function containsInterval(Periodicity $periodicity): bool + { + return self::loadIntervalMap()->contains($periodicity); + } + + public function isAvailablePeriodicity(Periodicity $periodicity): bool + { + return self::containsInterval($periodicity); + } + + private function skipInterval(int $skip): int + { + return self::DEFAULT_INTERVAL + $skip; + } + + /** + * @param Carbon $epoch + * @param Periodicity $periodicity + * @param int $skipInterval + * @return Carbon + * @throws IntervalException + */ + public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon + { + if (!self::isAvailablePeriodicity($periodicity)) { + throw IntervalException::unavailable($periodicity, self::$intervals); + } + + /** @var Periodicity\Interval $periodicity */ + $periodicity = self::$intervalMap->offsetGet($periodicity); + $interval = $this->skipInterval($skipInterval); + return $periodicity->nextDate($epoch->clone(), $interval); + } + +} diff --git a/app/Support/Calendar/Exceptions/IntervalException.php b/app/Support/Calendar/Exceptions/IntervalException.php new file mode 100644 index 0000000000..40daccd8f6 --- /dev/null +++ b/app/Support/Calendar/Exceptions/IntervalException.php @@ -0,0 +1,28 @@ +name, + join(', ', $instervals) + ); + + $exception = new IntervalException($message, $code, $previous); + $exception->periodicity = $periodicity; + $exception->availableIntervals = $instervals; + return $exception; + } +} diff --git a/tests/Support/Calendar/CalculatorProvider.php b/tests/Support/Calendar/CalculatorProvider.php new file mode 100644 index 0000000000..dcbad2c8d3 --- /dev/null +++ b/tests/Support/Calendar/CalculatorProvider.php @@ -0,0 +1,120 @@ +. + */ + +namespace Tests\Support\Calendar; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use Tests\Support\Calendar\Periodicity\IntervalProvider; + +readonly class CalculatorProvider +{ + public IntervalProvider $intervalProvider; + public Periodicity $periodicity; + public string $label; + public int $skip; + + private function __construct(IntervalProvider $intervalProvider, Periodicity $periodicity, int $skip = 0) + { + $this->skip = $skip; + $this->intervalProvider = $intervalProvider; + $this->periodicity = $periodicity; + $this->label = "{$periodicity->name} {$intervalProvider->label}"; + } + + public static function from(Periodicity $periodicity, IntervalProvider $interval, int $skip = 0): CalculatorProvider + { + return new self($interval, $periodicity, $skip); + } + + public function epoch(): Carbon + { + return $this->intervalProvider->epoch; + } + + public function expected(): Carbon + { + return $this->intervalProvider->expected; + } + + public static function providePeriodicityWithSkippedIntervals(): \Generator + { + $intervals = [ + CalculatorProvider::from(Periodicity::Daily, new IntervalProvider(Carbon::now(), Carbon::now()->addDays(2)), 1), + CalculatorProvider::from(Periodicity::Daily, new IntervalProvider(Carbon::now(), Carbon::now()->addDays(3)), 2), + CalculatorProvider::from(Periodicity::Daily, new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-11')), 10), + + CalculatorProvider::from(Periodicity::Weekly, new IntervalProvider(Carbon::now(), Carbon::now()->addWeeks(3)), 2), + CalculatorProvider::from(Periodicity::Weekly, new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-14')), 1), + + CalculatorProvider::from(Periodicity::Fortnightly, new IntervalProvider(Carbon::now(), Carbon::now()->addWeeks(4)), 1), + CalculatorProvider::from(Periodicity::Fortnightly, new IntervalProvider(Carbon::parse('2023-01-29'), Carbon::parse('2023-02-26')), 1), + CalculatorProvider::from(Periodicity::Fortnightly, new IntervalProvider(Carbon::parse('2023-01-30'), Carbon::parse('2023-02-27')), 1), + CalculatorProvider::from(Periodicity::Fortnightly, new IntervalProvider(Carbon::parse('2023-01-31'), Carbon::parse('2023-02-28')), 1), + + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(2)), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2019-12-30'), Carbon::parse('2020-02-29')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2019-12-31'), Carbon::parse('2020-02-29')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2020-01-29'), Carbon::parse('2020-03-29')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2020-01-31'), Carbon::parse('2020-09-30')), 7), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2020-12-29'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2020-12-30'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2020-12-31'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-03-31'), Carbon::parse('2023-11-30')), 7), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-05-31'), Carbon::parse('2023-08-31')), 2), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-07-31'), Carbon::parse('2023-09-30')), 1), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-10-30'), Carbon::parse('2024-02-29')), 3), + CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-10-31'), Carbon::parse('2024-02-29')), 3), + + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(9)), 2), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2019-05-29'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2019-05-30'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2019-05-31'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2020-02-29'), Carbon::parse('2021-02-28')), 3), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2020-08-29'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2020-08-30'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2020-08-31'), Carbon::parse('2021-02-28')), 1), + + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(12)), 1), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(18)), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(24)), 3), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2018-08-29'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2018-08-30'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2018-08-31'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2019-01-31'), Carbon::parse('2021-01-31')), 3), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2019-02-28'), Carbon::parse('2021-08-28')), 4), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2020-01-31'), Carbon::parse('2021-01-31')), 1), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2020-02-29'), Carbon::parse('2021-02-28')), 1), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2020-08-29'), Carbon::parse('2022-02-28')), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2020-08-30'), Carbon::parse('2022-02-28')), 2), + CalculatorProvider::from(Periodicity::HalfYearly, new IntervalProvider(Carbon::parse('2020-08-31'), Carbon::parse('2022-02-28')), 2), + + CalculatorProvider::from(Periodicity::Yearly, new IntervalProvider(Carbon::now(), Carbon::now()->addYearsNoOverflow(3)), 2), + CalculatorProvider::from(Periodicity::Yearly, new IntervalProvider(Carbon::parse('2019-01-29'), Carbon::parse('2025-01-29')), 5), + CalculatorProvider::from(Periodicity::Yearly, new IntervalProvider(Carbon::parse('2020-02-29'), Carbon::parse('2031-02-28')), 10), + ]; + + /** @var IntervalProvider $interval */ + foreach ($intervals as $index => $interval) { + yield "#{$index} {$interval->label}" => [$interval]; + } + } +} diff --git a/tests/Support/Calendar/CalculatorTest.php b/tests/Support/Calendar/CalculatorTest.php new file mode 100644 index 0000000000..c4fb07c6e8 --- /dev/null +++ b/tests/Support/Calendar/CalculatorTest.php @@ -0,0 +1,97 @@ +. + */ + +namespace Tests\Support\Calendar; + +use Carbon\Carbon; +use FireflyIII\Api\V1\Controllers\Insight\Income\PeriodController; +use FireflyIII\Support\Calendar\Calculator; +use FireflyIII\Support\Calendar\Exceptions\IntervalException; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Navigation; +use Tests\Support\Calendar\Periodicity\DailyTest; +use Tests\Support\Calendar\Periodicity\FortnightlyTest; +use Tests\Support\Calendar\Periodicity\HalfYearlyTest; +use Tests\Support\Calendar\Periodicity\IntervalProvider; +use Tests\Support\Calendar\Periodicity\MonthlyTest; +use Tests\Support\Calendar\Periodicity\QuarterlyTest; +use Tests\Support\Calendar\Periodicity\WeeklyTest; +use Tests\Support\Calendar\Periodicity\YearlyTest; +use Tests\TestCase; + +class CalculatorTest extends TestCase +{ + private static function convert(Periodicity $periodicity, array $intervals): array + { + $periodicityIntervals = []; + /** @var IntervalProvider $interval */ + foreach ($intervals as $index => $interval) { + $calculator = CalculatorProvider::from($periodicity, $interval); + + $periodicityIntervals["#{$index} {$calculator->label}"] = [$calculator]; + } + return $periodicityIntervals; + } + + public static function provideAllPeriodicity(): \Generator + { + $intervals = []; + $intervals = array_merge($intervals, self::convert(Periodicity::Daily, DailyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Weekly, WeeklyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Fortnightly, FortnightlyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Monthly, MonthlyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Quarterly, QuarterlyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::HalfYearly, HalfYearlyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Yearly, YearlyTest::provideIntervals())); + + /** @var IntervalProvider $interval */ + foreach ($intervals as $label => $interval) { + yield $label => $interval; + } + } + + /** + * @dataProvider provideAllPeriodicity + * @throws IntervalException + */ + public function testGivenADailyPeriodicityWhenCallTheNextDateByIntervalMethodThenReturnsTheExpectedDateSuccessful(CalculatorProvider $provider) + { + $calculator = new Calculator(); + $period = $calculator->nextDateByInterval($provider->epoch(), $provider->periodicity); + $this->assertEquals($provider->expected()->toDateString(), $period->toDateString()); + } + + public static function provideSkippedIntervals(): \Generator + { + return CalculatorProvider::providePeriodicityWithSkippedIntervals(); + } + + /** + * @dataProvider provideSkippedIntervals + * @throws IntervalException + */ + public function testGivenAnEpochWithSkipIntervalNumberWhenCallTheNextDateBySkippedIntervalMethodThenReturnsTheExpectedDateSuccessful(CalculatorProvider $provider) + { + $calculator = new Calculator(); + $period = $calculator->nextDateByInterval($provider->epoch(), $provider->periodicity, $provider->skip); + $this->assertEquals($provider->expected()->toDateString(), $period->toDateString()); + } +} From 563879c218f73717f485e6c794442f5ab72ecb4d Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Mon, 3 Jul 2023 00:59:01 -0300 Subject: [PATCH 4/8] Fix a bug for monthly calculation periodicity This change reveals a bug in the Monthly calculation date where the difference between more than one month was discarded. The new calendar calculator was prepared to avoid overflow at the end of the month. --- app/Support/Navigation.php | 122 +++++++++++++++++-------------- tests/Support/NavigationTest.php | 51 ++++++++++++- 2 files changed, 113 insertions(+), 60 deletions(-) diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 7d5f90b23b..f8e435c675 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -26,6 +26,9 @@ namespace FireflyIII\Support; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; +use FireflyIII\Support\Calendar\Calculator; +use FireflyIII\Support\Calendar\Exceptions\IntervalException; +use FireflyIII\Support\Calendar\Periodicity; use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -35,81 +38,88 @@ use Psr\Container\NotFoundExceptionInterface; */ class Navigation { + private Calculator $calculator; + + public function __construct(Calculator $calculator = null) + { + $this->calculator = ($calculator instanceof Calculator) ?: new Calculator(); + } + + /** + * @param Carbon $epoch + * @param Periodicity $periodicity + * @param int $skipInterval + * @return Carbon + */ + public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon + { + try { + return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); + } catch (IntervalException $exception) { + Log::warning($exception->getMessage(), ['exception' => $exception]); + } catch (\Throwable $exception) { + Log::error($exception->getMessage(), ['exception' => $exception]); + } + + Log::debug( + "Any error occurred to calculate the next date.", + ['date' => $epoch, 'periodicity' => $periodicity->name, 'skipInterval' => $skipInterval] + ); + + return $epoch; + } + /** * @param Carbon $theDate * @param string $repeatFreq * @param int $skip * * @return Carbon + * @deprecated This method will be substituted by nextDateByInterval() */ - public function addPeriod(Carbon $theDate, string $repeatFreq, int $skip): Carbon + public function addPeriod(Carbon $theDate, string $repeatFreq, int $skip = 0): Carbon { $date = clone $theDate; $add = ($skip + 1); $functionMap = [ - '1D' => 'addDays', - 'daily' => 'addDays', - '1W' => 'addWeeks', - 'weekly' => 'addWeeks', - 'week' => 'addWeeks', - '1M' => 'addMonths', - 'month' => 'addMonths', - 'monthly' => 'addMonths', - '3M' => 'addMonths', - 'quarter' => 'addMonths', - 'quarterly' => 'addMonths', - '6M' => 'addMonths', - 'half-year' => 'addMonths', - 'year' => 'addYears', - 'yearly' => 'addYears', - '1Y' => 'addYears', - 'custom' => 'addMonths', // custom? just add one month. + '1D' => Periodicity::Daily, + 'daily' => Periodicity::Daily, + '1W' => Periodicity::Weekly, + 'weekly' => Periodicity::Weekly, + 'week' => Periodicity::Weekly, + '1M' => Periodicity::Monthly, + 'month' => Periodicity::Monthly, + 'monthly' => Periodicity::Monthly, + '3M' => Periodicity::Quarterly, + 'quarter' => Periodicity::Quarterly, + 'quarterly' => Periodicity::Quarterly, + '6M' => Periodicity::HalfYearly, + 'half-year' => Periodicity::HalfYearly, + 'year' => Periodicity::Yearly, + 'yearly' => Periodicity::Yearly, + '1Y' => Periodicity::Yearly, + 'custom' => Periodicity::Monthly, // custom? just add one month. // last X periods? Jump the relevant month / quarter / year - 'last7' => 'addDays', - 'last30' => 'addMonths', - 'last90' => 'addMonths', - 'last365' => 'addYears', - 'MTD' => 'addMonths', - 'QTD' => 'addMonths', - 'YTD' => 'addYears', - ]; - $modifierMap = [ - 'quarter' => 3, - '3M' => 3, - 'quarterly' => 3, - '6M' => 6, - 'half-year' => 6, - 'last7' => 7, - 'last90' => 3, - 'QTD' => 3, + 'last7' => Periodicity::Weekly, + 'last30' => Periodicity::Monthly, + 'last90' => Periodicity::Quarterly, + 'last365' => Periodicity::Yearly, + 'MTD' => Periodicity::Monthly, + 'QTD' => Periodicity::Quarterly, + 'YTD' => Periodicity::Yearly, ]; if (!array_key_exists($repeatFreq, $functionMap)) { - Log::error(sprintf('Cannot do addPeriod for $repeat_freq "%s"', $repeatFreq)); - + Log::error(sprintf( + 'The periodicity %s is unknown. Choose one of available periodicity: %s', + $repeatFreq, + join(', ', array_keys($functionMap)) + )); return $theDate; } - if (array_key_exists($repeatFreq, $modifierMap)) { - $add *= $modifierMap[$repeatFreq]; - } - $function = $functionMap[$repeatFreq]; - $date->$function($add); - // if period is 1M and diff in month is 2 and new DOM > 1, sub a number of days: - // AND skip is 1 - // result is: - // '2019-01-29', '2019-02-28' - // '2019-01-30', '2019-02-28' - // '2019-01-31', '2019-02-28' - - $months = ['1M', 'month', 'monthly']; - $difference = $date->month - $theDate->month; - if (1 === $add && 2 === $difference && $date->day > 0 && in_array($repeatFreq, $months, true)) { - $date->subDays($date->day); - } - - return $date; + return $this->nextDateByInterval($theDate, $functionMap[$repeatFreq], $skip); } /** diff --git a/tests/Support/NavigationTest.php b/tests/Support/NavigationTest.php index 1f4d36a54f..5ffdd0dd10 100644 --- a/tests/Support/NavigationTest.php +++ b/tests/Support/NavigationTest.php @@ -3,6 +3,7 @@ namespace Tests\Support; use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Navigation; use Tests\TestCase; @@ -76,9 +77,10 @@ class NavigationTest extends TestCase $this->assertEquals($expected->toDateString(), $period->toDateString()); } - public static function providePeriodsWithSkippingParam(): array + public static function providePeriodsWithSkippingParam(): \Generator { - return [ + $intervals = [ + '2019-01-31 to 2019-02-11' => ['skip' => 10, 'frequency' => 'daily', 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-02-11')], '1D' => ['skip' => 1, 'frequency' => '1D', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(2)], 'daily' => ['skip' => 1, 'frequency' => 'daily', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(2)], '1W' => ['skip' => 1, 'frequency' => '1W', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(2)], @@ -94,7 +96,7 @@ class NavigationTest extends TestCase '2023-05-31 to 2023-07-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-05-31'), 'expected' => Carbon::parse('2023-07-31')], '2023-08-31 to 2023-10-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-08-31'), 'expected' => Carbon::parse('2023-10-31')], '2023-10-31 to 2023-12-31' => ['skip' => 1, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-10-31'), 'expected' => Carbon::parse('2023-12-31')], - '2023-01-31 to 2023-03-30' => ['skip' => 2, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-01-31'), 'expected' => Carbon::parse('2023-05-1')], + '2023-01-31 to 2023-03-30' => ['skip' => 2, 'frequency' => 'monthly', 'from' => Carbon::parse('2023-01-31'), 'expected' => Carbon::parse('2023-04-30')], '3M' => ['skip' => 1, 'frequency' => '3M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], 'quarter' => ['skip' => 1, 'frequency' => 'quarter', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], 'quarterly' => ['skip' => 1, 'frequency' => 'quarterly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], @@ -103,6 +105,7 @@ class NavigationTest extends TestCase 'year' => ['skip' => 1, 'frequency' => 'year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], 'yearly' => ['skip' => 1, 'frequency' => 'yearly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], '1Y' => ['skip' => 1, 'frequency' => '1Y', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], + '2023-02-01 to 2023-02-15' => ['skip' => 1, 'frequency' => 'last7', 'from' => Carbon::parse('2023-02-01'), 'expected' => Carbon::parse('2023-02-15')], 'last7' => ['skip' => 1, 'frequency' => 'last7', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(14)], 'last30' => ['skip' => 1, 'frequency' => 'last30', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(2)], 'last90' => ['skip' => 1, 'frequency' => 'last90', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], @@ -111,14 +114,54 @@ class NavigationTest extends TestCase 'QTD' => ['skip' => 1, 'frequency' => 'QTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], 'YTD' => ['skip' => 1, 'frequency' => 'YTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(2)], ]; + foreach ($intervals as $interval) { + yield "{$interval["frequency"]} {$interval["from"]->toDateString()} to {$interval["expected"]->toDateString()}" => $interval; + } } /** * @dataProvider providePeriodsWithSkippingParam */ - public function testGivenAFrequencyWhenCalculateTheDateThenReturnsTheSkippedDateSuccessful(int $skip, string $frequency, Carbon $from, Carbon $expected) + public function testGivenAFrequencyAndSkipIntervalWhenCalculateTheDateThenReturnsTheSkippedDateSuccessful(int $skip, string $frequency, Carbon $from, Carbon $expected) { $period = $this->navigation->addPeriod($from, $frequency, $skip); $this->assertEquals($expected->toDateString(), $period->toDateString()); } + + public static function provideFrequencies(): array + { + return [ + Periodicity::Daily->name => ['periodicity' => Periodicity::Daily, 'from' => Carbon::now(), 'expected' => Carbon::tomorrow()], + Periodicity::Weekly->name => ['periodicity' => Periodicity::Weekly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(1)], + Periodicity::Fortnightly->name => ['periodicity' => Periodicity::Fortnightly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeeks(2)], + Periodicity::Monthly->name => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(1)], + '2019-01-01 to 2019-02-01' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2019-01-01'), 'expected' => Carbon::parse('2019-02-01')], + '2019-01-29 to 2019-02-28' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2019-01-29'), 'expected' => Carbon::parse('2019-02-28')], + '2019-01-30 to 2019-02-28' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2019-01-30'), 'expected' => Carbon::parse('2019-02-28')], + '2019-01-31 to 2019-02-28' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-02-28')], + '2023-03-31 to 2023-04-30' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2023-03-31'), 'expected' => Carbon::parse('2023-04-30')], + '2023-05-31 to 2023-06-30' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2023-05-31'), 'expected' => Carbon::parse('2023-06-30')], + '2023-08-31 to 2023-09-30' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2023-08-31'), 'expected' => Carbon::parse('2023-09-30')], + '2023-10-31 to 2023-11-30' => ['periodicity' => Periodicity::Monthly, 'from' => Carbon::parse('2023-10-31'), 'expected' => Carbon::parse('2023-11-30')], + Periodicity::Quarterly->name => ['periodicity' => Periodicity::Quarterly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(3)], + '2019-01-29 to 2020-04-29' => ['periodicity' => Periodicity::Quarterly, 'from' => Carbon::parse('2019-01-29'), 'expected' => Carbon::parse('2019-04-29')], + '2019-01-30 to 2020-04-30' => ['periodicity' => Periodicity::Quarterly, 'from' => Carbon::parse('2019-01-30'), 'expected' => Carbon::parse('2019-04-30')], + '2019-01-31 to 2020-04-30' => ['periodicity' => Periodicity::Quarterly, 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-04-30')], + Periodicity::HalfYearly->name => ['periodicity' => Periodicity::HalfYearly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonths(6)], + '2019-01-31 to 2020-07-29' => ['periodicity' => Periodicity::HalfYearly, 'from' => Carbon::parse('2019-01-29'), 'expected' => Carbon::parse('2019-07-29')], + '2019-01-31 to 2020-07-30' => ['periodicity' => Periodicity::HalfYearly, 'from' => Carbon::parse('2019-01-30'), 'expected' => Carbon::parse('2019-07-30')], + '2019-01-31 to 2020-07-31' => ['periodicity' => Periodicity::HalfYearly, 'from' => Carbon::parse('2019-01-31'), 'expected' => Carbon::parse('2019-07-31')], + Periodicity::Yearly->name => ['periodicity' => Periodicity::Yearly, 'from' => Carbon::now(), 'expected' => Carbon::now()->addYears(1)], + '2020-02-29 to 2021-02-28' => ['periodicity' => Periodicity::Yearly, 'from' => Carbon::parse('2020-02-29'), 'expected' => Carbon::parse('2021-02-28')], + ]; + } + + /** + * @dataProvider provideFrequencies + */ + public function testGivenAIntervalWhenCallTheNextDateByIntervalMethodThenReturnsTheExpectedDateSuccessful(Periodicity $periodicity, Carbon $from, Carbon $expected) + { + $period = $this->navigation->nextDateByInterval($from, $periodicity); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } } From 6ac3cc384bcc4881ace96902c04801f7b87d1cf3 Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Mon, 3 Jul 2023 10:36:56 -0300 Subject: [PATCH 5/8] Add Bimonthly periodicity for Support\Calendar --- app/Support/Calendar/Periodicity.php | 1 + .../Calendar/Periodicity/Bimonthly.php | 27 ++++++++++ tests/Support/Calendar/CalculatorProvider.php | 11 ++++ tests/Support/Calendar/CalculatorTest.php | 2 + .../Calendar/Periodicity/BimonthlyTest.php | 50 +++++++++++++++++++ 5 files changed, 91 insertions(+) create mode 100644 app/Support/Calendar/Periodicity/Bimonthly.php create mode 100644 tests/Support/Calendar/Periodicity/BimonthlyTest.php diff --git a/app/Support/Calendar/Periodicity.php b/app/Support/Calendar/Periodicity.php index 05825e19a1..75c492bb13 100644 --- a/app/Support/Calendar/Periodicity.php +++ b/app/Support/Calendar/Periodicity.php @@ -27,6 +27,7 @@ enum Periodicity case Weekly; case Fortnightly; case Monthly; + case Bimonthly; case Quarterly; case HalfYearly; case Yearly; diff --git a/app/Support/Calendar/Periodicity/Bimonthly.php b/app/Support/Calendar/Periodicity/Bimonthly.php new file mode 100644 index 0000000000..a8aac196fa --- /dev/null +++ b/app/Support/Calendar/Periodicity/Bimonthly.php @@ -0,0 +1,27 @@ +. + */ + +namespace FireflyIII\Support\Calendar\Periodicity; + +final class Bimonthly extends Monthly +{ + const INTERVAL = 2; +} diff --git a/tests/Support/Calendar/CalculatorProvider.php b/tests/Support/Calendar/CalculatorProvider.php index dcbad2c8d3..20e6172e1e 100644 --- a/tests/Support/Calendar/CalculatorProvider.php +++ b/tests/Support/Calendar/CalculatorProvider.php @@ -84,6 +84,17 @@ readonly class CalculatorProvider CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-10-30'), Carbon::parse('2024-02-29')), 3), CalculatorProvider::from(Periodicity::Monthly, new IntervalProvider(Carbon::parse('2023-10-31'), Carbon::parse('2024-02-29')), 3), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(6)), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2019-08-29'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2019-08-30'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2019-08-31'), Carbon::parse('2020-02-29')), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2019-10-30'), Carbon::parse('2020-02-29')), 1), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2019-10-31'), Carbon::parse('2020-02-29')), 1), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2020-02-29'), Carbon::parse('2021-02-28')), 5), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2020-08-29'), Carbon::parse('2021-02-28')), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2020-08-30'), Carbon::parse('2021-02-28')), 2), + CalculatorProvider::from(Periodicity::Bimonthly, new IntervalProvider(Carbon::parse('2020-08-31'), Carbon::parse('2021-02-28')), 2), + CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::now(), Carbon::now()->addMonthsNoOverflow(9)), 2), CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2019-05-29'), Carbon::parse('2020-02-29')), 2), CalculatorProvider::from(Periodicity::Quarterly, new IntervalProvider(Carbon::parse('2019-05-30'), Carbon::parse('2020-02-29')), 2), diff --git a/tests/Support/Calendar/CalculatorTest.php b/tests/Support/Calendar/CalculatorTest.php index c4fb07c6e8..d3a05fe0cf 100644 --- a/tests/Support/Calendar/CalculatorTest.php +++ b/tests/Support/Calendar/CalculatorTest.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Calendar\Calculator; use FireflyIII\Support\Calendar\Exceptions\IntervalException; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Navigation; +use Tests\Support\Calendar\Periodicity\BimonthlyTest; use Tests\Support\Calendar\Periodicity\DailyTest; use Tests\Support\Calendar\Periodicity\FortnightlyTest; use Tests\Support\Calendar\Periodicity\HalfYearlyTest; @@ -58,6 +59,7 @@ class CalculatorTest extends TestCase $intervals = array_merge($intervals, self::convert(Periodicity::Weekly, WeeklyTest::provideIntervals())); $intervals = array_merge($intervals, self::convert(Periodicity::Fortnightly, FortnightlyTest::provideIntervals())); $intervals = array_merge($intervals, self::convert(Periodicity::Monthly, MonthlyTest::provideIntervals())); + $intervals = array_merge($intervals, self::convert(Periodicity::Bimonthly, BimonthlyTest::provideIntervals())); $intervals = array_merge($intervals, self::convert(Periodicity::Quarterly, QuarterlyTest::provideIntervals())); $intervals = array_merge($intervals, self::convert(Periodicity::HalfYearly, HalfYearlyTest::provideIntervals())); $intervals = array_merge($intervals, self::convert(Periodicity::Yearly, YearlyTest::provideIntervals())); diff --git a/tests/Support/Calendar/Periodicity/BimonthlyTest.php b/tests/Support/Calendar/Periodicity/BimonthlyTest.php new file mode 100644 index 0000000000..90c46c59b5 --- /dev/null +++ b/tests/Support/Calendar/Periodicity/BimonthlyTest.php @@ -0,0 +1,50 @@ +. + */ + +namespace Tests\Support\Calendar\Periodicity; + +use Carbon\Carbon; +use FireflyIII\Support\Calendar\Periodicity; +use FireflyIII\Support\Calendar\Periodicity\Interval; + +class BimonthlyTest extends IntervalTestCase +{ + public static function factory(): Interval + { + return new Periodicity\Bimonthly(); + } + + public static function provideIntervals(): array + { + return [ + new IntervalProvider(Carbon::now(), Carbon::now()->addMonths(2)), + new IntervalProvider(Carbon::parse('2019-01-29'), Carbon::parse('2019-03-29')), + new IntervalProvider(Carbon::parse('2018-12-30'), Carbon::parse('2019-02-28')), + new IntervalProvider(Carbon::parse('2018-12-31'), Carbon::parse('2019-02-28')), + new IntervalProvider(Carbon::parse('2018-11-01'), Carbon::parse('2019-01-01')), + new IntervalProvider(Carbon::parse('2019-11-29'), Carbon::parse('2020-01-29')), + new IntervalProvider(Carbon::parse('2019-11-30'), Carbon::parse('2020-01-30')), + new IntervalProvider(Carbon::parse('2020-12-29'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2020-12-30'), Carbon::parse('2021-02-28')), + new IntervalProvider(Carbon::parse('2020-12-31'), Carbon::parse('2021-02-28')), + ]; + } +} From 7f0db0de0436bcd508e31434b8dc64a4bbddef3f Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Mon, 3 Jul 2023 11:08:24 -0300 Subject: [PATCH 6/8] Organize test suites into unit and integration This is the goals of project organization composing different combinations to run any number of tests together. --- composer.json | 6 ++++ phpunit.xml | 7 +++-- .../Autocomplete/AccountControllerTest.php | 4 +-- .../{ => integration}/CreatesApplication.php | 2 +- tests/{ => integration}/TestCase.php | 4 +-- .../Traits/CollectsValues.php | 2 +- .../Support/Calendar/CalculatorProvider.php | 4 +-- .../Support/Calendar/CalculatorTest.php | 31 ++++++++++--------- .../Calendar/Periodicity/BimonthlyTest.php | 8 ++++- .../Calendar/Periodicity/DailyTest.php | 8 ++++- .../Calendar/Periodicity/FortnightlyTest.php | 8 ++++- .../Calendar/Periodicity/HalfYearlyTest.php | 8 ++++- .../Calendar/Periodicity/IntervalProvider.php | 2 +- .../Calendar/Periodicity/IntervalTestCase.php | 4 +-- .../Calendar/Periodicity/MonthlyTest.php | 8 ++++- .../Calendar/Periodicity/QuarterlyTest.php | 8 ++++- .../Calendar/Periodicity/WeeklyTest.php | 8 ++++- .../Calendar/Periodicity/YearlyTest.php | 8 ++++- tests/{ => unit}/Support/NavigationTest.php | 9 ++++-- 19 files changed, 102 insertions(+), 37 deletions(-) rename tests/{ => integration}/Api/Autocomplete/AccountControllerTest.php (95%) rename tests/{ => integration}/CreatesApplication.php (97%) rename tests/{ => integration}/TestCase.php (95%) rename tests/{ => integration}/Traits/CollectsValues.php (96%) rename tests/{ => unit}/Support/Calendar/CalculatorProvider.php (99%) rename tests/{ => unit}/Support/Calendar/CalculatorTest.php (84%) rename tests/{ => unit}/Support/Calendar/Periodicity/BimonthlyTest.php (93%) rename tests/{ => unit}/Support/Calendar/Periodicity/DailyTest.php (90%) rename tests/{ => unit}/Support/Calendar/Periodicity/FortnightlyTest.php (90%) rename tests/{ => unit}/Support/Calendar/Periodicity/HalfYearlyTest.php (93%) rename tests/{ => unit}/Support/Calendar/Periodicity/IntervalProvider.php (95%) rename tests/{ => unit}/Support/Calendar/Periodicity/IntervalTestCase.php (95%) rename tests/{ => unit}/Support/Calendar/Periodicity/MonthlyTest.php (94%) rename tests/{ => unit}/Support/Calendar/Periodicity/QuarterlyTest.php (93%) rename tests/{ => unit}/Support/Calendar/Periodicity/WeeklyTest.php (90%) rename tests/{ => unit}/Support/Calendar/Periodicity/YearlyTest.php (91%) rename tests/{ => unit}/Support/NavigationTest.php (99%) diff --git a/composer.json b/composer.json index b05a6a12c4..0b8810c515 100644 --- a/composer.json +++ b/composer.json @@ -168,6 +168,12 @@ "post-install-cmd": [ "@php artisan firefly:instructions install", "@php artisan firefly-iii:verify-security-alerts" + ], + "unit-test": [ + "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit" + ], + "integration-test": [ + "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration" ] }, "config": { diff --git a/phpunit.xml b/phpunit.xml index bdcc61af15..575dac224f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,8 +21,11 @@ - - ./tests + + ./tests/unit + + + ./tests/integration diff --git a/tests/Api/Autocomplete/AccountControllerTest.php b/tests/integration/Api/Autocomplete/AccountControllerTest.php similarity index 95% rename from tests/Api/Autocomplete/AccountControllerTest.php rename to tests/integration/Api/Autocomplete/AccountControllerTest.php index 7ff8e51672..5eb0ec0ca7 100644 --- a/tests/Api/Autocomplete/AccountControllerTest.php +++ b/tests/integration/Api/Autocomplete/AccountControllerTest.php @@ -21,11 +21,11 @@ declare(strict_types=1); -namespace Tests\Api\Autocomplete; +namespace Tests\integration\Api\Autocomplete; use Laravel\Passport\Passport; use Log; -use Tests\TestCase; +use Tests\integration\TestCase; /** * Class AccountControllerTest diff --git a/tests/CreatesApplication.php b/tests/integration/CreatesApplication.php similarity index 97% rename from tests/CreatesApplication.php rename to tests/integration/CreatesApplication.php index 22211a5905..1900ee8eb1 100644 --- a/tests/CreatesApplication.php +++ b/tests/integration/CreatesApplication.php @@ -20,7 +20,7 @@ */ declare(strict_types=1); -namespace Tests; +namespace Tests\integration; use Illuminate\Contracts\Console\Kernel; use Illuminate\Foundation\Application; diff --git a/tests/TestCase.php b/tests/integration/TestCase.php similarity index 95% rename from tests/TestCase.php rename to tests/integration/TestCase.php index 4e53124582..e0990a65b6 100644 --- a/tests/TestCase.php +++ b/tests/integration/TestCase.php @@ -21,10 +21,10 @@ */ declare(strict_types=1); -namespace Tests; +namespace Tests\integration; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; -use Tests\Traits\CollectsValues; +use Tests\integration\Traits\CollectsValues; /** * Class TestCase diff --git a/tests/Traits/CollectsValues.php b/tests/integration/Traits/CollectsValues.php similarity index 96% rename from tests/Traits/CollectsValues.php rename to tests/integration/Traits/CollectsValues.php index 33c6bfe647..cae0305ff3 100644 --- a/tests/Traits/CollectsValues.php +++ b/tests/integration/Traits/CollectsValues.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace Tests\Traits; +namespace Tests\integration\Traits; use FireflyIII\User; diff --git a/tests/Support/Calendar/CalculatorProvider.php b/tests/unit/Support/Calendar/CalculatorProvider.php similarity index 99% rename from tests/Support/Calendar/CalculatorProvider.php rename to tests/unit/Support/Calendar/CalculatorProvider.php index 20e6172e1e..12f23d7363 100644 --- a/tests/Support/Calendar/CalculatorProvider.php +++ b/tests/unit/Support/Calendar/CalculatorProvider.php @@ -19,11 +19,11 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar; +namespace Tests\unit\Support\Calendar; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; -use Tests\Support\Calendar\Periodicity\IntervalProvider; +use Tests\unit\Support\Calendar\Periodicity\IntervalProvider; readonly class CalculatorProvider { diff --git a/tests/Support/Calendar/CalculatorTest.php b/tests/unit/Support/Calendar/CalculatorTest.php similarity index 84% rename from tests/Support/Calendar/CalculatorTest.php rename to tests/unit/Support/Calendar/CalculatorTest.php index d3a05fe0cf..a5c8162a01 100644 --- a/tests/Support/Calendar/CalculatorTest.php +++ b/tests/unit/Support/Calendar/CalculatorTest.php @@ -19,25 +19,28 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar; +namespace Tests\unit\Support\Calendar; -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Insight\Income\PeriodController; use FireflyIII\Support\Calendar\Calculator; use FireflyIII\Support\Calendar\Exceptions\IntervalException; use FireflyIII\Support\Calendar\Periodicity; -use FireflyIII\Support\Navigation; -use Tests\Support\Calendar\Periodicity\BimonthlyTest; -use Tests\Support\Calendar\Periodicity\DailyTest; -use Tests\Support\Calendar\Periodicity\FortnightlyTest; -use Tests\Support\Calendar\Periodicity\HalfYearlyTest; -use Tests\Support\Calendar\Periodicity\IntervalProvider; -use Tests\Support\Calendar\Periodicity\MonthlyTest; -use Tests\Support\Calendar\Periodicity\QuarterlyTest; -use Tests\Support\Calendar\Periodicity\WeeklyTest; -use Tests\Support\Calendar\Periodicity\YearlyTest; -use Tests\TestCase; +use PHPUnit\Framework\TestCase; +use Tests\unit\Support\Calendar\Periodicity\BimonthlyTest; +use Tests\unit\Support\Calendar\Periodicity\DailyTest; +use Tests\unit\Support\Calendar\Periodicity\FortnightlyTest; +use Tests\unit\Support\Calendar\Periodicity\HalfYearlyTest; +use Tests\unit\Support\Calendar\Periodicity\IntervalProvider; +use Tests\unit\Support\Calendar\Periodicity\MonthlyTest; +use Tests\unit\Support\Calendar\Periodicity\QuarterlyTest; +use Tests\unit\Support\Calendar\Periodicity\WeeklyTest; +use Tests\unit\Support\Calendar\Periodicity\YearlyTest; +/** + * @group unit-test + * @group support + * @group calendar + * @group calculator + */ class CalculatorTest extends TestCase { private static function convert(Periodicity $periodicity, array $intervals): array diff --git a/tests/Support/Calendar/Periodicity/BimonthlyTest.php b/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php similarity index 93% rename from tests/Support/Calendar/Periodicity/BimonthlyTest.php rename to tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php index 90c46c59b5..e44b188e33 100644 --- a/tests/Support/Calendar/Periodicity/BimonthlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class BimonthlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/DailyTest.php b/tests/unit/Support/Calendar/Periodicity/DailyTest.php similarity index 90% rename from tests/Support/Calendar/Periodicity/DailyTest.php rename to tests/unit/Support/Calendar/Periodicity/DailyTest.php index b4f25f2e5e..56169a646b 100644 --- a/tests/Support/Calendar/Periodicity/DailyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/DailyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class DailyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/FortnightlyTest.php b/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php similarity index 90% rename from tests/Support/Calendar/Periodicity/FortnightlyTest.php rename to tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php index e28b818e92..abc63993ec 100644 --- a/tests/Support/Calendar/Periodicity/FortnightlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class FortnightlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/HalfYearlyTest.php b/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php similarity index 93% rename from tests/Support/Calendar/Periodicity/HalfYearlyTest.php rename to tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php index 08da7027c7..fa15fbfc35 100644 --- a/tests/Support/Calendar/Periodicity/HalfYearlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class HalfYearlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/IntervalProvider.php b/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php similarity index 95% rename from tests/Support/Calendar/Periodicity/IntervalProvider.php rename to tests/unit/Support/Calendar/Periodicity/IntervalProvider.php index 4725885d26..2c62f5802f 100644 --- a/tests/Support/Calendar/Periodicity/IntervalProvider.php +++ b/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; diff --git a/tests/Support/Calendar/Periodicity/IntervalTestCase.php b/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php similarity index 95% rename from tests/Support/Calendar/Periodicity/IntervalTestCase.php rename to tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php index d5db1d901e..a0cf5bb124 100644 --- a/tests/Support/Calendar/Periodicity/IntervalTestCase.php +++ b/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php @@ -19,10 +19,10 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; -use Tests\TestCase; +use PHPUnit\Framework\TestCase; abstract class IntervalTestCase extends TestCase { diff --git a/tests/Support/Calendar/Periodicity/MonthlyTest.php b/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php similarity index 94% rename from tests/Support/Calendar/Periodicity/MonthlyTest.php rename to tests/unit/Support/Calendar/Periodicity/MonthlyTest.php index fab8785a4a..a9fe75d15d 100644 --- a/tests/Support/Calendar/Periodicity/MonthlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class MonthlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/QuarterlyTest.php b/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php similarity index 93% rename from tests/Support/Calendar/Periodicity/QuarterlyTest.php rename to tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php index 22a527e109..093276bcb1 100644 --- a/tests/Support/Calendar/Periodicity/QuarterlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class QuarterlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/WeeklyTest.php b/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php similarity index 90% rename from tests/Support/Calendar/Periodicity/WeeklyTest.php rename to tests/unit/Support/Calendar/Periodicity/WeeklyTest.php index b889e977cd..b2fa868518 100644 --- a/tests/Support/Calendar/Periodicity/WeeklyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class WeeklyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/Calendar/Periodicity/YearlyTest.php b/tests/unit/Support/Calendar/Periodicity/YearlyTest.php similarity index 91% rename from tests/Support/Calendar/Periodicity/YearlyTest.php rename to tests/unit/Support/Calendar/Periodicity/YearlyTest.php index b7f143fd62..fb8e70d9d1 100644 --- a/tests/Support/Calendar/Periodicity/YearlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/YearlyTest.php @@ -19,12 +19,18 @@ * along with this program. If not, see . */ -namespace Tests\Support\Calendar\Periodicity; +namespace Tests\unit\Support\Calendar\Periodicity; use Carbon\Carbon; use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity\Interval; +/** + * @group unit-test + * @group support + * @group calendar + * @group periodicity + */ class YearlyTest extends IntervalTestCase { public static function factory(): Interval diff --git a/tests/Support/NavigationTest.php b/tests/unit/Support/NavigationTest.php similarity index 99% rename from tests/Support/NavigationTest.php rename to tests/unit/Support/NavigationTest.php index 5ffdd0dd10..6ad2a395dd 100644 --- a/tests/Support/NavigationTest.php +++ b/tests/unit/Support/NavigationTest.php @@ -1,12 +1,17 @@ Date: Mon, 3 Jul 2023 11:37:11 -0300 Subject: [PATCH 7/8] Send code coverage to SonarCloud --- .github/workflows/sonarcloud.yml | 28 +++++++++++ .gitignore | 2 +- composer.json | 7 ++- phpunit.xml | 63 ++++++++++++++++-------- sonar-project.properties | 1 + tests/integration/CreatesApplication.php | 2 +- 6 files changed, 78 insertions(+), 25 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index a1a34fa24f..e9ba1b9b27 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,5 +1,6 @@ name: Sonarcloud on: + pull_request: push: branches: - main @@ -12,6 +13,33 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup PHP with Xdebug + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: xdebug + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-interaction --no-progress --no-scripts + + - name: Copy environment file + run: cp .env.example .env + + - name: Generate app key + run: php artisan key:generate + + - name: "Run tests with coverage" + run: composer coverage + + - name: Fix code coverage paths + run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage.xml + - name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@master env: diff --git a/.gitignore b/.gitignore index 220b48d300..ab681f4f44 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ npm-debug.log yarn-error.log .env /.ci/php-cs-fixer/vendor -/.ci/coverage +coverage.xml diff --git a/composer.json b/composer.json index 0b8810c515..1c6c62f1ba 100644 --- a/composer.json +++ b/composer.json @@ -170,10 +170,13 @@ "@php artisan firefly-iii:verify-security-alerts" ], "unit-test": [ - "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit" + "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit --no-coverage" ], "integration-test": [ - "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration" + "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration --no-coverage" + ], + "coverage": [ + "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit" ] }, "config": { diff --git a/phpunit.xml b/phpunit.xml index 575dac224f..aa51a02e21 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,25 +18,46 @@ ~ You should have received a copy of the GNU Affero General Public License ~ along with this program. If not, see . --> - - - - - ./tests/unit - - - ./tests/integration - - - - - - - - - - - ./app - - + + + + + + + + + + + ./tests + + + ./tests/unit + + + ./tests/integration + + + + + app + + + + + + + diff --git a/sonar-project.properties b/sonar-project.properties index 72777406a0..30a426dd31 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -15,3 +15,4 @@ sonar.organization=firefly-iii sonar.projectVersion=6.0.11 sonar.sources=app,bootstrap,database,resources/assets,resources/views,routes,tests sonar.sourceEncoding=UTF-8 +sonar.php.coverage.reportPaths=coverage.xml diff --git a/tests/integration/CreatesApplication.php b/tests/integration/CreatesApplication.php index 1900ee8eb1..6dbc977700 100644 --- a/tests/integration/CreatesApplication.php +++ b/tests/integration/CreatesApplication.php @@ -38,7 +38,7 @@ trait CreatesApplication */ public function createApplication() { - $app = require __DIR__ . '/../bootstrap/app.php'; + $app = require_once __DIR__ . '/../../bootstrap/app.php'; $app->make(Kernel::class)->bootstrap(); From 720fff4595da1f8c07f743cb5aba86b3b2e31dda Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Tue, 4 Jul 2023 06:23:59 -0300 Subject: [PATCH 8/8] Fix typo, remove unused class and change the copyright author --- app/Support/Calendar/Calculator.php | 2 +- .../Calendar/Exceptions/IntervalException.php | 31 ++++++++++++++++--- app/Support/Calendar/Periodicity.php | 2 +- .../Calendar/Periodicity/Bimonthly.php | 2 +- app/Support/Calendar/Periodicity/Daily.php | 2 +- .../Calendar/Periodicity/Fortnightly.php | 2 +- .../Calendar/Periodicity/HalfYearly.php | 2 +- .../Calendar/Periodicity/Interspacable.php | 2 +- app/Support/Calendar/Periodicity/Interval.php | 2 +- app/Support/Calendar/Periodicity/Monthly.php | 2 +- .../Calendar/Periodicity/Quarterly.php | 2 +- app/Support/Calendar/Periodicity/Weekly.php | 2 +- app/Support/Calendar/Periodicity/Yearly.php | 2 +- .../Support/Calendar/CalculatorProvider.php | 2 +- .../unit/Support/Calendar/CalculatorTest.php | 2 +- .../Calendar/Periodicity/BimonthlyTest.php | 2 +- .../Calendar/Periodicity/DailyTest.php | 2 +- .../Calendar/Periodicity/FortnightlyTest.php | 2 +- .../Calendar/Periodicity/HalfYearlyTest.php | 2 +- .../Calendar/Periodicity/IntervalProvider.php | 2 +- .../Calendar/Periodicity/IntervalTestCase.php | 2 +- .../Calendar/Periodicity/MonthlyTest.php | 2 +- .../Calendar/Periodicity/QuarterlyTest.php | 2 +- .../Calendar/Periodicity/WeeklyTest.php | 2 +- .../Calendar/Periodicity/YearlyTest.php | 2 +- 25 files changed, 51 insertions(+), 28 deletions(-) diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index a26fb5ea18..b4475c7df7 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -1,7 +1,7 @@ . + */ + namespace FireflyIII\Support\Calendar\Exceptions; use FireflyIII\Support\Calendar\Periodicity; -use JetBrains\PhpStorm\Pure; final class IntervalException extends \Exception { @@ -12,17 +30,22 @@ final class IntervalException extends \Exception public readonly Periodicity $periodicity; public readonly array $availableIntervals; - public static function unavailable(Periodicity $periodicity, array $instervals, int $code = 0, ?\Throwable $previous = null): IntervalException + public static function unavailable( + Periodicity $periodicity, + array $intervals, + int $code = 0, + ?\Throwable $previous = null + ): IntervalException { $message = sprintf( 'The periodicity %s is unknown. Choose one of available periodicity: %s', $periodicity->name, - join(', ', $instervals) + join(', ', $intervals) ); $exception = new IntervalException($message, $code, $previous); $exception->periodicity = $periodicity; - $exception->availableIntervals = $instervals; + $exception->availableIntervals = $intervals; return $exception; } } diff --git a/app/Support/Calendar/Periodicity.php b/app/Support/Calendar/Periodicity.php index 75c492bb13..83d5d449f9 100644 --- a/app/Support/Calendar/Periodicity.php +++ b/app/Support/Calendar/Periodicity.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Bimonthly.php b/app/Support/Calendar/Periodicity/Bimonthly.php index a8aac196fa..b521813bc1 100644 --- a/app/Support/Calendar/Periodicity/Bimonthly.php +++ b/app/Support/Calendar/Periodicity/Bimonthly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Daily.php b/app/Support/Calendar/Periodicity/Daily.php index 39fc34196d..1ca47c983a 100644 --- a/app/Support/Calendar/Periodicity/Daily.php +++ b/app/Support/Calendar/Periodicity/Daily.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Fortnightly.php b/app/Support/Calendar/Periodicity/Fortnightly.php index a64880a058..cd9af54441 100644 --- a/app/Support/Calendar/Periodicity/Fortnightly.php +++ b/app/Support/Calendar/Periodicity/Fortnightly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/HalfYearly.php b/app/Support/Calendar/Periodicity/HalfYearly.php index e8d6242bb2..f5bb2f0308 100644 --- a/app/Support/Calendar/Periodicity/HalfYearly.php +++ b/app/Support/Calendar/Periodicity/HalfYearly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Interspacable.php b/app/Support/Calendar/Periodicity/Interspacable.php index a10658cdfc..e61f360dff 100644 --- a/app/Support/Calendar/Periodicity/Interspacable.php +++ b/app/Support/Calendar/Periodicity/Interspacable.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Interval.php b/app/Support/Calendar/Periodicity/Interval.php index 73db1f0409..1e40ef27b8 100644 --- a/app/Support/Calendar/Periodicity/Interval.php +++ b/app/Support/Calendar/Periodicity/Interval.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Monthly.php b/app/Support/Calendar/Periodicity/Monthly.php index 34cc9bf3e6..e1739a643a 100644 --- a/app/Support/Calendar/Periodicity/Monthly.php +++ b/app/Support/Calendar/Periodicity/Monthly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Quarterly.php b/app/Support/Calendar/Periodicity/Quarterly.php index b20dc60250..46bac74728 100644 --- a/app/Support/Calendar/Periodicity/Quarterly.php +++ b/app/Support/Calendar/Periodicity/Quarterly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Weekly.php b/app/Support/Calendar/Periodicity/Weekly.php index 519ea143d8..fa0724cefc 100644 --- a/app/Support/Calendar/Periodicity/Weekly.php +++ b/app/Support/Calendar/Periodicity/Weekly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/app/Support/Calendar/Periodicity/Yearly.php b/app/Support/Calendar/Periodicity/Yearly.php index 768f3d87b3..466cb19640 100644 --- a/app/Support/Calendar/Periodicity/Yearly.php +++ b/app/Support/Calendar/Periodicity/Yearly.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/CalculatorProvider.php b/tests/unit/Support/Calendar/CalculatorProvider.php index 12f23d7363..800adb15b0 100644 --- a/tests/unit/Support/Calendar/CalculatorProvider.php +++ b/tests/unit/Support/Calendar/CalculatorProvider.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/CalculatorTest.php b/tests/unit/Support/Calendar/CalculatorTest.php index a5c8162a01..29662db573 100644 --- a/tests/unit/Support/Calendar/CalculatorTest.php +++ b/tests/unit/Support/Calendar/CalculatorTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php b/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php index e44b188e33..3bcd6977c5 100644 --- a/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/BimonthlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/DailyTest.php b/tests/unit/Support/Calendar/Periodicity/DailyTest.php index 56169a646b..c1175891d1 100644 --- a/tests/unit/Support/Calendar/Periodicity/DailyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/DailyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php b/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php index abc63993ec..3c2977074c 100644 --- a/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/FortnightlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php b/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php index fa15fbfc35..b12d6410e5 100644 --- a/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/HalfYearlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php b/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php index 2c62f5802f..88e3cd63ae 100644 --- a/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php +++ b/tests/unit/Support/Calendar/Periodicity/IntervalProvider.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php b/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php index a0cf5bb124..de3a640fd9 100644 --- a/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php +++ b/tests/unit/Support/Calendar/Periodicity/IntervalTestCase.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php b/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php index a9fe75d15d..c7235729c3 100644 --- a/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/MonthlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php b/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php index 093276bcb1..60e6eba15c 100644 --- a/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/QuarterlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php b/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php index b2fa868518..598886b52a 100644 --- a/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/WeeklyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). * diff --git a/tests/unit/Support/Calendar/Periodicity/YearlyTest.php b/tests/unit/Support/Calendar/Periodicity/YearlyTest.php index fb8e70d9d1..462f4ceaaa 100644 --- a/tests/unit/Support/Calendar/Periodicity/YearlyTest.php +++ b/tests/unit/Support/Calendar/Periodicity/YearlyTest.php @@ -1,7 +1,7 @@ * * This file is part of Firefly III (https://github.com/firefly-iii). *