Search code examples
node.jscookieschai

Chai.request.agent does not persist cookies between requests


I am encountering a strange issue when working with Chai.request.agent. Below is my test code:

const chai = require("chai");
const chaiHttp = require("chai-http");
chai.use(chaiHttp);
let agent;

beforeEach(() => {
  agent = chai.request.agent(app);
});

afterEach(() => {
  agent.close();
});

it("should do something", async () => {
  //Log in
  const res = await agent.post("/api/my-endpoint").send(userDetails);

  expect(res).to.have.cookie("my-cookie");

  //Test endpoint
  const res2 = await agent.get("/api/my-other-endpoint");
  expect(res2).to.have.status(200);
});

The second request is sent to an endpoint that checks for the presence of my-cookie, returning a 401 if it is not present and a 200 if it is. I am getting a 401 here, and I know it is triggered by the lack of my-cookie as console.logs are logged from the piece of code that handles the check.

Here is the endpoint code:

router.post("/my-endpoint", async function (req, res) {
try {
    const token = jwt.sign(
      { id: 4 },
      process.env.SECRET,
      { expiresIn: "12h" }
    );

    res.cookie("my-cookie", token, {
      httpOnly: true,
      maxAge: 60 * 60 * 12 * 1000,
      secure: true,
      sameSite: "strict",
    });

    res.sendStatus(200);
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: "ERROR: log in failed" });
  }
});

router.get("/my-other-endpoint", async function (req, res) {
  try {
    const cookies = req.cookies;

    if (!cookies?.my-cookie) {
      console.log("No cookie"); //This gets logged
      return res.sendStatus(401);

    } else {
      res.sendStatus(204);
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: "ERROR" });
  }
});

I have tried copying the promise chaining example from the docs but when I use this approach the second request does not run


Solution

  • Set the secure option to false or unset it.

    app.js:

    const express = require('express');
    const cookieParser = require('cookie-parser');
    
    const app = express();
    
    app.use(cookieParser());
    app.post('/my-endpoint', function (req, res) {
      res
        .cookie('my-cookie', 'abc123', {
          httpOnly: true,
          maxAge: 60 * 60 * 12 * 1000,
          // secure: true,
          sameSite: 'strict',
        })
        .sendStatus(200);
    });
    
    app.get('/my-other-endpoint', function (req, res) {
      console.log('my-cookie: ', req.cookies['my-cookie']);
      if (!req.cookies['my-cookie']) {
        console.log('No cookie');
        res.sendStatus(401);
      } else {
        res.sendStatus(204);
      }
    });
    
    module.exports = app;
    

    app.test.js:

    const chai = require('chai');
    const chaiHttp = require('chai-http');
    const app = require('./app');
    
    chai.use(chaiHttp);
    const { expect } = chai;
    
    let agent;
    
    beforeEach(() => {
      agent = chai.request.agent(app);
    });
    
    afterEach(() => {
      agent.close();
    });
    
    it('should do something', async () => {
      const res = await agent.post('/my-endpoint').send({});
      expect(res).to.have.cookie('my-cookie');
      const res2 = await agent.get('/my-other-endpoint');
      expect(res2).to.have.status(204);
    });
    

    Test result:

    my-cookie:  abc123
      ✓ should do something
    
      1 passing (18ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   84.61 |       50 |     100 |   84.61 |                   
     app.js   |   84.61 |       50 |     100 |   84.61 | 21-22             
    ----------|---------|----------|---------|---------|-------------------