Laravel pipeline tests with POSTs fail with 419

Was able to get the gitlab ci/cd pipeline working with my laravel project. The pipeline passes with the example tests, and others, but fails on each test that issues a post. When I run the tests locally all pass without any issue, so there seems to be something wrong with the pipeline config…

For example this test passes:

public function test_user_can_view_a_login_form()
{
  $response = $this->get('/login');
  $response->assertSuccessful();
  $response->assertViewIs('auth.login');
}

and this test fails, as will every other test that uses a POST:

public function test_user_can_login_with_correct_credentials()
{
    $user = User::factory()->create([
        'password' => bcrypt($password = 'i-love-laravel'),
    ]);

    $response = $this->post('/login', [
        'email' => $user->email,
        'password' => $password,
    ]);

    $response->assertRedirect('/home');
    $response->assertStatus(302);
    $this->assertAuthenticatedAs($user);
}

This is the error produce in the pipeline (but not when I run the tests outside of the pipeline).

• Tests\Feature\Auth\LoginTest > user can login with correct credentials

[1502] Response status code [419] is not a redirect status code.

[1503]Failed asserting that false is true.

Using Gitlab.com not self managed.

my ci.yml:

image: php:latest

services:
  - mysql:latest

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - vendor/
    - node_modules/

variables:
  MYSQL_DATABASE: db
  MYSQL_ROOT_PASSWORD: secret

before_script:

  - apt-get update -yqq

  - apt-get install -y libfreetype6-dev libjpeg-dev libpng-dev libwebp-dev zlib1g-dev libzip-dev libonig-dev libpng-dev
  - docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/
  - docker-php-ext-install -j$(nproc) gd
  - apt-get install -y --no-install-recommends libgmp-dev
  - docker-php-ext-install gmp pdo_mysql pdo opcache zip
  - pecl install xdebug
  - docker-php-ext-enable xdebug
  - curl -sS https://getcomposer.org/installer | php
  - php composer.phar update
  - php composer.phar install
  - cp .env.example.testing .env
  - php artisan key:generate
  - php artisan config:cache
  - php artisan migrate 
  - php artisan db:seed

test:
  script:
    - php vendor/bin/phpunit --coverage-text --colors=never

I have tried to add in nodejs dependencies but that didn’t change anything, doesn’t make sense why every post returns a 419 in the pipeline but not locally.

I do have a dockerized app and so a docker-compose, but I’m not testing that with the pipeline, just just testing laravel specifically.

The 419 code means:

419 Page Expired

Used by the Laravel Framework when a CSRF Token is missing or expired.

It’s not clear to me, from your code, whether or not you are using a CRSF token but that’s the place to start in terms of debugging.

Good luck!

Sarah

1 Like

Thanks, I’m aware what the error means, just have no idea why it hits it while running tests in the pipeline. I assume something isn’t being configured or initialized correctly but since the gitlab.con pipeline is a bit of a black box not sure how to debug that.

The tests (and server) work fine out of the pipeline so it has to be something with the laravel test config in the pipeline, I’ve tried changing the order of the artisan commands to no avail.

Are you using a CRSF token, @JonathanAlexander ?

1 Like

I’m confused by your question. Laravel uses a CRSF token yes, and in my other deployments I haven’t had to do anything other than the basic artisan commands to get the server and tests working properly. Is there something special I need to do to get the laravel CRSF token to work in the gitlab pipeline?

Thanks for your help!

I’m not sure exactly what it is about the pipeline that is different, but I just ask because, although Laravel generates the token automatically, you do need to add it to your forms. If you have not done that, or something has gone wrong with that code, this may be the cause of your problems.

This may seem very obvious to you, but I notice there are some SO questions that are similar.

Also, it is worth checking that your APP_ENV in the pipeline is the same as your APP_ENV on the command line. This may be set in your phpunit.xml file, or in an environment variable somewhere, or your .env.

Also, I know there used to be a WithoutMiddleware option in Laravel at some point, but I think it may have been deprecated. I’m not sure whether it was replaced with anything, but it may be worth looking at “special” testing environments like that.

1 Like

Thank you for that feedback, I’ll look through your recommendations and report back on my finding.

Once again really appreciate your help and quick response here

1 Like

Things I’ve confirmed, my login form does have the laravel @csrf directive

 <form method="POST" action="{{ route('login') }}">
                    @csrf

which is equivalent to

<input type="hidden" name="_token" value="{{ csrf_token() }}" />

I’ve both and both produce the same failure in the pipeline.

I’ve also confirm my root view has the csrf_token in the head tags

 <!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">

If I add the login route to the VerifyCsrfToken class expect array I am able to get the tests to pass, so it is seems something wrong with the laravel csrf token generation in the pipeline…

protected $except = [
    'webhook',
    'login',
];

Of course I can’t disable csrf token on the login route, as that would just a backward way of fixing the test.

I also verified that my .env APP_ENV is the same in my local config and my pipeline config. But I think you are on to something with the .env file, as when running locally I was just using the php artisan test command, which would use my .env and not my .env.testing which the pipeline is using… I have since copied the .env.example that the pipeline is using and have been able to test with it locally using the php artisan test --env=testing command and started to see some issues, not specifically the 419 csrf issue, and I’m currently working through them. Will provide a further update once I do.

Solved!

It seems to be combination of using the correct env configuration, and NOT running the following commands in the pipeline:

  php artisan config:cache --env=testing
  php artisan config:clean --env=testing

my working .env.testing.example file:

APP_NAME='APP NAME'
APP_ENV=testing 
APP_KEY=
APP_DEBUG=true

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=db
DB_USERNAME=root
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=array
QUEUE_CONNECTION=sync
SESSION_DRIVER=array

working .gitlab-ci.yml

image: php:latest

services:
  - mysql:latest

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - vendor/

variables:
  MYSQL_DATABASE: db
  MYSQL_ROOT_PASSWORD: secret

before_script:
  #update
  - apt-get update -yqq
  #install php dependencies
  - apt-get install -y libfreetype6-dev libjpeg-dev libpng-dev libwebp-dev zlib1g-dev libzip-dev libonig-dev libpng-dev
  - docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/
  - docker-php-ext-install -j$(nproc) gd
  - apt-get install -y --no-install-recommends libgmp-dev
  - docker-php-ext-install gmp pdo_mysql pdo opcache zip
  - pecl install xdebug
  - docker-php-ext-enable xdebug
  - apt-get autoclean -y
  - rm -rf /var/lib/apt/lists/*
  - rm -rf /tmp/pear/
  # install composer and update
  - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
  - composer update
  # initialize testing environment 
  - cp .env.example.testing .env
  - php artisan key:generate
  - php artisan migrate

test:
  script:
    # run laravel tests
    #- php vendor/bin/phpunit --coverage-text --colors=never
    - php artisan test --env=testing

output:

$ php artisan test --env=testing

Warning: TTY mode requires /dev/tty to be read/writable.

PASS Tests\Unit\ExampleTest

✓ basic test

PASS Tests\Feature\Auth\LoginTest

✓ user can view a login form

✓ user cannot view a login form when authenticated

✓ user can login with correct credentials

PASS Tests\Feature\ExampleTest

✓ basic test

Tests: 5 passed

Wohoo! Thanks for the help and encouragement!

Hey congrats! Debugging is quite the roller-coaster :roller_coaster: :sunglasses:

1 Like

Much obliged, I’m mindful what the blunder implies, simply have no clue about why it hits it while running tests in the pipeline. I accept something isn’t being designed or introduced effectively however since the gitlab.con pipeline is somewhat of a black box not certain how to troubleshoot that.

The tests (and worker) turn out great out of the pipeline so it must be something with the laravel test config in the pipeline, I’ve taken a stab at changing the request for the craftsman orders without any result.

Since I wrote the first couple of replies, I found this gitlab-ci-local tool that allows you to run pipelines on your local development environment, which might be useful to you. It’s not clear to me whether this sort of problem will be reproducible though.

I find config:cache and config:clean to be confusing, but I think it does matter when, in your deployment process, you run these commands, not least because:

If you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files. Once the configuration has been cached, the .env file will not be loaded; therefore, the env function will only return external, system level environment variables. [1].