gnikyt / laravel-shopify

A full-featured Laravel package for aiding in Shopify App development
MIT License
1.24k stars 374 forks source link

Cannot authenticate when using Laravel feature tests. #1141

Closed grahamsutton closed 2 years ago

grahamsutton commented 2 years ago

Hi there. First of all, I wanted to say I really appreciate this package and the ease of use it's brought to creating a Shopify app.

One thing I noticed when searching the Wiki is that there is no testing strategy or any discussion about writing automated tests in Laravel with this package and I believe this is a critical topic that would be great to add. I was taking a test-driven approach to my Shopify app but I struggled to authenticate my app in my Laravel feature tests.

For example, I have the following feature test in Laravel:

class ListBoxesTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function can_list_the_most_recent_boxes()
    {
        $user = User::factory()
            ->hasBoxes(5)
            ->create();

        $response = $this->actingAs($user)->json('GET', '/api/v1/boxes');

        $response->assertStatus(200);

        $response->assertJsonStructure([
            'data' => [
                '*' => [
                    'length',
                    'width',
                    'height'
                ]
            ]
        ]);
    }
}

However, if I were to run this test, it fails because it returns a 403 with the following error:

  • Tests\Feature\Boxes\ListBoxesTest > can list the most recent boxes
  Expected response status code [200] but received 403.

  The following exception occurred during the request:

  Osiset\ShopifyApp\Exceptions\HttpException: Shop is not installed or missing data. in /var/www/html/vendor/osiset/laravel-shopify/src/Http/Middleware/VerifyShopify.php:196
Stack trace: ...

This happens because the authentication is handled by extracting the data points from of our session token via Authorization: Bearer <session-token> to authenticate the right shop. The middleware gets exeucted on every test and unfortunately, without a valid JWT, the verify-shopify middleware kicks out every request with that 403. Using $this->actingAs(User)doesn't work either and neither does trying to generate my own JWT to use and signing them with my Shopify secret.

Ultimately, my questions are: what is the best strategy for authenticating a shop with Laravel feature tests? What is the best way to test my endpoints with the verify.shopify middleware to simulate the most realistic scenarios possible?

I've been trying to figure it out but I unfortunately cannot progress further unless I disable the middleware, but since the middleware is a critical authentication component, it feels wrong to leave it out of my tests because my tests don't completely mirror what would happen in production.

Kyon147 commented 2 years ago

Hi @grahamsutton

I tend to log the user in manually in my tests like so:

        $user = User::factory()->create();
        $auth = new AuthManager($this->app);
        $auth->login($user);

This allows you to use Auth:user() if you want to simulate a user.

For the middleware, there's so quick solution other than mocking the JWT auth process but you can skip the middleware when testing anyway, as you are in a control environment.

So you could just use the $this->withoutMiddleware() and bypass the verify.shopify completely.

With the auth manager I mentioned and removing the middleware, you should get most of what you need to test your internal business logic.

Kyon147 commented 2 years ago

I see your other issue you come to a middle ground solution, I'll close this ticket for now.