0

So i have this test doing in my typescript project and when i do the mock tests two test have been failing all the issues are related to TypeError [Error]: Cannot redefine property: fetchAndNormalizePullRequests i looked all the way and couldn't find a way to fix the issue. this is the

pull-request.handler.test.ts

import { test, describe, beforeEach, afterEach, mock } from 'node:test';
import assert from 'node:assert';
import type { Request, Response } from 'express';
import { getAllPullRequests } from '../../handlers/pull-requests.handler.js';

const originalEnv = process.env;
// const pullRequestsService = await import('../../services/pull-requests.service.js');

describe('Pull Requests Handler', () => {
  let mockReq: Partial<Request>;
  let mockRes: Partial<Response>;
  let statusMock: any;
  let jsonMock: any;
  let fetchMock: any;

  beforeEach(() => {
    mock.restoreAll();

    statusMock = mock.fn(() => mockRes);
    jsonMock = mock.fn();
    
    mockRes = {
      status: statusMock,
      json: jsonMock
    };

    process.env = { ...originalEnv };
  });

  afterEach(() => {
    process.env = originalEnv;
    mock.restoreAll();
  });

  test('should return 500 when GITHUB_API_TOKEN is missing', async () => {
    delete process.env.GITHUB_API_TOKEN;
    mockReq = {
      query: { owner: 'testowner', repo: 'testrepo' }
    };

    await getAllPullRequests(mockReq as Request, mockRes as Response);

    assert.strictEqual(statusMock.mock.callCount(), 1);
    assert.deepStrictEqual(statusMock.mock.calls[0].arguments, [500]);
    assert.strictEqual(jsonMock.mock.callCount(), 1);
    assert.deepStrictEqual(jsonMock.mock.calls[0].arguments, [{ error: 'Missing environment variables.' }]);
  });

  test('should return 500 when owner is missing', async () => {
    process.env.GITHUB_API_TOKEN = 'test-token';
    mockReq = {
      query: { repo: 'testrepo' }
    };

    try{
    await getAllPullRequests(mockReq as Request, mockRes as Response);
    }
    catch(e){
      console.error(e);
    }

    assert.strictEqual(statusMock.mock.callCount(), 1);
    assert.deepStrictEqual(statusMock.mock.calls[0].arguments, [500]);
    assert.strictEqual(jsonMock.mock.callCount(), 1);
    assert.deepStrictEqual(jsonMock.mock.calls[0].arguments, [{ error: 'Missing environment variables.' }]);
  });

  test('should return 500 when repo is missing', async () => {
    process.env.GITHUB_API_TOKEN = 'test-token';
    mockReq = {
      query: { owner: 'testowner' }
    };

    await getAllPullRequests(mockReq as Request, mockRes as Response);

    assert.strictEqual(statusMock.mock.callCount(), 1);
    assert.deepStrictEqual(statusMock.mock.calls[0].arguments, [500]);
    assert.strictEqual(jsonMock.mock.callCount(), 1);
    assert.deepStrictEqual(jsonMock.mock.calls[0].arguments, [{ error: 'Missing environment variables.' }]);
  });

  test('should return 200 with pull requests when all parameters are valid', async () => {
    process.env.GITHUB_API_TOKEN = 'test-token';
    mockReq = {
      query: { owner: 'testowner', repo: 'testrepo' }
    };

    const mockPullRequests = [
      {
        prNumber: 1,
        title: 'Test PR',
        creator: 'testuser',
        creationTimestamp: '2024-01-01T00:00:00Z',
        requestedReviewers: ['reviewer1'],
        lastActionType: 'open',
        lastActionTimestamp: '2024-01-01T00:00:00Z'
      }
    ];

    const fetchMock = mock.fn(() => Promise.resolve(mockPullRequests));
    mock.method(await import('../../services/pull-requests.service.js'), 'fetchAndNormalizePullRequests', fetchMock);

    await getAllPullRequests(mockReq as Request, mockRes as Response);

    assert.strictEqual(statusMock.mock.callCount(), 1);
    assert.deepStrictEqual(statusMock.mock.calls[0].arguments, [200]);
    assert.strictEqual(jsonMock.mock.callCount(), 1);
    assert.deepStrictEqual(jsonMock.mock.calls[0].arguments, [mockPullRequests]);
  });

  test('should return 500 when service throws an error', async () => {
    process.env.GITHUB_API_TOKEN = 'test-token';
    mockReq = {
      query: { owner: 'testowner', repo: 'testrepo' }
    };

    const fetchMock = mock.fn(() => Promise.reject(new Error('Service error')));
    mock.method(await import('../../services/pull-requests.service.js'), 'fetchAndNormalizePullRequests', fetchMock);

    await getAllPullRequests(mockReq as Request, mockRes as Response);

    assert.strictEqual(statusMock.mock.callCount(), 1);
    assert.deepStrictEqual(statusMock.mock.calls[0].arguments, [500]);
    assert.strictEqual(jsonMock.mock.callCount(), 1);
    assert.deepStrictEqual(jsonMock.mock.calls[0].arguments, [{ error: 'Internal server error.' }]);
  });
});

and this is my fetchAndNormalizePullRequest function

import axios from 'axios';
import type{ NormalizedPR } from '../types/types.js';

export const fetchAndNormalizePullRequests = async (owner: string, repo: string, token: string): Promise<NormalizedPR[]> => {
  const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=all`;

  try {
    const response = await axios.get(url, {
      headers: {
        Authorization: `token ${token}`,
        'Content-Type': 'application/json',
      },
    });

    return response.data.map((pr: any) => ({
      prNumber: pr.number,
      title: pr.title,
      creator: pr.user.login,
      creationTimestamp: pr.created_at,
      requestedReviewers: pr.requested_reviewers.map((reviewer: any) => reviewer.login),
      lastActionType: pr.merged_at ? 'merged' : pr.state,
      lastActionTimestamp: pr.updated_at,
    }));
  } catch (error) {
    throw new Error('Failed to fetch pull requests from GitHub API.');
  }
};

what am i doing wrong? can't fix the issue

1
  • fetchAndNormalizePullRequests is a named ESM export from pull-requests.service.js. The thing you get from await import('../../services/pull-requests.service.js') is a module namespace object. Module namespace properties that correspond to named exports are immutable and non-configurable. Commented Sep 22 at 15:32

1 Answer 1

0

When you

mock.method(await import('../../services/pull-requests.service.js'), 'fetchAndNormalizePullRequests', fetchMock);

You're trying to mock a method, but your pull-requests.service.js is a module, so in essence you're trying to change a ESM module instead of the method.

To achieve what you want, you should do something like:


/** placeholder variables to be assigned later */
let getAllPullRequests;
let mockedFetchAndNormalizePullRequests;

describe('Pull requests handler', async () => {
  const mockedPullRequestsValue = [];

  before(async () => {
    // setup the mock fn
    mockedFetchAndNormalizePullRequests = mock.fn(async () => mockedPullRequestsValue);
    
    // mock the module and provide the stub for the method
    mock.module('../../services/pull-requests.service.js', {
      fetchAndNormalizePullRequests: mockedFetchAndNormalizePullRequests,
    });

    // import (and assign to the variable) the function AFTER mocking the method on the module
    ({ getAllPullRequests } = import('../../handlers/pull-requests.handler.js')); 
  })

  beforeEach(() => {
    mockedPullRequestsValue.length = 0; // clear the mocked values
  })

  after(() => {
    mock.restoreAll();
  })

  test('Your test', async () => {
    mockedPullRequestsValue.push({yourObject: 'your value'}) // setup the return value you want
    await getAllPullRequests(/* params */); // call your function
    
    /* make your asserts */

  })
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.