Files
n8n-enterprise-unlocked/packages/nodes-base/nodes/Github/__tests__/SearchFunctions.test.ts

500 lines
14 KiB
TypeScript

import type { ILoadOptionsFunctions } from 'n8n-workflow';
import { getUsers, getRepositories, getWorkflows, getRefs } from '../SearchFunctions';
const mockLoadOptionsFunctions = {
getNodeParameter: jest.fn(),
getCredentials: jest.fn().mockResolvedValue({
server: 'https://api.github.com',
}),
helpers: {
requestWithAuthentication: jest.fn(),
},
getCurrentNodeParameter: jest.fn(),
} as unknown as ILoadOptionsFunctions;
describe('Search Functions', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('getUsers', () => {
it('should fetch users', async () => {
const filter = 'test-user';
const responseData = {
items: [
{ login: 'test-user-1', html_url: 'https://github.com/test-user-1' },
{ login: 'test-user-2', html_url: 'https://github.com/test-user-2' },
],
total_count: 2,
};
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getUsers.call(mockLoadOptionsFunctions, filter);
expect(result).toEqual({
results: [
{ name: 'test-user-1', value: 'test-user-1', url: 'https://github.com/test-user-1' },
{ name: 'test-user-2', value: 'test-user-2', url: 'https://github.com/test-user-2' },
],
paginationToken: undefined,
});
expect(mockLoadOptionsFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
'githubOAuth2Api',
expect.objectContaining({
method: 'GET',
qs: expect.objectContaining({ page: 1 }),
}),
);
});
it('should handle pagination', async () => {
const filter = 'test-user';
const responseData = {
items: [
{ login: 'test-user-1', html_url: 'https://github.com/test-user-1' },
{ login: 'test-user-2', html_url: 'https://github.com/test-user-2' },
],
total_count: 200,
};
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getUsers.call(mockLoadOptionsFunctions, filter);
expect(result).toEqual({
results: [
{ name: 'test-user-1', value: 'test-user-1', url: 'https://github.com/test-user-1' },
{ name: 'test-user-2', value: 'test-user-2', url: 'https://github.com/test-user-2' },
],
paginationToken: 2,
});
});
it('should use paginationToken when provided', async () => {
const filter = 'test-user';
const paginationToken = '3';
const responseData = {
items: [
{ login: 'test-user-5', html_url: 'https://github.com/test-user-5' },
{ login: 'test-user-6', html_url: 'https://github.com/test-user-6' },
],
total_count: 200,
};
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getUsers.call(mockLoadOptionsFunctions, filter, paginationToken);
expect(result).toEqual({
results: [
{ name: 'test-user-5', value: 'test-user-5', url: 'https://github.com/test-user-5' },
{ name: 'test-user-6', value: 'test-user-6', url: 'https://github.com/test-user-6' },
],
paginationToken: undefined,
});
expect(mockLoadOptionsFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
'githubOAuth2Api',
expect.objectContaining({
method: 'GET',
qs: expect.objectContaining({ page: 3 }),
}),
);
});
});
describe('getRepositories', () => {
it('should fetch repositories', async () => {
const filter = 'test-repo';
const owner = 'test-owner';
const responseData = {
items: [
{ name: 'test-repo-1', html_url: 'https://github.com/test-owner/test-repo-1' },
{ name: 'test-repo-2', html_url: 'https://github.com/test-owner/test-repo-2' },
],
total_count: 2,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockReturnValue(owner);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getRepositories.call(mockLoadOptionsFunctions, filter);
expect(result).toEqual({
results: [
{
name: 'test-repo-1',
value: 'test-repo-1',
url: 'https://github.com/test-owner/test-repo-1',
},
{
name: 'test-repo-2',
value: 'test-repo-2',
url: 'https://github.com/test-owner/test-repo-2',
},
],
paginationToken: undefined,
});
});
it('should fetch repositories without filter', async () => {
const owner = 'test-owner';
const responseData = {
items: [
{ name: 'test-repo-1', html_url: 'https://github.com/test-owner/test-repo-1' },
{ name: 'test-repo-2', html_url: 'https://github.com/test-owner/test-repo-2' },
],
total_count: 2,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockReturnValue(owner);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getRepositories.call(mockLoadOptionsFunctions);
expect(result).toEqual({
results: [
{
name: 'test-repo-1',
value: 'test-repo-1',
url: 'https://github.com/test-owner/test-repo-1',
},
{
name: 'test-repo-2',
value: 'test-repo-2',
url: 'https://github.com/test-owner/test-repo-2',
},
],
paginationToken: undefined,
});
});
it('should use paginationToken when provided', async () => {
const filter = 'test-repo';
const paginationToken = '3';
const owner = 'test-owner';
const responseData = {
items: [
{ name: 'test-repo-5', html_url: 'https://github.com/test-owner/test-repo-5' },
{ name: 'test-repo-6', html_url: 'https://github.com/test-owner/test-repo-6' },
],
total_count: 200,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockReturnValue(owner);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getRepositories.call(mockLoadOptionsFunctions, filter, paginationToken);
expect(result).toEqual({
results: [
{
name: 'test-repo-5',
value: 'test-repo-5',
url: 'https://github.com/test-owner/test-repo-5',
},
{
name: 'test-repo-6',
value: 'test-repo-6',
url: 'https://github.com/test-owner/test-repo-6',
},
],
paginationToken: undefined,
});
expect(mockLoadOptionsFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
'githubOAuth2Api',
expect.objectContaining({
method: 'GET',
qs: expect.objectContaining({ page: 3 }),
}),
);
});
it('should handle empty repositories', async () => {
const filter = 'test-repo';
const owner = 'test-owner';
const responseData = {
items: [],
total_count: 0,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockReturnValue(owner);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getRepositories.call(mockLoadOptionsFunctions, filter);
expect(result).toEqual({
results: [],
paginationToken: undefined,
});
});
});
describe('getWorkflows', () => {
it('should fetch workflows', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const responseData = {
workflows: [
{ id: '1', name: 'workflow-1' },
{ id: '2', name: 'workflow-2' },
],
total_count: 2,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock)
.mockReturnValueOnce(owner)
.mockReturnValueOnce(repository);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getWorkflows.call(mockLoadOptionsFunctions);
expect(result).toEqual({
results: [
{ name: 'workflow-1', value: '1' },
{ name: 'workflow-2', value: '2' },
],
paginationToken: undefined,
});
});
it('should handle pagination', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const responseData = {
workflows: [
{ id: '1', name: 'workflow-1' },
{ id: '2', name: 'workflow-2' },
],
total_count: 200,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock)
.mockReturnValueOnce(owner)
.mockReturnValueOnce(repository);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getWorkflows.call(mockLoadOptionsFunctions);
expect(result).toEqual({
results: [
{ name: 'workflow-1', value: '1' },
{ name: 'workflow-2', value: '2' },
],
paginationToken: 2,
});
});
it('should use paginationToken when provided and return next page token', async () => {
const paginationToken = '1';
const owner = 'test-owner';
const repository = 'test-repo';
const responseData = {
workflows: [
{ id: '3', name: 'workflow-3' },
{ id: '4', name: 'workflow-4' },
],
total_count: 300,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock)
.mockReturnValueOnce(owner)
.mockReturnValueOnce(repository);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getWorkflows.call(mockLoadOptionsFunctions, paginationToken);
expect(result).toEqual({
results: [
{ name: 'workflow-3', value: '3' },
{ name: 'workflow-4', value: '4' },
],
paginationToken: 2,
});
expect(mockLoadOptionsFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
'githubOAuth2Api',
expect.objectContaining({
method: 'GET',
qs: expect.objectContaining({ page: 1 }),
}),
);
});
it('should handle empty workflows', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const responseData = {
workflows: [],
total_count: 0,
};
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock)
.mockReturnValueOnce(owner)
.mockReturnValueOnce(repository);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
responseData,
);
const result = await getWorkflows.call(mockLoadOptionsFunctions);
expect(result).toEqual({
results: [],
paginationToken: undefined,
});
});
});
describe('getRefs', () => {
it('should fetch branches and tags using git/refs endpoint', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const refsResponse = [
{ ref: 'refs/heads/Main' },
{ ref: 'refs/heads/Dev' },
{ ref: 'refs/tags/v1.0.0' },
{ ref: 'refs/tags/v2.0.0' },
{ ref: 'refs/Pull/123/head' },
];
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockImplementation(
(param: string) => {
if (param === 'owner') return owner;
if (param === 'repository') return repository;
},
);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
refsResponse,
);
const result = await getRefs.call(mockLoadOptionsFunctions);
expect(result).toEqual({
results: [
{ name: 'Main', value: 'Main', description: 'Branch: Main' },
{ name: 'Dev', value: 'Dev', description: 'Branch: Dev' },
{ name: 'v1.0.0', value: 'v1.0.0', description: 'Tag: v1.0.0' },
{ name: 'v2.0.0', value: 'v2.0.0', description: 'Tag: v2.0.0' },
{ name: '123/head', value: '123/head', description: 'Pull: 123/head' },
],
paginationToken: undefined,
});
});
it('should use paginationToken when provided', async () => {
const paginationToken = '3';
const owner = 'test-owner';
const repository = 'test-repo';
const refsResponse = [{ ref: 'refs/heads/branch-5' }, { ref: 'refs/heads/branch-6' }];
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockImplementation(
(param: string) => {
if (param === 'owner') return owner;
if (param === 'repository') return repository;
},
);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
refsResponse,
);
const result = await getRefs.call(mockLoadOptionsFunctions, undefined, paginationToken);
expect(result).toEqual({
results: [
{ name: 'branch-5', value: 'branch-5', description: 'Branch: branch-5' },
{ name: 'branch-6', value: 'branch-6', description: 'Branch: branch-6' },
],
paginationToken: undefined,
});
expect(mockLoadOptionsFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
'githubOAuth2Api',
expect.objectContaining({
method: 'GET',
qs: expect.objectContaining({ page: 3 }),
}),
);
});
it('should filter refs based on the provided filter', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const refsResponse = [
{ ref: 'refs/heads/main' },
{ ref: 'refs/heads/dev' },
{ ref: 'refs/tags/v1.0.0' },
{ ref: 'refs/tags/v2.0.0' },
];
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockImplementation(
(param: string) => {
if (param === 'owner') return owner;
if (param === 'repository') return repository;
},
);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
refsResponse,
);
const result = await getRefs.call(mockLoadOptionsFunctions, 'v1');
expect(result).toEqual({
results: [{ name: 'v1.0.0', value: 'v1.0.0', description: 'Tag: v1.0.0' }],
});
});
it('should handle pagination correctly', async () => {
const owner = 'test-owner';
const repository = 'test-repo';
const refsResponse = Array(100)
.fill(0)
.map((_, i) => ({
ref: i % 2 === 0 ? `refs/heads/branch-${i}` : `refs/tags/tag-${i}`,
}));
(mockLoadOptionsFunctions.getCurrentNodeParameter as jest.Mock).mockImplementation(
(param: string) => {
if (param === 'owner') return owner;
if (param === 'repository') return repository;
},
);
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
refsResponse,
);
const result = await getRefs.call(mockLoadOptionsFunctions);
expect(result.paginationToken).toBe(2);
expect(result.results.length).toBe(100);
});
});
});