From 3fde63fd12be4ee6e38908cf05a35eaa01f5cd6d Mon Sep 17 00:00:00 2001 From: tchapi Date: Tue, 29 Sep 2026 23:33:44 +0200 Subject: [PATCH 1/2] chore --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++--- src/Entity/AddressBook.php | 2 +- src/Entity/AddressBookChange.php | 2 +- src/Entity/Calendar.php | 2 +- src/Entity/CalendarChange.php | 2 +- src/Entity/CalendarInstance.php | 2 +- src/Entity/CalendarObject.php | 2 +- src/Entity/CalendarSubscription.php | 2 +- src/Entity/Card.php | 2 +- src/Entity/Lock.php | 2 +- src/Entity/Principal.php | 2 +- src/Entity/PropertyStorage.php | 2 +- src/Entity/SchedulingObject.php | 2 +- src/Entity/User.php | 2 +- tests/bootstrap.php | 12 ++++++-- 15 files changed, 61 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3625f48..46e3a309 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: run: vendor/bin/php-cs-fixer fix --ansi phpunit: - name: PHPUnit (PHP ${{ matrix.php }}) + name: PHPUnit (PHP ${{ matrix.php }}, ${{ matrix.database }}) runs-on: ubuntu-24.04 container: image: php:${{ matrix.php }}-alpine @@ -98,6 +98,19 @@ jobs: --health-retries 5 ports: - 3306:3306 + postgres: + image: postgres:17-alpine + env: + POSTGRES_DB: davis_test + POSTGRES_USER: davis + POSTGRES_PASSWORD: davis + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 strategy: matrix: @@ -106,6 +119,10 @@ jobs: - '8.3' - '8.4' - '8.5' + database: + - mysql + - postgresql + - sqlite fail-fast: false steps: @@ -120,7 +137,8 @@ jobs: docker-php-ext-configure zip # ext-ldap is optional for Davis (only AUTH_METHOD=LDAP needs it) but the LDAP tests # skip themselves without it, and we would rather run them - docker-php-ext-install pdo pdo_mysql intl gd zip ldap + apk add postgresql-dev sqlite-dev + docker-php-ext-install pdo pdo_mysql pdo_pgsql pdo_sqlite intl gd zip ldap - name: Install Composer run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet @@ -128,11 +146,28 @@ jobs: - name: Install dependencies with Composer run: composer install --no-progress --no-interaction --ansi - - name: Run tests with PHPUnit + # The three engines differ in ways the suite has to see: SQLite enforces neither foreign + # keys nor column lengths, and PostgreSQL rejects writes MySQL accepts. The test database is + # built by the migrations (see tests/bootstrap.php), so this also covers the schema an + # install actually gets. + - name: Run tests with PHPUnit (MySQL) + if: matrix.database == 'mysql' env: DATABASE_URL: mysql://davis:davis@mysql:3306/davis_test run: vendor/bin/phpunit --process-isolation --colors=always + - name: Run tests with PHPUnit (PostgreSQL) + if: matrix.database == 'postgresql' + env: + DATABASE_URL: postgresql://davis:davis@postgres:5432/davis_test?serverVersion=17&charset=utf8 + run: vendor/bin/phpunit --process-isolation --colors=always + + - name: Run tests with PHPUnit (SQLite) + if: matrix.database == 'sqlite' + env: + DATABASE_URL: sqlite:///%kernel.project_dir%/var/data_test.db + run: vendor/bin/phpunit --process-isolation --colors=always + migrations: name: Migrations (${{ matrix.database }}) runs-on: ubuntu-24.04 @@ -220,7 +255,7 @@ jobs: run: | php bin/console doctrine:database:create --if-not-exists --env=test php bin/console doctrine:migrations:migrate --no-interaction --env=test - php bin/console doctrine:schema:validate --skip-sync --env=test + php bin/console doctrine:schema:validate --env=test # An empty schema only proves the DDL is valid. Seed the kind of row a DAV client # creates (optional columns left NULL) so the rollback is exercised against data too. php bin/console dbal:run-sql "INSERT INTO addressbooks (id, principaluri, uri, synctoken) VALUES (nextval('addressbooks_id_seq'), 'principals/ci', 'ci-rollback-probe', 1)" --env=test diff --git a/src/Entity/AddressBook.php b/src/Entity/AddressBook.php index 922a1984..dc208aa7 100644 --- a/src/Entity/AddressBook.php +++ b/src/Entity/AddressBook.php @@ -15,7 +15,7 @@ class AddressBook { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/AddressBookChange.php b/src/Entity/AddressBookChange.php index 93a7748b..89b5dd4c 100644 --- a/src/Entity/AddressBookChange.php +++ b/src/Entity/AddressBookChange.php @@ -11,7 +11,7 @@ class AddressBookChange { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/Calendar.php b/src/Entity/Calendar.php index 107229d2..815dfeda 100644 --- a/src/Entity/Calendar.php +++ b/src/Entity/Calendar.php @@ -15,7 +15,7 @@ class Calendar public const COMPONENT_NOTES = 'VJOURNAL'; #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/CalendarChange.php b/src/Entity/CalendarChange.php index 5842b6b0..53433802 100644 --- a/src/Entity/CalendarChange.php +++ b/src/Entity/CalendarChange.php @@ -11,7 +11,7 @@ class CalendarChange { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/CalendarInstance.php b/src/Entity/CalendarInstance.php index 046193da..f53620ad 100644 --- a/src/Entity/CalendarInstance.php +++ b/src/Entity/CalendarInstance.php @@ -22,7 +22,7 @@ public static function getOwnerAccesses(): array } #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/CalendarObject.php b/src/Entity/CalendarObject.php index 1366c023..578722f3 100644 --- a/src/Entity/CalendarObject.php +++ b/src/Entity/CalendarObject.php @@ -9,7 +9,7 @@ class CalendarObject { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/CalendarSubscription.php b/src/Entity/CalendarSubscription.php index 7b8c5ede..cc4e6394 100644 --- a/src/Entity/CalendarSubscription.php +++ b/src/Entity/CalendarSubscription.php @@ -9,7 +9,7 @@ class CalendarSubscription { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/Card.php b/src/Entity/Card.php index 0b293e1f..20a96de0 100644 --- a/src/Entity/Card.php +++ b/src/Entity/Card.php @@ -9,7 +9,7 @@ class Card { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/Lock.php b/src/Entity/Lock.php index aea669e1..f3145a39 100644 --- a/src/Entity/Lock.php +++ b/src/Entity/Lock.php @@ -9,7 +9,7 @@ class Lock { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/Principal.php b/src/Entity/Principal.php index 281cc14d..b7236bcf 100644 --- a/src/Entity/Principal.php +++ b/src/Entity/Principal.php @@ -19,7 +19,7 @@ class Principal public const WRITE_PROXY_SUFFIX = '/calendar-proxy-write'; #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/PropertyStorage.php b/src/Entity/PropertyStorage.php index 6b795aab..c5b297fa 100644 --- a/src/Entity/PropertyStorage.php +++ b/src/Entity/PropertyStorage.php @@ -11,7 +11,7 @@ class PropertyStorage { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/SchedulingObject.php b/src/Entity/SchedulingObject.php index b1b0fb3c..0c1bf11e 100644 --- a/src/Entity/SchedulingObject.php +++ b/src/Entity/SchedulingObject.php @@ -10,7 +10,7 @@ class SchedulingObject { #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/src/Entity/User.php b/src/Entity/User.php index 4eb62bf6..15fe6aa8 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -14,7 +14,7 @@ class User public const DEFAULT_AUTH_REALM = 'SabreDAV'; #[ORM\Id] - #[ORM\GeneratedValue] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\Column(type: 'integer')] private $id; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 60c4fa99..cb6b948f 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,11 +10,17 @@ (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); } -// Create the test database, update the schema and resets the fixture before each test. +// Create the test database, build its schema and reset the fixtures before each test. // Note: `--quiet` is needed here for each step so that PHPUnit doesn't fail. $actions = [ - 'doctrine:database:create --if-not-exists ', - 'doctrine:schema:update --complete --force', + // No `--if-not-exists`: it asks the platform to list its databases, which SQLite cannot do. + // Creating an existing database is a no-op here either way, the exit code is ignored below. + 'doctrine:database:create', + // The schema is built by the migrations, not by `doctrine:schema:update`, so the tests run + // against the schema an install actually has. The two are not the same on PostgreSQL: DBAL + // models an identity column without its `DEFAULT nextval(...)`, and the sabre/dav backends + // INSERT without an id, so every DAV write would fail against a schema:update database. + 'doctrine:migrations:migrate --no-interaction --allow-no-migration', 'doctrine:fixtures:load --no-interaction', ]; From c351877827b391392cb8219768f5eb4448aca3dd Mon Sep 17 00:00:00 2001 From: tchapi Date: Wed, 30 Sep 2026 21:56:12 +0200 Subject: [PATCH 2/2] chore --- tests/Functional/Service/DiagnosticsTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Functional/Service/DiagnosticsTest.php b/tests/Functional/Service/DiagnosticsTest.php index 0ed8a790..ce41a564 100644 --- a/tests/Functional/Service/DiagnosticsTest.php +++ b/tests/Functional/Service/DiagnosticsTest.php @@ -76,6 +76,8 @@ public function testAHealthyInstallReportsNothingToLookAt(): void { $diagnostics = $this->diagnosticsWith(); + // The test database is built by the migrations, so none of them are pending + $this->assertSame(Diagnostics::OK, $this->severityOf($diagnostics, 'diagnostics.migrations')); $this->assertSame(Diagnostics::OK, $this->severityOf($diagnostics, 'diagnostics.log_file')); $this->assertSame(Diagnostics::OK, $this->severityOf($diagnostics, 'diagnostics.environment')); $this->assertSame(Diagnostics::OK, $this->severityOf($diagnostics, 'diagnostics.timezone'));