Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Bitbucket pipeline for Drupal browser test

Kristof De Bie July 3, 2024

I am trying to run Drupal browsertests/functional-tests in a Bitbucket pipeline, but always get the following error while doing so:

Exception: Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database: INSERT INTO "test35378103"."watchdog"...

The repository is just an empty/clean Drupal install as we were already facing the same issue on our actual repo. So we thought we would give it a try on a clean install so we could rule out any custom changes we have done.

Additional info:

  • The sqlite.php file that is being called is a small script I have downloaded just to check that sqlite is actually working correctly. It opens a sqlite-db (in the same folder as where phpunit should do it), creates a table, inserts some data and selects that data again (and closes the db). All without any error.
  • Apache seems to be running correctly, as the three curl-command all give the expected output.
  • I have tried different docker-images, but I always run into the exact same exception "attempt to write a readonly database" when running the functional tests.
  • I have tried placing the db in a lot of different locations (like /tmp and sorts), but I always have the same exception.
  • I have tried switching to using MySql instead of Sqlite for phpunit (although the latter is prefered), but then the browsertest would also fail with an exception on some sort of file-permissions issue.
  • I have also tried "sqlite://localhost/:memory:" as db-connection-string, but that also does not work. The database will be removed from memory after closing the connection to it. It looks like Drupal/phpunit is able to do it's setup for the functional test, but then when actually running the test-method a new db-connection is opened and thus all setup is gone again). Then when calling the /user url, Drupal is redirecting to the install.php page (which is normal behaviour as there are no db-tables at that moment).
  • Running the same functional-test in the same docker-image locally (+ running the same commands as from the pipeline before) is working just fine: not a single error.
  • After a lot of searching on the internet almost all "solutions" for this exception mention it is a permission problem. So I have tried changing the permissions to the folder where the db should live and creating an empty db-file + given it all permissions as well: no luck...

Did anyone encounter a similar problem before? What is/could be the solution? I am out of ideas...

Happy to provide extra info if needed (and if I am able to share it).

 


 

bitbucket-pipelines.yml

 

 

 

 

image: php:8.2-apache
pipelines:
  default:
    - step:
        name: Run Drupal phpunit (unit and functional)
        caches:
          - composer
          - drush
        script:
          # Change the docroot of apache to match the bitbucket directory.
          - sed -i 's#DocumentRoot /var/www/html#DocumentRoot /opt/atlassian/pipelines/agent/build/web#g' /etc/apache2/sites-available/000-default.conf
          - cat /etc/apache2/sites-available/000-default.conf
          - sed -i 's#<Directory /var/www/>#<Directory /opt/atlassian/pipelines/agent/build/>#g' /etc/apache2/apache2.conf
          # Make sure .htaccess file are taken into account. We are changing this here for all directories listed in the .conf,
          # but that is not an issue for this usecase.
          - sed -i 's#AllowOverride None#AllowOverride All#g' /etc/apache2/apache2.conf
          # Install gd
          - apt-get update && apt-get install -y libfreetype-dev libjpeg62-turbo-dev libpng-dev && docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd
          # Update the container and install zip so composer can
          # use zip to unpack the dependencies.
          - apt update -y
          - apt install -y zip unzip

          # Install dependencies
          - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
          - export COMPOSER_ALLOW_SUPERUSER=1
          - composer install -o

          # Create the folder for the browser-output
          - mkdir $(pwd)/web/sites/simpletest
          - mkdir $(pwd)/web/sites/simpletest/browser_output

          # For testing only: create an empty db-file
          - touch $(pwd)/web/sites/simpletest/phpunit.sqlite

          # For testing only: set all permissions open
          - chown -R www-data:www-data $(pwd)/web/sites/simpletest
          - chmod -R 777 $(pwd)/web/sites/simpletest

          # Start Apache
          - apachectl restart
          # Wait for a few seconds to ensure the server starts
          - sleep 5

          # Check if the server is running and serving the expected content
          # This should give a redirect to install.php
          - curl -vk http://localhost
          # This should also give a redirect to install.php
          - curl -vk http://localhost/user
          # This should give some output using a sqlite-db
          - curl -vk http://localhost/sqlite.php

          # Run tests
          # Create a link from phpunit.xml to phpunit.xml.dist (as the first one
          # is not a part of the repo).
          - ln -s phpunit.xml.dist phpunit.xml

          # For testing only: set all permissions open
          - chown -R www-data:www-data /opt/atlassian/pipelines/agent/build
          - chmod -R 777 /opt/atlassian/pipelines/agent/build

          # Run tests
          - ./vendor/bin/phpunit --debug -v -c $(pwd)/phpunit.xml web/modules/custom/sandbox/Tests/src/Unit
          - ./vendor/bin/phpunit --debug -v -c $(pwd)/phpunit.xml web/modules/custom/sandbox/Tests/src/Functional

definitions:
  caches:
    composer: /root/.composer/cache
    drush: ~/.drush/cache

partial content of phpunit.xml(.dist)

<env name="SIMPLETEST_BASE_URL" value="http://localhost"/>
<env name="SIMPLETEST_DB" value="sqlite://localhost//opt/atlassian/pipelines/agent/build/web/sites/simpletest/phpunit.sqlite"/>
<env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/opt/atlassian/pipelines/agent/build/web/sites/simpletest/browser_output"/>

 

3 answers

0 votes
Kristof De Bie July 3, 2024

output of the exception in the pipeline

./vendor/bin/phpunit --debug -v -c $(pwd)/phpunit.xml web/modules/custom/sandbox/Tests/src/Functional
PHPUnit 9.6.19 by Sebastian Bergmann and contributors.
Runtime:       PHP 8.2.20
Configuration: /opt/atlassian/pipelines/agent/build/phpunit.xml
Testing /opt/atlassian/pipelines/agent/build/web/modules/custom/sandbox/Tests/src/Functional
Test 'Drupal\Tests\sandbox\Functional\SampleTest::testSample' started
Test 'Drupal\Tests\sandbox\Functional\SampleTest::testSample' ended
Time: 00:02.810, Memory: 10.00 MB
There was 1 error:
1) Drupal\Tests\sandbox\Functional\SampleTest::testSample
Exception: Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database: INSERT INTO "test69483024"."watchdog" ("uid", "type", "message", "variables", "severity", "link", "location", "referer", "hostname", "timestamp") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); Array
(
    [0] => 0
    [1] => php
    [2] => %type: @message in %function (line %line of %file).
    [3] => a:6:{s:5:"%type";s:45:"Drupal\Core\Database\DatabaseExceptionWrapper";s:8:"@message";s:4384:"SQLSTATE[HY000]: General error: 8 attempt to write a readonly database: INSERT INTO "test69483024"."cache_data" ("cid", "expire", "created", "tags", "checksum", "data", "serialized") VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6) ON CONFLICT ("cid") DO UPDATE SET "cid" = EXCLUDED."cid", "expire" = EXCLUDED."expire", "created" = EXCLUDED."created", "tags" = EXCLUDED."tags", "checksum" = EXCLUDED."checksum", "data" = EXCLUDED."data", "serialized" = EXCLUDED."serialized"; Array
(
    [:db_insert_placeholder_0] => route:[language]=en:/:
    [:db_insert_placeholder_1] => -1
    [:db_insert_placeholder_2] => 1720006065.735
    [:db_insert_placeholder_3] => route_match
    [:db_insert_placeholder_4] => 1
    [:db_insert_placeholder_5] => a:3:{s:4:"path";s:11:"/user/login";s:5:"query";a:0:{}s:6:"routes";O:41:"Symfony\Component\Routing\RouteCollection":4:{s:49:"Symfony\Component\Routing\RouteCollectionroutes";a:3:{s:10:"user.login";O:31:"Symfony\Component\Routing\Route":9:{s:4:"path";s:11:"/user/login";s:4:"host";s:0:"";s:8:"defaults";a:2:{s:5:"_form";s:31:"\Drupal\user\Form\UserLoginForm";s:6:"_title";s:6:"Log in";}s:12:"requirements";a:1:{s:18:"_user_is_logged_in";s:5:"FALSE";}s:7:"options";a:4:{s:14:"compiler_class";s:33:"Drupal\Core\Routing\RouteCompiler";s:19:"_maintenance_access";b:1;s:4:"utf8";b:1;s:14:"_access_checks";a:1:{i:0;s:30:"access_check.user.login_status";}}s:7:"schemes";a:0:{}s:7:"methods";a:2:{i:0;s:3:"GET";i:1;s:4:"POST";}s:9:"condition";s:0:"";s:8:"compiled";O:33:"Drupal\Core\Routing\CompiledRoute":11:{s:4:"vars";a:0:{}s:11:"path_prefix";s:0:"";s:10:"path_regex";s:18:"{^/user/login$}sDu";s:11:"path_tokens";a:1:{i:0;a:2:{i:0;s:4:"text";i:1;s:11:"/user/login";}}s:9:"path_vars";a:0:{}s:10:"host_regex";N;s:11:"host_tokens";a:0:{}s:9:"host_vars";a:0:{}s:3:"fit";i:3;s:14:"patternOutline";s:11:"/user/login";s:8:"numParts";i:2;}}s:15:"user.login.http";O:31:"Symfony\Component\Routing\Route":9:{s:4:"path";s:11:"/user/login";s:4:"host";s:0:"";s:8:"defaults";a:1:{s:11:"_controller";s:59:"\Drupal\user\Controller\UserAuthenticationController::login";}s:12:"requirements";a:2:{s:18:"_user_is_logged_in";s:5:"FALSE";s:7:"_format";s:4:"json";}s:7:"options";a:3:{s:14:"compiler_class";s:33:"Drupal\Core\Routing\RouteCompiler";s:4:"utf8";b:1;s:14:"_access_checks";a:1:{i:0;s:30:"access_check.user.login_status";}}s:7:"schemes";a:0:{}s:7:"methods";a:1:{i:0;s:4:"POST";}s:9:"condition";s:0:"";s:8:"compiled";O:33:"Drupal\Core\Routing\CompiledRoute":11:{s:4:"vars";a:0:{}s:11:"path_prefix";s:0:"";s:10:"path_regex";s:18:"{^/user/login$}sDu";s:11:"path_tokens";a:1:{i:0;a:2:{i:0;s:4:"text";i:1;s:11:"/user/login";}}s:9:"path_vars";a:0:{}s:10:"host_regex";N;s:11:"host_tokens";a:0:{}s:9:"host_vars";a:0:{}s:3:"fit";i:3;s:14:"patternOutline";s:11:"/user/login";s:8:"numParts";i:2;}}s:21:"entity.user.canonical";O:31:"Symfony\Component\Routing\Route":9:{s:4:"path";s:12:"/user/{user}";s:4:"host";s:0:"";s:8:"defaults";a:2:{s:12:"_entity_view";s:9:"user.full";s:15:"_title_callback";s:48:"Drupal\user\Controller\UserController::userTitle";}s:12:"requirements";a:2:{s:4:"user";s:3:"\d+";s:14:"_entity_access";s:9:"user.view";}s:7:"options";a:4:{s:14:"compiler_class";s:33:"Drupal\Core\Routing\RouteCompiler";s:10:"parameters";a:1:{s:4:"user";a:2:{s:4:"type";s:11:"entity:user";s:9:"converter";s:21:"paramconverter.entity";}}s:14:"_access_checks";a:1:{i:0;s:19:"access_check.entity";}s:4:"utf8";b:1;}s:7:"schemes";a:0:{}s:7:"methods";a:2:{i:0;s:3:"GET";i:1;s:4:"POST";}s:9:"condition";s:0:"";s:8:"compiled";O:33:"Drupal\Core\Routing\CompiledRoute":11:{s:4:"vars";a:1:{i:0;s:4:"user";}s:11:"path_prefix";s:0:"";s:10:"path_regex";s:26:"{^/user/(?P<user>\d+)$}sDu";s:11:"path_tokens";a:2:{i:0;a:5:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:4:"user";i:4;b:1;}i:1;a:2:{i:0;s:4:"text";i:1;s:5:"/user";}}s:9:"path_vars";a:1:{i:0;s:4:"user";}s:10:"host_regex";N;s:11:"host_tokens";a:0:{}s:9:"host_vars";a:0:{}s:3:"fit";i:2;s:14:"patternOutline";s:7:"/user/%";s:8:"numParts";i:2;}}}s:50:"Symfony\Component\Routing\RouteCollectionaliases";a:0:{}s:52:"Symfony\Component\Routing\RouteCollectionresources";a:0:{}s:53:"Symfony\Component\Routing\RouteCollectionpriorities";a:0:{}}}
    [:db_insert_placeholder_6] => 1
)...
0 votes
Kristof De Bie July 3, 2024

edited: double post

0 votes
Kristof De Bie July 3, 2024

(cannot put all info in post: adding here in replies instead)

SampleTest

<?php

namespace Drupal\Tests\sandbox\Functional;

use Drupal\Tests\BrowserTestBase;

class SampleTest extends BrowserTestBase {
  protected static $modules = [
    'system',
    'node',
    'dblog',
  ];

  protected $defaultTheme = 'olivero';

  protected function setUp(): void {
    parent::setUp();
  }

  public function testSample() {
    $this->drupalGet('user');
    $this->assertSession()->pageTextContains('Log in');
  }
}
Kristof De Bie July 3, 2024

edited: double post

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
CLOUD
PRODUCT PLAN
PREMIUM
TAGS
AUG Leaders

Atlassian Community Events