Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parallel sessions with $I->haveFriend() when no session is active #91

Merged
merged 1 commit into from Nov 15, 2023

Conversation

michaelarnauts
Copy link
Contributor

When using $I->haveFriend(), my tests would fail with the following error.

[yii\base\UnknownPropertyException] Getting unknown property: yii\console\Application::session

This is because the code in _backupSession() didn't check if we actually have a Yii::$app->session before accessing Yii::$app->session->useCustomStorage.

This PR fixes this and allows me to run my tests.

See #2 and #3

@SamMousa
Copy link
Collaborator

I'm a bit confused though. This would resolve the exception for sure, but in your specific test case it will not work as expected.
Since sessions are not actually used in the console application.

As far as I can tell the code should work for custom storage backends, any reason why you explicitly check for it and exclude it?

@michaelarnauts
Copy link
Contributor Author

michaelarnauts commented Nov 15, 2023

I'm a bit confused though. This would resolve the exception for sure, but in your specific test case it will not work as expected. Since sessions are not actually used in the console application.

As far as I can tell the code should work for custom storage backends, any reason why you explicitly check for it and exclude it?

I'm testing a REST application with enableSession set to false;

        'user' => [
            'identityClass' => common\models\UserIdentity::class,
            'enableSession' => false,
            'loginUrl' => null,
        ],

In my tests, I set a Bearer token to login as a specific user. I can now use this like this (simplified).

        $I->haveHttpHeader('Content-Type', 'application/json');
        $I->setUpAuthentication('admin');
        // make calls as admin

        $friend = $I->haveFriend('friend');
        $friend->does(function (FunctionalTester $I) {
            $I->haveHttpHeader('Content-Type', 'application/json');
            $I->setUpAuthentication('user');
            // make calls as user
        });

The $I->setupAuthentication() generates a token and eventually does a $I->amBearerAuthenticated($token)

@SamMousa
Copy link
Collaborator

Hmm, but if you are not really using sessions, why even use the friend workflow?

 $I->haveHttpHeader('Content-Type', 'application/json');
        $I->setUpAuthentication('admin');
        // make calls as admin

        $I->setUpAuthentication('user');
        // make calls as user

This would work just as well right? I'm happy merging this btw, but I feel it's not very descriptive of what's happening. Since there is actually no session stuff and I'm pretty sure that even outside the haveFriend closure your bearer authentication will persist and you'll not be an admin.

In big caps my expectation of what'll happen:

        $I->haveHttpHeader('Content-Type', 'application/json');
        $I->setUpAuthentication('admin');
        // YOU ARE NOW ADMIN

        $friend = $I->haveFriend('friend');
        $friend->does(function (FunctionalTester $I) {
            $I->haveHttpHeader('Content-Type', 'application/json');
            $I->setUpAuthentication('user');
            // YOU ARE NOW USER
        });
        // YOU ARE STILL USER

@michaelarnauts
Copy link
Contributor Author

michaelarnauts commented Nov 15, 2023

Hmm, it actually works as expected. I'm using another token inside the closure. I suppose inside the closure, there is a new FunctionalTester instance with fresh empty headers.

Some (simplified) debug log:

 I send post "/api/admin-call"
  [Request] POST http://localhost:8080/index-test.php/api/admin-call []
  [Request Headers] {"Authorization":"Bearer ey...4EgaIJAGeikPCriXHo"}

 FRIEND DOES ---
 I have http header "Content-Type","application/json"
 Codeception\Lib\Friend: does "Closure"
 I am bearer authenticated "ey..."

 I send post "/api/user-call"
  [Request] POST http://localhost:8080/index-test.php/api/user-call []
  [Request Headers] {"Content-Type":"application/json","Authorization":"Bearer ey.._OtZ0K5fFi21o"}

 --- FRIEND FINISHED
 I send post "/api/admin-call"
  [Request] POST http://localhost:8080/index-test.php/api/admin-call []
  [Request Headers] {"Authorization":"Bearer ey...4EgaIJAGeikPCriXHo"}

@SamMousa
Copy link
Collaborator

Ah, of course; the backup session function not only backs up the session but also the client context and headers:

    /**
     * Return the session content for future restoring. Implements MultiSession.
     * @return array backup data
     */
    public function _backupSession(): array
    {
        if (isset(Yii::$app) && Yii::$app->session->useCustomStorage) {
            throw new ModuleException($this, "Yii2 MultiSession only supports the default session backend.");
        }
        return [
            'clientContext' => $this->client->getContext(),
            'headers' => $this->headers,
            'cookie' => isset($_COOKIE) ? $_COOKIE : [],
            'session' => isset($_SESSION) ? $_SESSION : [],
        ];
    }

@SamMousa SamMousa merged commit d819def into Codeception:master Nov 15, 2023
3 checks passed
@SamMousa
Copy link
Collaborator

@samdark can you trigger / do a release? It's not automated and I don't know the release process.

@michaelarnauts
Copy link
Contributor Author

@samdark @SamMousa Can we do a release with this change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants