From dd2f8d4404e1ebc8376afb5542140bea7f540959 Mon Sep 17 00:00:00 2001 From: Antonio Spinelli Date: Fri, 20 Oct 2023 19:00:45 -0300 Subject: [PATCH] Cover endOfPeriod method of the Navigation class The custom frequency requires a timezone configuration, forcing it to run in the integration test scope. Running the integration tests requires a database connection in the build process. It enables other case tests. The API Tests cause interference in other tests, requiring isolating them. --- .github/workflows/sonarcloud.yml | 60 +++++++++- .../Commands/System/CreateDatabase.php | 1 + composer.json | 2 +- phpunit.xml | 1 + .../Autocomplete/AccountControllerTest.php | 27 +---- .../NavigationCustomEndOfPeriodTest.php | 25 ++++ .../Support/NavigationEndOfPeriodTest.php | 107 ++++++++++++++++++ 7 files changed, 197 insertions(+), 26 deletions(-) create mode 100644 tests/integration/Support/NavigationCustomEndOfPeriodTest.php create mode 100644 tests/unit/Support/NavigationEndOfPeriodTest.php diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index e9ba1b9b27..1a62d846f7 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,14 +1,37 @@ name: Sonarcloud on: - pull_request: + pull_request: push: branches: - main - develop +env: + DB_CONNECTION: mysql + DB_HOST: "127.0.0.1" + DB_DATABASE: firefly + DB_USER: firefly + DB_PASSWORD: secret_firefly_password + jobs: sonarcloud: name: SonarCloud runs-on: ubuntu-latest + services: + mariadb: + image: mariadb:latest + ports: + - 3306:3306 + env: + MYSQL_ROOT_PASSWORD: yes + MYSQL_USER: ${{ env.DB_USER }} + MYSQL_PASSWORD: ${{ env.DB_PASSWORD }} + MYSQL_DATABASE: ${{ env.DB_DATABASE }} + options: >- + --health-cmd="healthcheck.sh --connect --innodb_initialized" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + steps: - uses: actions/checkout@v3 with: @@ -24,16 +47,49 @@ jobs: with: php-version: '8.2' coverage: xdebug + extensions: >- + bcmath + curl + fileinfo + iconv + intl + json + mbstring + openssl + pdo + session + simplexml + sodium + tokenizer + xml + xmlwriter - name: Install Composer dependencies run: composer install --prefer-dist --no-interaction --no-progress --no-scripts + - name: Verify Database connection + env: + PORT: ${{ job.services.mariadb.ports[3306] }} + run: | + while ! mysqladmin ping -h"${{env.DB_HOST}}" -P"${PORT}" --silent; do + sleep 1 + done + - name: Copy environment file - run: cp .env.example .env + run: sed 's@DB_HOST=.*@DB_HOST=${{env.DB_HOST}}@g' .env.example > .env - name: Generate app key run: php artisan key:generate + - name: "Create the database" + run: php artisan firefly-iii:create-database + + - name: "Upgrades the database to the latest version" + run: php artisan firefly-iii:upgrade-database + + - name: "Integrity Database Report" + run: php artisan firefly-iii:report-integrity + - name: "Run tests with coverage" run: composer coverage diff --git a/app/Console/Commands/System/CreateDatabase.php b/app/Console/Commands/System/CreateDatabase.php index b34914e310..5960f59144 100644 --- a/app/Console/Commands/System/CreateDatabase.php +++ b/app/Console/Commands/System/CreateDatabase.php @@ -83,6 +83,7 @@ class CreateDatabase extends Command $pdo = new PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options); } catch (PDOException $e) { $this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage())); + return 1; } // only continue when no error. diff --git a/composer.json b/composer.json index f0143e13a5..7819851728 100644 --- a/composer.json +++ b/composer.json @@ -176,7 +176,7 @@ "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration --no-coverage" ], "coverage": [ - "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit" + "@php vendor/bin/phpunit -c phpunit.xml" ] }, "config": { diff --git a/phpunit.xml b/phpunit.xml index aa51a02e21..d5008da502 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -33,6 +33,7 @@ stopOnFailure="true"> + diff --git a/tests/integration/Api/Autocomplete/AccountControllerTest.php b/tests/integration/Api/Autocomplete/AccountControllerTest.php index 5eb0ec0ca7..c268f9fb2e 100644 --- a/tests/integration/Api/Autocomplete/AccountControllerTest.php +++ b/tests/integration/Api/Autocomplete/AccountControllerTest.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace Tests\integration\Api\Autocomplete; -use Laravel\Passport\Passport; -use Log; use Tests\integration\TestCase; /** @@ -32,33 +30,16 @@ use Tests\integration\TestCase; */ class AccountControllerTest extends TestCase { - /** - * - */ - public function setUp(): void - { - parent::setUp(); - Passport::actingAs($this->user()); - Log::info(sprintf('Now in %s.', get_class($this))); - } - /** * @covers \FireflyIII\Api\V1\Controllers\Autocomplete\AccountController + * @runInSeparateProcess */ - public function testAccounts(): void + public function testGivenAnUnauthenticatedRequestWhenCallingTheAccountsEndpointThenReturns401HttpCode(): void { // test API $response = $this->get(route('api.v1.autocomplete.accounts'), ['Accept' => 'application/json']); - $response->assertStatus(200); + $response->assertStatus(401); $response->assertHeader('Content-Type', 'application/json'); - $response->assertSee('Checking Account'); - } - - /** - * - */ - public function testBasic(): void - { - $this->assertTrue(true); + $response->assertContent('{"message":"Unauthenticated","exception":"AuthenticationException"}'); } } diff --git a/tests/integration/Support/NavigationCustomEndOfPeriodTest.php b/tests/integration/Support/NavigationCustomEndOfPeriodTest.php new file mode 100644 index 0000000000..a7bd3c1652 --- /dev/null +++ b/tests/integration/Support/NavigationCustomEndOfPeriodTest.php @@ -0,0 +1,25 @@ +endOfPeriod($from, 'custom'); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } +} diff --git a/tests/unit/Support/NavigationEndOfPeriodTest.php b/tests/unit/Support/NavigationEndOfPeriodTest.php new file mode 100644 index 0000000000..af86ed7f03 --- /dev/null +++ b/tests/unit/Support/NavigationEndOfPeriodTest.php @@ -0,0 +1,107 @@ + + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +declare(strict_types=1); + +namespace Tests\unit\Support; + +use Carbon\Carbon; +use FireflyIII\Support\Navigation; +use Illuminate\Support\Facades\Log; +use PHPUnit\Framework\TestCase; + +/** + * @group unit-test + * @group support + * @group navigation + */ +class NavigationEndOfPeriodTest extends TestCase +{ + private Navigation $navigation; + + public function __construct(string $name) + { + parent::__construct($name); + $this->navigation = new Navigation(); + } + + public static function provideDates(): array + { + return [ + '1D' => ['frequency' => '1D', 'from' => Carbon::now(), 'expected' => Carbon::now()->endOfDay()], + 'daily' => ['frequency' => 'daily', 'from' => Carbon::now(), 'expected' => Carbon::now()->endOfDay()], + '1W' => ['frequency' => '1W', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeek()->subDay()->endOfDay()], + 'week' => ['frequency' => 'week', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeek()->subDay()->endOfDay()], + 'weekly' => ['frequency' => 'weekly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addWeek()->subDay()->endOfDay()], + 'month' => ['frequency' => 'month', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonth()->subDay()->endOfDay()], + '1M' => ['frequency' => '1M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonth()->subDay()->endOfDay()], + 'monthly' => ['frequency' => 'monthly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addMonth()->subDay()->endOfDay()], + '3M' => ['frequency' => '3M', 'from' => Carbon::now(), 'expected' => Carbon::now()->addQuarterNoOverflow()->subDay()->endOfDay()], + 'quarter' => ['frequency' => 'quarter', 'from' => Carbon::now(), 'expected' => Carbon::now()->addQuarterNoOverflow()->subDay()->endOfDay()], + 'quarterly' => ['frequency' => 'quarterly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addQuarterNoOverflow()->subDay()->endOfDay()], + 'year' => ['frequency' => 'year', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYearNoOverflow()->subDay()->endOfDay()], + 'yearly' => ['frequency' => 'yearly', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYearNoOverflow()->subDay()->endOfDay()], + '1Y' => ['frequency' => '1Y', 'from' => Carbon::now(), 'expected' => Carbon::now()->addYearNoOverflow()->subDay()->endOfDay()], + 'half-year' => ['frequency' => 'half-year', 'from' => Carbon::parse('2023-05-20'), 'expected' => Carbon::parse('2023-11-19')->endOfDay()], + '6M' => ['frequency' => '6M', 'from' => Carbon::parse('2023-08-20'), 'expected' => Carbon::parse('2024-02-19')], + 'last7' => ['frequency' => 'last7', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(7)->endOfDay()], + 'last30' => ['frequency' => 'last30', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(30)->endOfDay()], + 'last90' => ['frequency' => 'last90', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(90)->endOfDay()], + 'last365' => ['frequency' => 'last365', 'from' => Carbon::now(), 'expected' => Carbon::now()->addDays(365)->endOfDay()], + 'MTD' => ['frequency' => 'MTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->startOfMonth()->startOfDay()], + 'QTD' => ['frequency' => 'QTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->firstOfQuarter()->startOfDay()], + 'YTD' => ['frequency' => 'YTD', 'from' => Carbon::now(), 'expected' => Carbon::now()->startOfYear()->startOfDay()], + 'week 2023-08-05 to 2023-08-11' => ['frequency' => '1W', 'from' => Carbon::parse('2023-08-05'), 'expected' => Carbon::parse('2023-08-11')->endOfDay()], + ]; + } + + /** + * @dataProvider provideDates + */ + public function testGivenADateAndFrequencyWhenCalculateTheDateThenReturnsTheExpectedDateSuccessful(string $frequency, Carbon $from, Carbon $expected) + { + $period = $this->navigation->endOfPeriod($from, $frequency); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + } + + public static function provideUnknownFrequencies(): array + { + return [ + '1day' => ['frequency' => '1day', 'from' => Carbon::now(), 'expected' => Carbon::now()], + 'unknown' => ['frequency' => 'unknown', 'from' => Carbon::now(), 'expected' => Carbon::now()->startOfDay()], + 'empty' => ['frequency' => '', 'from' => Carbon::now(), 'expected' => Carbon::now()->startOfDay()], + ]; + } + + /** + * @dataProvider provideUnknownFrequencies + */ + public function testGivenADateAndUnknownFrequencyWhenCalculateTheDateThenReturnsTheSameDateSuccessful(string $frequency, Carbon $from, Carbon $expected) + { + Log::spy(); + + $period = $this->navigation->endOfPeriod($from, $frequency); + $this->assertEquals($expected->toDateString(), $period->toDateString()); + $expectedMessage = sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $frequency); + + Log::shouldHaveReceived('error', [$expectedMessage]); + } +}