How to mock supabase APi select requests in nodejs

340 views Asked by At

I use supabase for my express js backend. This is how I have defined my supabase client:

const config = require('../config/env');
const supabase = require('@supabase/supabase-js');
const supabaseClient = supabase.createClient(
  config.supabase.url,
  config.supabase.key,
  {
    auth: {
      autoRefreshToken: false,
      persistSession: false,
      detectSessionInUrl: false,
    },
  }
);

module.exports = supabaseClient;

An example call to the database can be something like this:

 const query = supabaseClient
    .from('items')
    .select('*')
    .eq('id', id);


  const { data, error } = await executeWithRetry(async () => await query);

I am writing some tests to test this logic and I would like to mock the supabase calls. This is my jest mock for supabase:

jest.mock('@supabase/supabase-js', () => {
  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          execute: jest.fn().mockImplementation(() => {
            return Promise.resolve({
              data: [{ id: 'some-uuid-text' }],
              error: null,
            });
          }),
        })),
      };
    }),
  };
});

and this is the test

describe('userPermissions', () => {
  it('should fetch user permissions', async () => {
    const email = '[email protected]';
    const result = await userPermissions({ email });
    expect(result).toEqual([{ role_id: 'mockedRoleId' }]);
  });
});

The userPermissions method has a query that attempts to fetch data from supabase which keeps on responding with undefined data and undefined error. My expectation is that it should respond with what's in my execute part of the mock

return Promise.resolve({
              data: [{ id: 'some-uuid-text' }],
              error: null,
            });

What could be causing the issue?

1

There are 1 answers

0
John Mboga On

The logic that I was using seemed correct but supabase doesn't have the execute function as part of its functions that's why the supabase calls were not responding with any response because I wasn't passing any data or error. Updating the mock with data and error to this fixes the issue:

jest.mock('@supabase/supabase-js', () => {
  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          data: [
            {
              id: 'mockedRoleId',
              org_users: [{ id: 'mockUserId', status: 'active' }],
            },
          ],
          error: null,
        })),
        update: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          select: jest.fn().mockReturnThis(),
          data: [
            {
              id: 'mockedRoleId',
              org_users: [{ id: 'mockUserId', status: 'active' }],
            },
          ],
          error: null,
        })),
      };
    }),
  };
});

To make the mocks more reusable, I went with a variant where I could define the expected data for every test, as seen below:

jest.mock('@supabase/supabase-js', () => {
  let testData = [
    {
      id: 'mockedRoleId',
      org_users: [{ id: 'mockUserId', status: 'active' }],
    },
  ];

  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          data: testData, // Use the data variable here
          error: null,
        })),
        update: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          select: jest.fn().mockReturnThis(),
          data: testData, // Use the data variable here
          error: null,
        })),
      };
    }),
    setTestData: (newData) => {
      testData = newData;
    },
  };
});

This is how the mock is used in the actual tests:

describe('activateUser', () => {
  beforeEach(() => {
    const { setTestData } = require('@supabase/supabase-js');
    setTestData([]);
  });

  test(
    'should throw in error when no active user exists',
    async () => {
      const userEmail = '[email protected]';
      const userProfileId = 'some-random-uuid';
      const tenantId = 'some-random-uuid';

      expect(() => {
        activateUser({ userProfileId, userEmail, tenantId });
      }).toThrowError('User does not exist');
    }
  );

  it('should activate user account', async () => {
    const { setTestData } = require('@supabase/supabase-js');
    setTestData([
      {
        id: 'mockedRoleId',
        org_users: [{ id: 'mockUserId', status: 'active' }],
      },
    ]);
    const userEmail = '[email protected]';
    const userProfileId = 'ab3b2cb0-c032-42d8-b1d2-c78de679189f';
    const tenantId = 'a76b1802-0acb-4e9c-a157-d4c8239f6c2a';

    activateUser({ userProfileId, userEmail, tenantId });
  });
});