first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import type { IExecuteFunctions, IHookFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
githubApiRequest,
|
||||
getFileSha,
|
||||
githubApiRequestAllItems,
|
||||
isBase64,
|
||||
validateJSON,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
const mockExecuteHookFunctions = {
|
||||
getNodeParameter: jest.fn().mockImplementation((param: string) => {
|
||||
if (param === 'authentication') return 'accessToken';
|
||||
return undefined;
|
||||
}),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
}),
|
||||
helpers: {
|
||||
requestWithAuthentication: jest.fn(),
|
||||
},
|
||||
getCurrentNodeParameter: jest.fn(),
|
||||
getWebhookName: jest.fn(),
|
||||
getWebhookDescription: jest.fn(),
|
||||
getNodeWebhookUrl: jest.fn(),
|
||||
getNode: jest.fn().mockReturnValue({
|
||||
id: 'test-node-id',
|
||||
name: 'test-node',
|
||||
}),
|
||||
} as unknown as IExecuteFunctions | IHookFunctions;
|
||||
|
||||
describe('GenericFunctions', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('githubApiRequest', () => {
|
||||
it('should make a successful API request', async () => {
|
||||
const method = 'GET';
|
||||
const endpoint = '/repos/test-owner/test-repo';
|
||||
const body = {};
|
||||
const responseData = { id: 123, name: 'test-repo' };
|
||||
|
||||
(mockExecuteHookFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
const result = await githubApiRequest.call(mockExecuteHookFunctions, method, endpoint, body);
|
||||
|
||||
expect(result).toEqual(responseData);
|
||||
expect(mockExecuteHookFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'githubApi',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'User-Agent': 'n8n' },
|
||||
body: {},
|
||||
qs: undefined,
|
||||
uri: 'https://api.github.com/repos/test-owner/test-repo',
|
||||
json: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a NodeApiError on API failure', async () => {
|
||||
const method = 'GET';
|
||||
const endpoint = '/repos/test-owner/test-repo';
|
||||
const body = {};
|
||||
const error = new Error('API Error');
|
||||
|
||||
(mockExecuteHookFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
error,
|
||||
);
|
||||
|
||||
await expect(
|
||||
githubApiRequest.call(mockExecuteHookFunctions, method, endpoint, body),
|
||||
).rejects.toThrow(NodeApiError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFileSha', () => {
|
||||
it('should return the SHA of a file', async () => {
|
||||
const owner = 'test-owner';
|
||||
const repository = 'test-repo';
|
||||
const filePath = 'README.md';
|
||||
const branch = 'main';
|
||||
const responseData = { sha: 'abc123' };
|
||||
|
||||
(mockExecuteHookFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
const result = await getFileSha.call(
|
||||
mockExecuteHookFunctions,
|
||||
owner,
|
||||
repository,
|
||||
filePath,
|
||||
branch,
|
||||
);
|
||||
|
||||
expect(result).toBe('abc123');
|
||||
expect(mockExecuteHookFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'githubApi',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'User-Agent': 'n8n' },
|
||||
body: {},
|
||||
qs: { ref: 'main' },
|
||||
uri: 'https://api.github.com/repos/test-owner/test-repo/contents/README.md',
|
||||
json: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a NodeOperationError if SHA is missing', async () => {
|
||||
const owner = 'test-owner';
|
||||
const repository = 'test-repo';
|
||||
const filePath = 'README.md';
|
||||
const responseData = {};
|
||||
|
||||
(mockExecuteHookFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
await expect(
|
||||
getFileSha.call(mockExecuteHookFunctions, owner, repository, filePath),
|
||||
).rejects.toThrow(NodeOperationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('githubApiRequestAllItems', () => {
|
||||
it('should fetch all items with pagination', async () => {
|
||||
const method = 'GET';
|
||||
const endpoint = '/repos/test-owner/test-repo/issues';
|
||||
const body = {};
|
||||
const query = { state: 'open' };
|
||||
const responseData1 = [{ id: 1, title: 'Issue 1' }];
|
||||
const responseData2 = [{ id: 2, title: 'Issue 2' }];
|
||||
|
||||
(mockExecuteHookFunctions.helpers.requestWithAuthentication as jest.Mock)
|
||||
.mockResolvedValueOnce({ headers: { link: 'next' }, body: responseData1 })
|
||||
.mockResolvedValueOnce({ headers: {}, body: responseData2 });
|
||||
|
||||
const result = await githubApiRequestAllItems.call(
|
||||
mockExecuteHookFunctions,
|
||||
method,
|
||||
endpoint,
|
||||
body,
|
||||
query,
|
||||
);
|
||||
|
||||
expect(result).toEqual([...responseData1, ...responseData2]);
|
||||
expect(mockExecuteHookFunctions.helpers.requestWithAuthentication).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBase64', () => {
|
||||
it('should return true for valid Base64 strings', () => {
|
||||
expect(isBase64('aGVsbG8gd29ybGQ=')).toBe(true);
|
||||
expect(isBase64('Zm9vYmFy')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for invalid Base64 strings', () => {
|
||||
expect(isBase64('not base64')).toBe(false);
|
||||
expect(isBase64('123!@#')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateJSON', () => {
|
||||
it('should return parsed JSON for valid JSON strings', () => {
|
||||
const jsonString = '{"key": "value"}';
|
||||
const result = validateJSON(jsonString);
|
||||
|
||||
expect(result).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
it('should return undefined for invalid JSON strings', () => {
|
||||
const invalidJsonString = 'not json';
|
||||
const result = validateJSON(invalidJsonString);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,261 @@
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import { Github } from '../Github.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
...jest.requireActual('../GenericFunctions'),
|
||||
githubApiRequest: jest.fn(),
|
||||
getFileSha: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Github Node - File Create/Edit Operations', () => {
|
||||
let github: Github;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
github = new Github();
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions = {
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
getNode: jest.fn().mockReturnValue({
|
||||
id: 'test-node-id',
|
||||
name: 'Github',
|
||||
type: 'n8n-nodes-base.github',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
}),
|
||||
helpers: {
|
||||
assertBinaryData: jest.fn(),
|
||||
getBinaryDataBuffer: jest.fn(),
|
||||
requestWithAuthentication: jest.fn(),
|
||||
returnJsonArray: jest.fn((data) => (Array.isArray(data) ? data : [data])),
|
||||
constructExecutionMetaData: jest.fn((data) => data),
|
||||
},
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
accessToken: 'test-token',
|
||||
server: 'https://api.github.com',
|
||||
}),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
} as unknown as jest.Mocked<IExecuteFunctions>;
|
||||
});
|
||||
|
||||
describe('File Create - Binary Data', () => {
|
||||
it('should handle binary data by converting buffer to base64', async () => {
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation(
|
||||
(paramName: string, _itemIndex: number, fallback?: any) => {
|
||||
const params: Record<string, any> = {
|
||||
resource: 'file',
|
||||
operation: 'create',
|
||||
owner: 'test-owner',
|
||||
repository: 'test-repo',
|
||||
filePath: 'test/file.txt',
|
||||
commitMessage: 'Add test file',
|
||||
binaryData: true,
|
||||
binaryPropertyName: 'data',
|
||||
additionalParameters: {},
|
||||
};
|
||||
return params[paramName] ?? fallback;
|
||||
},
|
||||
);
|
||||
|
||||
const mockBinaryData = {
|
||||
id: 'test-id',
|
||||
data: 'base64data',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const expectedBuffer = Buffer.from('test content');
|
||||
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockReturnValue(mockBinaryData);
|
||||
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
||||
expectedBuffer,
|
||||
);
|
||||
|
||||
(GenericFunctions.githubApiRequest as jest.Mock).mockResolvedValue({
|
||||
content: {
|
||||
name: 'file.txt',
|
||||
path: 'test/file.txt',
|
||||
sha: 'abc123',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await github.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(mockExecuteFunctions.helpers.getBinaryDataBuffer).toHaveBeenCalledWith(0, 'data');
|
||||
|
||||
expect(GenericFunctions.githubApiRequest).toHaveBeenCalledWith(
|
||||
'PUT',
|
||||
'/repos/test-owner/test-repo/contents/test%2Ffile.txt',
|
||||
expect.objectContaining({
|
||||
content: expectedBuffer.toString('base64'),
|
||||
message: 'Add test file',
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('File Create - Text Content', () => {
|
||||
it('should use base64 content as-is when fileContent is already base64', async () => {
|
||||
const base64Content = 'dGVzdCBjb250ZW50';
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation(
|
||||
(paramName: string, _itemIndex: number, fallback?: any) => {
|
||||
const params: Record<string, any> = {
|
||||
resource: 'file',
|
||||
operation: 'create',
|
||||
owner: 'test-owner',
|
||||
repository: 'test-repo',
|
||||
filePath: 'test/file.txt',
|
||||
commitMessage: 'Add test file',
|
||||
binaryData: false,
|
||||
fileContent: base64Content,
|
||||
additionalParameters: {},
|
||||
};
|
||||
return params[paramName] ?? fallback;
|
||||
},
|
||||
);
|
||||
|
||||
(GenericFunctions.githubApiRequest as jest.Mock).mockResolvedValue({
|
||||
content: {
|
||||
name: 'file.txt',
|
||||
path: 'test/file.txt',
|
||||
sha: 'abc123',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await github.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(GenericFunctions.githubApiRequest).toHaveBeenCalledWith(
|
||||
'PUT',
|
||||
'/repos/test-owner/test-repo/contents/test%2Ffile.txt',
|
||||
expect.objectContaining({
|
||||
content: base64Content,
|
||||
message: 'Add test file',
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should convert plain text to base64 when fileContent is not base64', async () => {
|
||||
const plainTextContent = 'Hello, World! This is plain text.';
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation(
|
||||
(paramName: string, _itemIndex: number, fallback?: any) => {
|
||||
const params: Record<string, any> = {
|
||||
resource: 'file',
|
||||
operation: 'create',
|
||||
owner: 'test-owner',
|
||||
repository: 'test-repo',
|
||||
filePath: 'test/file.txt',
|
||||
commitMessage: 'Add test file',
|
||||
binaryData: false,
|
||||
fileContent: plainTextContent,
|
||||
additionalParameters: {},
|
||||
};
|
||||
return params[paramName] ?? fallback;
|
||||
},
|
||||
);
|
||||
|
||||
(GenericFunctions.githubApiRequest as jest.Mock).mockResolvedValue({
|
||||
content: {
|
||||
name: 'file.txt',
|
||||
path: 'test/file.txt',
|
||||
sha: 'abc123',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await github.execute.call(mockExecuteFunctions);
|
||||
|
||||
const expectedBase64 = Buffer.from(plainTextContent).toString('base64');
|
||||
|
||||
expect(GenericFunctions.githubApiRequest).toHaveBeenCalledWith(
|
||||
'PUT',
|
||||
'/repos/test-owner/test-repo/contents/test%2Ffile.txt',
|
||||
expect.objectContaining({
|
||||
content: expectedBase64,
|
||||
message: 'Add test file',
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('File Edit - Binary Data', () => {
|
||||
it('should get file SHA and convert buffer to base64 for edit operation', async () => {
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation(
|
||||
(paramName: string, _itemIndex: number, fallback?: any) => {
|
||||
const params: Record<string, any> = {
|
||||
resource: 'file',
|
||||
operation: 'edit',
|
||||
owner: 'test-owner',
|
||||
repository: 'test-repo',
|
||||
filePath: 'test/file.txt',
|
||||
commitMessage: 'Update test file',
|
||||
binaryData: true,
|
||||
binaryPropertyName: 'data',
|
||||
additionalParameters: {},
|
||||
};
|
||||
return params[paramName] ?? fallback;
|
||||
},
|
||||
);
|
||||
|
||||
const mockBinaryData = {
|
||||
id: 'test-id',
|
||||
data: 'old-base64-data',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const expectedBuffer = Buffer.from('updated content');
|
||||
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockReturnValue(mockBinaryData);
|
||||
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
||||
expectedBuffer,
|
||||
);
|
||||
|
||||
(GenericFunctions.getFileSha as jest.Mock).mockResolvedValue('existing-sha-123');
|
||||
|
||||
(GenericFunctions.githubApiRequest as jest.Mock).mockResolvedValue({
|
||||
content: {
|
||||
name: 'file.txt',
|
||||
path: 'test/file.txt',
|
||||
sha: 'new-sha-456',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await github.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(GenericFunctions.getFileSha).toHaveBeenCalledWith(
|
||||
'test-owner',
|
||||
'test-repo',
|
||||
'test/file.txt',
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(mockExecuteFunctions.helpers.getBinaryDataBuffer).toHaveBeenCalledWith(0, 'data');
|
||||
|
||||
expect(GenericFunctions.githubApiRequest).toHaveBeenCalledWith(
|
||||
'PUT',
|
||||
'/repos/test-owner/test-repo/contents/test%2Ffile.txt',
|
||||
expect.objectContaining({
|
||||
content: expectedBuffer.toString('base64'),
|
||||
message: 'Update test file',
|
||||
sha: 'existing-sha-123',
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Github Node - Organization getRepositories', () => {
|
||||
const credentials = {
|
||||
githubApi: {
|
||||
accessToken: 'test-token',
|
||||
server: 'https://api.github.com',
|
||||
user: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Basic getRepositories Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/orgs/testorg/repos')
|
||||
.query(true)
|
||||
.reply(200, [
|
||||
{
|
||||
id: 1296269,
|
||||
name: 'hello-world',
|
||||
full_name: 'testorg/hello-world',
|
||||
owner: {
|
||||
login: 'testorg',
|
||||
id: 1,
|
||||
type: 'Organization',
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testorg/hello-world',
|
||||
description: 'My first repository on GitHub!',
|
||||
fork: false,
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
clone_url: 'https://github.com/testorg/hello-world.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
forks_count: 9,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
},
|
||||
visibility: 'public',
|
||||
default_branch: 'master',
|
||||
},
|
||||
{
|
||||
id: 1296270,
|
||||
name: 'test-repo',
|
||||
full_name: 'testorg/test-repo',
|
||||
owner: {
|
||||
login: 'testorg',
|
||||
id: 1,
|
||||
type: 'Organization',
|
||||
},
|
||||
private: true,
|
||||
html_url: 'https://github.com/testorg/test-repo',
|
||||
description: 'Test repository',
|
||||
fork: false,
|
||||
created_at: '2011-01-27T19:01:12Z',
|
||||
updated_at: '2011-01-27T19:14:43Z',
|
||||
pushed_at: '2011-01-27T19:06:43Z',
|
||||
clone_url: 'https://github.com/testorg/test-repo.git',
|
||||
size: 256,
|
||||
stargazers_count: 42,
|
||||
watchers_count: 15,
|
||||
language: 'JavaScript',
|
||||
forks_count: 3,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 2,
|
||||
license: {
|
||||
key: 'apache-2.0',
|
||||
name: 'Apache License 2.0',
|
||||
},
|
||||
visibility: 'private',
|
||||
default_branch: 'main',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getRepositories.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Paginated getRepositories Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/orgs/testorg/repos')
|
||||
.query({ per_page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
id: 1296269,
|
||||
name: 'hello-world',
|
||||
full_name: 'testorg/hello-world',
|
||||
owner: {
|
||||
login: 'testorg',
|
||||
id: 1,
|
||||
type: 'Organization',
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testorg/hello-world',
|
||||
description: 'My first repository on GitHub!',
|
||||
fork: false,
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
clone_url: 'https://github.com/testorg/hello-world.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
forks_count: 9,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
},
|
||||
visibility: 'public',
|
||||
default_branch: 'master',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getRepositoriesLimit.workflow.json'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,374 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Github Node - Repository getIssues', () => {
|
||||
const credentials = {
|
||||
githubApi: {
|
||||
accessToken: 'test-token',
|
||||
server: 'https://api.github.com',
|
||||
user: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Basic getIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/repos/testowner/testrepo/issues')
|
||||
.query(true)
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/1',
|
||||
repository_url: 'https://api.github.com/repos/testowner/testrepo',
|
||||
labels_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/comments',
|
||||
events_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/events',
|
||||
html_url: 'https://github.com/testowner/testrepo/issues/1',
|
||||
id: 1,
|
||||
number: 1,
|
||||
title: 'Found a bug',
|
||||
user: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045946,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=',
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/labels/bug',
|
||||
name: 'bug',
|
||||
description: "Something isn't working",
|
||||
color: 'd73a49',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: null,
|
||||
assignees: [],
|
||||
milestone: null,
|
||||
comments: 0,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'COLLABORATOR',
|
||||
active_lock_reason: null,
|
||||
body: "I'm having a problem with this.",
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/1/reactions',
|
||||
total_count: 0,
|
||||
'+1': 0,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
},
|
||||
{
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/2',
|
||||
repository_url: 'https://api.github.com/repos/testowner/testrepo',
|
||||
labels_url: 'https://api.github.com/repos/testowner/testrepo/issues/2/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/testowner/testrepo/issues/2/comments',
|
||||
events_url: 'https://api.github.com/repos/testowner/testrepo/issues/2/events',
|
||||
html_url: 'https://github.com/testowner/testrepo/issues/2',
|
||||
id: 2,
|
||||
number: 2,
|
||||
title: 'Feature request',
|
||||
user: {
|
||||
login: 'anotheruser',
|
||||
id: 2,
|
||||
node_id: 'MDQ6VXNlcjI=',
|
||||
avatar_url: 'https://github.com/images/error/anotheruser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/anotheruser',
|
||||
html_url: 'https://github.com/anotheruser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045947,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDc=',
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/labels/enhancement',
|
||||
name: 'enhancement',
|
||||
description: 'New feature or request',
|
||||
color: 'a2eeef',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'assigneduser',
|
||||
id: 3,
|
||||
node_id: 'MDQ6VXNlcjM=',
|
||||
avatar_url: 'https://github.com/images/error/assigneduser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/assigneduser',
|
||||
html_url: 'https://github.com/assigneduser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'assigneduser',
|
||||
id: 3,
|
||||
node_id: 'MDQ6VXNlcjM=',
|
||||
avatar_url: 'https://github.com/images/error/assigneduser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/assigneduser',
|
||||
html_url: 'https://github.com/assigneduser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: {
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/milestones/1',
|
||||
html_url: 'https://github.com/testowner/testrepo/milestone/1',
|
||||
labels_url: 'https://api.github.com/repos/testowner/testrepo/milestones/1/labels',
|
||||
id: 1002604,
|
||||
number: 1,
|
||||
state: 'open',
|
||||
title: 'v1.0',
|
||||
description: 'Tracking milestone for version 1.0',
|
||||
creator: {
|
||||
login: 'testowner',
|
||||
id: 4,
|
||||
node_id: 'MDQ6VXNlcjQ=',
|
||||
avatar_url: 'https://github.com/images/error/testowner_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testowner',
|
||||
html_url: 'https://github.com/testowner',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
open_issues: 4,
|
||||
closed_issues: 8,
|
||||
created_at: '2011-04-10T20:09:31Z',
|
||||
updated_at: '2014-03-03T18:58:10Z',
|
||||
closed_at: null,
|
||||
due_on: '2018-09-22T23:39:01Z',
|
||||
node_id: 'MDk6TWlsZXN0b25lMTAwMjYwNA==',
|
||||
},
|
||||
comments: 3,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'COLLABORATOR',
|
||||
active_lock_reason: null,
|
||||
body: 'It would be great if we could add this feature.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/2/reactions',
|
||||
total_count: 5,
|
||||
'+1': 3,
|
||||
'-1': 1,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 1,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/testowner/testrepo/issues/2/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getRepositoryIssues.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Limited getIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/repos/testowner/testrepo/issues')
|
||||
.query({ per_page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/1',
|
||||
repository_url: 'https://api.github.com/repos/testowner/testrepo',
|
||||
labels_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/comments',
|
||||
events_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/events',
|
||||
html_url: 'https://github.com/testowner/testrepo/issues/1',
|
||||
id: 1,
|
||||
number: 1,
|
||||
title: 'Found a bug',
|
||||
user: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045946,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=',
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/labels/bug',
|
||||
name: 'bug',
|
||||
description: "Something isn't working",
|
||||
color: 'd73a49',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: null,
|
||||
assignees: [],
|
||||
milestone: null,
|
||||
comments: 0,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'COLLABORATOR',
|
||||
active_lock_reason: null,
|
||||
body: "I'm having a problem with this.",
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/1/reactions',
|
||||
total_count: 0,
|
||||
'+1': 0,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/testowner/testrepo/issues/1/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getRepositoryIssuesLimit.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filtered getIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/repos/testowner/testrepo/issues')
|
||||
.query({ state: 'closed', labels: 'bug', assignee: 'testuser', per_page: 100, page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/3',
|
||||
repository_url: 'https://api.github.com/repos/testowner/testrepo',
|
||||
labels_url: 'https://api.github.com/repos/testowner/testrepo/issues/3/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/testowner/testrepo/issues/3/comments',
|
||||
events_url: 'https://api.github.com/repos/testowner/testrepo/issues/3/events',
|
||||
html_url: 'https://github.com/testowner/testrepo/issues/3',
|
||||
id: 3,
|
||||
number: 3,
|
||||
title: 'Fixed bug',
|
||||
user: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045946,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=',
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/labels/bug',
|
||||
name: 'bug',
|
||||
description: "Something isn't working",
|
||||
color: 'd73a49',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'closed',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: null,
|
||||
comments: 2,
|
||||
created_at: '2011-04-20T13:33:48Z',
|
||||
updated_at: '2011-04-25T13:33:48Z',
|
||||
closed_at: '2011-04-25T13:33:48Z',
|
||||
author_association: 'COLLABORATOR',
|
||||
active_lock_reason: null,
|
||||
body: 'This bug has been fixed.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/testowner/testrepo/issues/3/reactions',
|
||||
total_count: 1,
|
||||
'+1': 1,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/testowner/testrepo/issues/3/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: 'completed',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getRepositoryIssuesFiltered.workflow.json'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Github Node - User getRepositories', () => {
|
||||
const credentials = {
|
||||
githubApi: {
|
||||
accessToken: 'test-token',
|
||||
server: 'https://api.github.com',
|
||||
user: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Basic User getRepositories Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/users/testuser/repos')
|
||||
.query(true)
|
||||
.reply(200, [
|
||||
{
|
||||
id: 1296269,
|
||||
name: 'hello-world',
|
||||
full_name: 'testuser/hello-world',
|
||||
owner: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
type: 'User',
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testuser/hello-world',
|
||||
description: 'My first repository on GitHub!',
|
||||
fork: false,
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
clone_url: 'https://github.com/testuser/hello-world.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
forks_count: 9,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
},
|
||||
visibility: 'public',
|
||||
default_branch: 'master',
|
||||
},
|
||||
{
|
||||
id: 1296270,
|
||||
name: 'my-app',
|
||||
full_name: 'testuser/my-app',
|
||||
owner: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
type: 'User',
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testuser/my-app',
|
||||
description: 'My awesome application',
|
||||
fork: false,
|
||||
created_at: '2011-02-26T19:01:12Z',
|
||||
updated_at: '2011-02-26T19:14:43Z',
|
||||
pushed_at: '2011-02-26T19:06:43Z',
|
||||
clone_url: 'https://github.com/testuser/my-app.git',
|
||||
size: 512,
|
||||
stargazers_count: 156,
|
||||
watchers_count: 45,
|
||||
language: 'JavaScript',
|
||||
forks_count: 23,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 5,
|
||||
license: {
|
||||
key: 'apache-2.0',
|
||||
name: 'Apache License 2.0',
|
||||
},
|
||||
visibility: 'public',
|
||||
default_branch: 'main',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getUserRepositories.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Limited User getRepositories Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/users/testuser/repos')
|
||||
.query({ per_page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
id: 1296269,
|
||||
name: 'hello-world',
|
||||
full_name: 'testuser/hello-world',
|
||||
owner: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
type: 'User',
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testuser/hello-world',
|
||||
description: 'My first repository on GitHub!',
|
||||
fork: false,
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
clone_url: 'https://github.com/testuser/hello-world.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
forks_count: 9,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
},
|
||||
visibility: 'public',
|
||||
default_branch: 'master',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getUserRepositoriesLimit.workflow.json'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,609 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Github Node - User getUserIssues', () => {
|
||||
const credentials = {
|
||||
githubApi: {
|
||||
accessToken: 'test-token',
|
||||
server: 'https://api.github.com',
|
||||
user: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Basic getUserIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/issues')
|
||||
.query(true)
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/issues/1',
|
||||
repository_url: 'https://api.github.com/repos/someowner/somerepo',
|
||||
labels_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/comments',
|
||||
events_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/events',
|
||||
html_url: 'https://github.com/someowner/somerepo/issues/1',
|
||||
id: 1,
|
||||
number: 1,
|
||||
title: 'Issue assigned to me',
|
||||
user: {
|
||||
login: 'issueauthor',
|
||||
id: 5,
|
||||
node_id: 'MDQ6VXNlcjU=',
|
||||
avatar_url: 'https://github.com/images/error/issueauthor_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/issueauthor',
|
||||
html_url: 'https://github.com/issueauthor',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045946,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=',
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/labels/bug',
|
||||
name: 'bug',
|
||||
description: "Something isn't working",
|
||||
color: 'd73a49',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: null,
|
||||
comments: 0,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'NONE',
|
||||
active_lock_reason: null,
|
||||
body: 'This is an issue assigned to me.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/issues/1/reactions',
|
||||
total_count: 0,
|
||||
'+1': 0,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'somerepo',
|
||||
full_name: 'someowner/somerepo',
|
||||
owner: {
|
||||
login: 'someowner',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/someowner_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/someowner',
|
||||
html_url: 'https://github.com/someowner',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/someowner/somerepo',
|
||||
description: 'Repository with issues assigned to testuser',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/someowner/somerepo',
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
git_url: 'git://github.com/someowner/somerepo.git',
|
||||
ssh_url: 'git@github.com:someowner/somerepo.git',
|
||||
clone_url: 'https://github.com/someowner/somerepo.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
has_issues: true,
|
||||
has_projects: true,
|
||||
has_wiki: true,
|
||||
has_pages: false,
|
||||
forks_count: 9,
|
||||
mirror_url: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
spdx_id: 'MIT',
|
||||
url: 'https://api.github.com/licenses/mit',
|
||||
node_id: 'MDc6TGljZW5zZW1pdA==',
|
||||
},
|
||||
forks: 9,
|
||||
open_issues: 0,
|
||||
watchers: 9,
|
||||
default_branch: 'master',
|
||||
},
|
||||
},
|
||||
{
|
||||
url: 'https://api.github.com/repos/anotherowner/anotherrepo/issues/5',
|
||||
repository_url: 'https://api.github.com/repos/anotherowner/anotherrepo',
|
||||
labels_url:
|
||||
'https://api.github.com/repos/anotherowner/anotherrepo/issues/5/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/anotherowner/anotherrepo/issues/5/comments',
|
||||
events_url: 'https://api.github.com/repos/anotherowner/anotherrepo/issues/5/events',
|
||||
html_url: 'https://github.com/anotherowner/anotherrepo/issues/5',
|
||||
id: 5,
|
||||
number: 5,
|
||||
title: 'Enhancement request assigned to me',
|
||||
user: {
|
||||
login: 'requestor',
|
||||
id: 7,
|
||||
node_id: 'MDQ6VXNlcjc=',
|
||||
avatar_url: 'https://github.com/images/error/requestor_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/requestor',
|
||||
html_url: 'https://github.com/requestor',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045947,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDc=',
|
||||
url: 'https://api.github.com/repos/anotherowner/anotherrepo/labels/enhancement',
|
||||
name: 'enhancement',
|
||||
description: 'New feature or request',
|
||||
color: 'a2eeef',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 208045948,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDg=',
|
||||
url: 'https://api.github.com/repos/anotherowner/anotherrepo/labels/good-first-issue',
|
||||
name: 'good first issue',
|
||||
description: 'Good for newcomers',
|
||||
color: '7057ff',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: null,
|
||||
comments: 1,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'CONTRIBUTOR',
|
||||
active_lock_reason: null,
|
||||
body: 'Please add this enhancement.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/anotherowner/anotherrepo/issues/5/reactions',
|
||||
total_count: 2,
|
||||
'+1': 2,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/anotherowner/anotherrepo/issues/5/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
repository: {
|
||||
id: 1296270,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2Mjcw',
|
||||
name: 'anotherrepo',
|
||||
full_name: 'anotherowner/anotherrepo',
|
||||
owner: {
|
||||
login: 'anotherowner',
|
||||
id: 8,
|
||||
node_id: 'MDQ6VXNlcjg=',
|
||||
avatar_url: 'https://github.com/images/error/anotherowner_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/anotherowner',
|
||||
html_url: 'https://github.com/anotherowner',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/anotherowner/anotherrepo',
|
||||
description: 'Another repository with issues for testuser',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/anotherowner/anotherrepo',
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
git_url: 'git://github.com/anotherowner/anotherrepo.git',
|
||||
ssh_url: 'git@github.com:anotherowner/anotherrepo.git',
|
||||
clone_url: 'https://github.com/anotherowner/anotherrepo.git',
|
||||
size: 256,
|
||||
stargazers_count: 42,
|
||||
watchers_count: 15,
|
||||
language: 'JavaScript',
|
||||
has_issues: true,
|
||||
has_projects: true,
|
||||
has_wiki: true,
|
||||
has_pages: false,
|
||||
forks_count: 3,
|
||||
mirror_url: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 5,
|
||||
license: {
|
||||
key: 'apache-2.0',
|
||||
name: 'Apache License 2.0',
|
||||
spdx_id: 'Apache-2.0',
|
||||
url: 'https://api.github.com/licenses/apache-2.0',
|
||||
node_id: 'MDc6TGljZW5zZWFwYWNoZS0yLjA=',
|
||||
},
|
||||
forks: 3,
|
||||
open_issues: 5,
|
||||
watchers: 15,
|
||||
default_branch: 'main',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getUserIssues.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Limited getUserIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/issues')
|
||||
.query({ per_page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/issues/1',
|
||||
repository_url: 'https://api.github.com/repos/someowner/somerepo',
|
||||
labels_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/comments',
|
||||
events_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/events',
|
||||
html_url: 'https://github.com/someowner/somerepo/issues/1',
|
||||
id: 1,
|
||||
number: 1,
|
||||
title: 'Issue assigned to me',
|
||||
user: {
|
||||
login: 'issueauthor',
|
||||
id: 5,
|
||||
node_id: 'MDQ6VXNlcjU=',
|
||||
avatar_url: 'https://github.com/images/error/issueauthor_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/issueauthor',
|
||||
html_url: 'https://github.com/issueauthor',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045946,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=',
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/labels/bug',
|
||||
name: 'bug',
|
||||
description: "Something isn't working",
|
||||
color: 'd73a49',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'open',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: null,
|
||||
comments: 0,
|
||||
created_at: '2011-04-22T13:33:48Z',
|
||||
updated_at: '2011-04-22T13:33:48Z',
|
||||
closed_at: null,
|
||||
author_association: 'NONE',
|
||||
active_lock_reason: null,
|
||||
body: 'This is an issue assigned to me.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/someowner/somerepo/issues/1/reactions',
|
||||
total_count: 0,
|
||||
'+1': 0,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 0,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/someowner/somerepo/issues/1/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: null,
|
||||
repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'somerepo',
|
||||
full_name: 'someowner/somerepo',
|
||||
owner: {
|
||||
login: 'someowner',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/someowner_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/someowner',
|
||||
html_url: 'https://github.com/someowner',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/someowner/somerepo',
|
||||
description: 'Repository with issues assigned to testuser',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/someowner/somerepo',
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
git_url: 'git://github.com/someowner/somerepo.git',
|
||||
ssh_url: 'git@github.com:someowner/somerepo.git',
|
||||
clone_url: 'https://github.com/someowner/somerepo.git',
|
||||
size: 108,
|
||||
stargazers_count: 80,
|
||||
watchers_count: 9,
|
||||
language: 'C',
|
||||
has_issues: true,
|
||||
has_projects: true,
|
||||
has_wiki: true,
|
||||
has_pages: false,
|
||||
forks_count: 9,
|
||||
mirror_url: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
spdx_id: 'MIT',
|
||||
url: 'https://api.github.com/licenses/mit',
|
||||
node_id: 'MDc6TGljZW5zZW1pdA==',
|
||||
},
|
||||
forks: 9,
|
||||
open_issues: 0,
|
||||
watchers: 9,
|
||||
default_branch: 'master',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getUserIssuesLimit.workflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filtered getUserIssues Operation', () => {
|
||||
beforeAll(() => {
|
||||
const mock = nock('https://api.github.com');
|
||||
|
||||
mock
|
||||
.get('/issues')
|
||||
.query({ state: 'closed', labels: 'enhancement', per_page: 100, page: 1 })
|
||||
.reply(200, [
|
||||
{
|
||||
url: 'https://api.github.com/repos/testowner/closedrepo/issues/10',
|
||||
repository_url: 'https://api.github.com/repos/testowner/closedrepo',
|
||||
labels_url: 'https://api.github.com/repos/testowner/closedrepo/issues/10/labels{/name}',
|
||||
comments_url: 'https://api.github.com/repos/testowner/closedrepo/issues/10/comments',
|
||||
events_url: 'https://api.github.com/repos/testowner/closedrepo/issues/10/events',
|
||||
html_url: 'https://github.com/testowner/closedrepo/issues/10',
|
||||
id: 10,
|
||||
number: 10,
|
||||
title: 'Completed enhancement',
|
||||
user: {
|
||||
login: 'enhancementauthor',
|
||||
id: 9,
|
||||
node_id: 'MDQ6VXNlcjk=',
|
||||
avatar_url: 'https://github.com/images/error/enhancementauthor_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/enhancementauthor',
|
||||
html_url: 'https://github.com/enhancementauthor',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: 208045947,
|
||||
node_id: 'MDU6TGFiZWwyMDgwNDU5NDc=',
|
||||
url: 'https://api.github.com/repos/testowner/closedrepo/labels/enhancement',
|
||||
name: 'enhancement',
|
||||
description: 'New feature or request',
|
||||
color: 'a2eeef',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
state: 'closed',
|
||||
locked: false,
|
||||
assignee: {
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'testuser',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/testuser_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testuser',
|
||||
html_url: 'https://github.com/testuser',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
],
|
||||
milestone: null,
|
||||
comments: 5,
|
||||
created_at: '2011-04-10T13:33:48Z',
|
||||
updated_at: '2011-04-30T13:33:48Z',
|
||||
closed_at: '2011-04-30T13:33:48Z',
|
||||
author_association: 'CONTRIBUTOR',
|
||||
active_lock_reason: null,
|
||||
body: 'Enhancement has been completed successfully.',
|
||||
reactions: {
|
||||
url: 'https://api.github.com/repos/testowner/closedrepo/issues/10/reactions',
|
||||
total_count: 3,
|
||||
'+1': 2,
|
||||
'-1': 0,
|
||||
laugh: 0,
|
||||
hooray: 1,
|
||||
confused: 0,
|
||||
heart: 0,
|
||||
rocket: 0,
|
||||
eyes: 0,
|
||||
},
|
||||
timeline_url: 'https://api.github.com/repos/testowner/closedrepo/issues/10/timeline',
|
||||
performed_via_github_app: null,
|
||||
state_reason: 'completed',
|
||||
repository: {
|
||||
id: 1296271,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2Mjcx',
|
||||
name: 'closedrepo',
|
||||
full_name: 'testowner/closedrepo',
|
||||
owner: {
|
||||
login: 'testowner',
|
||||
id: 10,
|
||||
node_id: 'MDQ6VXNlcjEw',
|
||||
avatar_url: 'https://github.com/images/error/testowner_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/testowner',
|
||||
html_url: 'https://github.com/testowner',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/testowner/closedrepo',
|
||||
description: 'Repository with closed enhancement issues',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/testowner/closedrepo',
|
||||
created_at: '2011-01-26T19:01:12Z',
|
||||
updated_at: '2011-01-26T19:14:43Z',
|
||||
pushed_at: '2011-01-26T19:06:43Z',
|
||||
git_url: 'git://github.com/testowner/closedrepo.git',
|
||||
ssh_url: 'git@github.com:testowner/closedrepo.git',
|
||||
clone_url: 'https://github.com/testowner/closedrepo.git',
|
||||
size: 128,
|
||||
stargazers_count: 25,
|
||||
watchers_count: 5,
|
||||
language: 'Python',
|
||||
has_issues: true,
|
||||
has_projects: true,
|
||||
has_wiki: true,
|
||||
has_pages: false,
|
||||
forks_count: 2,
|
||||
mirror_url: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 0,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
spdx_id: 'MIT',
|
||||
url: 'https://api.github.com/licenses/mit',
|
||||
node_id: 'MDc6TGljZW5zZW1pdA==',
|
||||
},
|
||||
forks: 2,
|
||||
open_issues: 0,
|
||||
watchers: 5,
|
||||
default_branch: 'main',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
credentials,
|
||||
workflowFiles: ['getUserIssuesFiltered.workflow.json'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,159 @@
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
import { verifySignature } from '../GithubTriggerHelpers';
|
||||
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
createHmac: jest.fn().mockReturnValue({
|
||||
update: jest.fn().mockReturnThis(),
|
||||
digest: jest
|
||||
.fn()
|
||||
.mockReturnValue('757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17'),
|
||||
}),
|
||||
timingSafeEqual: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('GithubTriggerHelpers', () => {
|
||||
let mockWebhookFunctions: {
|
||||
getWorkflowStaticData: jest.Mock;
|
||||
getRequestObject: jest.Mock;
|
||||
};
|
||||
const testWebhookSecret = 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2';
|
||||
const testBody =
|
||||
'{"action":"opened","pull_request":{"id":123},"repository":{"full_name":"owner/repo"}}';
|
||||
const testSignature = 'sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getRequestObject: jest.fn(),
|
||||
};
|
||||
|
||||
// Default mock return values
|
||||
mockWebhookFunctions.getWorkflowStaticData.mockReturnValue({
|
||||
webhookSecret: testWebhookSecret,
|
||||
});
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hub-signature-256') return testSignature;
|
||||
return null;
|
||||
}),
|
||||
rawBody: testBody,
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifySignature', () => {
|
||||
it('should return true when no webhook secret is stored (backwards compatibility)', () => {
|
||||
mockWebhookFunctions.getWorkflowStaticData.mockReturnValue({});
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockWebhookFunctions.getWorkflowStaticData).toHaveBeenCalledWith('node');
|
||||
});
|
||||
|
||||
it('should return false when signature header is missing', () => {
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue(null),
|
||||
rawBody: testBody,
|
||||
});
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when signature does not start with sha256=', () => {
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hub-signature-256') return 'invalid-format-signature';
|
||||
return null;
|
||||
}),
|
||||
rawBody: testBody,
|
||||
});
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when rawBody is missing', () => {
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hub-signature-256') return testSignature;
|
||||
return null;
|
||||
}),
|
||||
rawBody: undefined,
|
||||
});
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when signature is valid', () => {
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(createHmac).toHaveBeenCalledWith('sha256', testWebhookSecret);
|
||||
expect(timingSafeEqual).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return false when signature is invalid', () => {
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(createHmac).toHaveBeenCalledWith('sha256', testWebhookSecret);
|
||||
expect(timingSafeEqual).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle Buffer rawBody correctly', () => {
|
||||
const bufferBody = Buffer.from(testBody);
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hub-signature-256') return testSignature;
|
||||
return null;
|
||||
}),
|
||||
rawBody: bufferBody,
|
||||
});
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(true);
|
||||
const mockHmac = createHmac('sha256', testWebhookSecret);
|
||||
expect(mockHmac.update).toHaveBeenCalledWith(bufferBody);
|
||||
});
|
||||
|
||||
it('should return false when computed and provided signatures have different lengths', () => {
|
||||
// Mock a different length signature
|
||||
const mockHmacInstance = {
|
||||
update: jest.fn().mockReturnThis(),
|
||||
digest: jest.fn().mockReturnValue('short'),
|
||||
};
|
||||
(createHmac as jest.Mock).mockReturnValue(mockHmacInstance);
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
// timingSafeEqual should not be called if lengths don't match
|
||||
expect(timingSafeEqual).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return false when an error occurs during verification', () => {
|
||||
(createHmac as jest.Mock).mockImplementation(() => {
|
||||
throw new Error('Crypto error');
|
||||
});
|
||||
|
||||
const result = verifySignature.call(mockWebhookFunctions as never);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,499 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,257 @@
|
||||
import { Github } from '../Github.node';
|
||||
import { GithubTrigger } from '../GithubTrigger.node';
|
||||
|
||||
interface ValidationRule {
|
||||
type: string;
|
||||
properties: {
|
||||
regex: string;
|
||||
errorMessage: string;
|
||||
};
|
||||
}
|
||||
|
||||
describe('GitHub Node URL Pattern Tests', () => {
|
||||
let githubNode: Github;
|
||||
let githubTriggerNode: GithubTrigger;
|
||||
|
||||
const getOwnerUrlMode = () => {
|
||||
const ownerParam = githubNode.description.properties.find((prop) => prop.name === 'owner');
|
||||
return ownerParam?.modes?.find((mode) => mode.name === 'url');
|
||||
};
|
||||
|
||||
const getRepositoryUrlMode = () => {
|
||||
const repoParam = githubNode.description.properties.find((prop) => prop.name === 'repository');
|
||||
return repoParam?.modes?.find((mode) => mode.name === 'url');
|
||||
};
|
||||
|
||||
const getOwnerExtractRegex = () => {
|
||||
const mode = getOwnerUrlMode();
|
||||
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||
};
|
||||
|
||||
const getOwnerValidationRegex = () => {
|
||||
const mode = getOwnerUrlMode();
|
||||
const validation = mode?.validation?.[0] as ValidationRule;
|
||||
return new RegExp(validation?.properties?.regex ?? '');
|
||||
};
|
||||
|
||||
const getRepositoryExtractRegex = () => {
|
||||
const mode = getRepositoryUrlMode();
|
||||
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||
};
|
||||
|
||||
const getRepositoryValidationRegex = () => {
|
||||
const mode = getRepositoryUrlMode();
|
||||
const validation = mode?.validation?.[0] as ValidationRule;
|
||||
return new RegExp(validation?.properties?.regex ?? '');
|
||||
};
|
||||
|
||||
// Helper functions for GithubTrigger node
|
||||
const getTriggerOwnerUrlMode = () => {
|
||||
const ownerParam = githubTriggerNode.description.properties.find(
|
||||
(prop) => prop.name === 'owner',
|
||||
);
|
||||
return ownerParam?.modes?.find((mode) => mode.name === 'url');
|
||||
};
|
||||
|
||||
const getTriggerRepositoryUrlMode = () => {
|
||||
const repoParam = githubTriggerNode.description.properties.find(
|
||||
(prop) => prop.name === 'repository',
|
||||
);
|
||||
return repoParam?.modes?.find((mode) => mode.name === 'url');
|
||||
};
|
||||
|
||||
const getTriggerOwnerExtractRegex = () => {
|
||||
const mode = getTriggerOwnerUrlMode();
|
||||
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||
};
|
||||
|
||||
const getTriggerOwnerValidationRegex = () => {
|
||||
const mode = getTriggerOwnerUrlMode();
|
||||
const validation = mode?.validation?.[0] as ValidationRule;
|
||||
return new RegExp(validation?.properties?.regex ?? '');
|
||||
};
|
||||
|
||||
const getTriggerRepositoryExtractRegex = () => {
|
||||
const mode = getTriggerRepositoryUrlMode();
|
||||
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||
};
|
||||
|
||||
const getTriggerRepositoryValidationRegex = () => {
|
||||
const mode = getTriggerRepositoryUrlMode();
|
||||
const validation = mode?.validation?.[0] as ValidationRule;
|
||||
return new RegExp(validation?.properties?.regex ?? '');
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
githubTriggerNode = new GithubTrigger();
|
||||
});
|
||||
|
||||
describe('GitHub Node Resource Locator Patterns', () => {
|
||||
describe('Owner URL Pattern', () => {
|
||||
it('should extract owner from github.com URL', () => {
|
||||
const regex = getOwnerExtractRegex();
|
||||
const url = 'https://github.com/n8n-io';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('n8n-io');
|
||||
});
|
||||
|
||||
it('should extract owner from custom GitHub URL', () => {
|
||||
const regex = getOwnerExtractRegex();
|
||||
const url = 'https://github.company.com/acme-corp';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('acme-corp');
|
||||
});
|
||||
|
||||
it('should validate github.com URL', () => {
|
||||
const validationRegex = getOwnerValidationRegex();
|
||||
const url = 'https://github.com/n8n-io';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate custom GitHub URL', () => {
|
||||
const validationRegex = getOwnerValidationRegex();
|
||||
const url = 'https://github.company.com/acme-corp';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should reject invalid URLs', () => {
|
||||
const validationRegex = getOwnerValidationRegex();
|
||||
expect(validationRegex.test('not-a-url')).toBe(false);
|
||||
expect(validationRegex.test('http://github.com/user')).toBe(false);
|
||||
expect(validationRegex.test('https://')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Repository URL Pattern', () => {
|
||||
it('should extract repository from github.com URL', () => {
|
||||
const regex = getRepositoryExtractRegex();
|
||||
const url = 'https://github.com/n8n-io/n8n';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('n8n');
|
||||
});
|
||||
|
||||
it('should extract repository from custom GitHub URL', () => {
|
||||
const regex = getRepositoryExtractRegex();
|
||||
const url = 'https://github.company.com/acme-corp/my-repo';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('my-repo');
|
||||
});
|
||||
|
||||
it('should validate github.com repository URL', () => {
|
||||
const validationRegex = getRepositoryValidationRegex();
|
||||
const url = 'https://github.com/n8n-io/n8n';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate custom GitHub repository URL', () => {
|
||||
const validationRegex = getRepositoryValidationRegex();
|
||||
const url = 'https://github.company.com/acme-corp/my-repo';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate URLs with additional paths', () => {
|
||||
const validationRegex = getRepositoryValidationRegex();
|
||||
expect(validationRegex.test('https://github.com/n8n-io/n8n/issues/123')).toBe(true);
|
||||
expect(validationRegex.test('https://github.company.com/org/repo/pulls')).toBe(true);
|
||||
});
|
||||
|
||||
it('should reject invalid repository URLs', () => {
|
||||
const validationRegex = getRepositoryValidationRegex();
|
||||
expect(validationRegex.test('https://github.com/user')).toBe(false);
|
||||
expect(validationRegex.test('not-a-url')).toBe(false);
|
||||
expect(validationRegex.test('https://')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GitHub Trigger Node Resource Locator Patterns', () => {
|
||||
describe('Owner URL Pattern', () => {
|
||||
it('should extract owner from github.com URL', () => {
|
||||
const regex = getTriggerOwnerExtractRegex();
|
||||
const url = 'https://github.com/n8n-io';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('n8n-io');
|
||||
});
|
||||
|
||||
it('should extract owner from custom GitHub URL', () => {
|
||||
const regex = getTriggerOwnerExtractRegex();
|
||||
const url = 'https://github.company.com/my-org';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('my-org');
|
||||
});
|
||||
|
||||
it('should validate github.com URL', () => {
|
||||
const validationRegex = getTriggerOwnerValidationRegex();
|
||||
const url = 'https://github.com/n8n-io';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate custom GitHub URL', () => {
|
||||
const validationRegex = getTriggerOwnerValidationRegex();
|
||||
const url = 'https://github.company.com/my-org';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Repository URL Pattern', () => {
|
||||
it('should extract repository from github.com URL', () => {
|
||||
const regex = getTriggerRepositoryExtractRegex();
|
||||
const url = 'https://github.com/n8n-io/n8n';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('n8n');
|
||||
});
|
||||
|
||||
it('should extract repository from custom GitHub URL', () => {
|
||||
const regex = getTriggerRepositoryExtractRegex();
|
||||
const url = 'https://github.company.com/my-org/my-repo';
|
||||
const match = url.match(regex);
|
||||
expect(match?.[1]).toBe('my-repo');
|
||||
});
|
||||
|
||||
it('should validate github.com repository URL', () => {
|
||||
const validationRegex = getTriggerRepositoryValidationRegex();
|
||||
const url = 'https://github.com/n8n-io/n8n';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate custom GitHub repository URL', () => {
|
||||
const validationRegex = getTriggerRepositoryValidationRegex();
|
||||
const url = 'https://github.company.com/my-org/my-repo';
|
||||
expect(validationRegex.test(url)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL Pattern Edge Cases', () => {
|
||||
it('should handle URLs with subdomains', () => {
|
||||
const ownerRegex = getOwnerExtractRegex();
|
||||
const repoRegex = getRepositoryExtractRegex();
|
||||
|
||||
// Test complex custom URLs
|
||||
expect('https://git.internal.company.com/dev-team'.match(ownerRegex)?.[1]).toBe('dev-team');
|
||||
expect('https://github.acme.corp/engineering/backend-api'.match(repoRegex)?.[1]).toBe(
|
||||
'backend-api',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle URLs with ports', () => {
|
||||
const ownerRegex = getOwnerExtractRegex();
|
||||
const repoRegex = getRepositoryExtractRegex();
|
||||
|
||||
// Test URLs with ports
|
||||
expect('https://github.local:8080/testuser'.match(ownerRegex)?.[1]).toBe('testuser');
|
||||
expect('https://git.company.com:443/org/project'.match(repoRegex)?.[1]).toBe('project');
|
||||
});
|
||||
|
||||
it('should handle URLs with additional path segments', () => {
|
||||
const ownerValidationRegex = getOwnerValidationRegex();
|
||||
const repoValidationRegex = getRepositoryValidationRegex();
|
||||
|
||||
// Test URLs with extra paths
|
||||
expect(ownerValidationRegex.test('https://github.com/user/settings')).toBe(true);
|
||||
expect(repoValidationRegex.test('https://github.com/user/repo/issues/123')).toBe(true);
|
||||
expect(repoValidationRegex.test('https://git.company.com/org/project/pulls')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"name": "Github Organization getRepositories Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "organization",
|
||||
"operation": "getRepositories",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testorg",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": true
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get Organization Repositories",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Repositories Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Repositories Response": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1296269,
|
||||
"name": "hello-world",
|
||||
"full_name": "testorg/hello-world",
|
||||
"owner": {
|
||||
"login": "testorg",
|
||||
"id": 1,
|
||||
"type": "Organization"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testorg/hello-world",
|
||||
"description": "My first repository on GitHub!",
|
||||
"fork": false,
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"clone_url": "https://github.com/testorg/hello-world.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"forks_count": 9,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License"
|
||||
},
|
||||
"visibility": "public",
|
||||
"default_branch": "master"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 1296270,
|
||||
"name": "test-repo",
|
||||
"full_name": "testorg/test-repo",
|
||||
"owner": {
|
||||
"login": "testorg",
|
||||
"id": 1,
|
||||
"type": "Organization"
|
||||
},
|
||||
"private": true,
|
||||
"html_url": "https://github.com/testorg/test-repo",
|
||||
"description": "Test repository",
|
||||
"fork": false,
|
||||
"created_at": "2011-01-27T19:01:12Z",
|
||||
"updated_at": "2011-01-27T19:14:43Z",
|
||||
"pushed_at": "2011-01-27T19:06:43Z",
|
||||
"clone_url": "https://github.com/testorg/test-repo.git",
|
||||
"size": 256,
|
||||
"stargazers_count": 42,
|
||||
"watchers_count": 15,
|
||||
"language": "JavaScript",
|
||||
"forks_count": 3,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 2,
|
||||
"license": {
|
||||
"key": "apache-2.0",
|
||||
"name": "Apache License 2.0"
|
||||
},
|
||||
"visibility": "private",
|
||||
"default_branch": "main"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Organization Repositories",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Organization Repositories": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Repositories Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"name": "Github Organization getRepositories Limit Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "organization",
|
||||
"operation": "getRepositories",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testorg",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": false,
|
||||
"limit": 1
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get Organization Repositories Limited",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Repositories Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Repositories Response": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1296269,
|
||||
"name": "hello-world",
|
||||
"full_name": "testorg/hello-world",
|
||||
"owner": {
|
||||
"login": "testorg",
|
||||
"id": 1,
|
||||
"type": "Organization"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testorg/hello-world",
|
||||
"description": "My first repository on GitHub!",
|
||||
"fork": false,
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"clone_url": "https://github.com/testorg/hello-world.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"forks_count": 9,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License"
|
||||
},
|
||||
"visibility": "public",
|
||||
"default_branch": "master"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Organization Repositories Limited",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Organization Repositories Limited": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Repositories Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"name": "Github Repository getIssues Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "repository",
|
||||
"operation": "getIssues",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testowner",
|
||||
"mode": "name"
|
||||
},
|
||||
"repository": {
|
||||
"__rl": true,
|
||||
"value": "testrepo",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": true,
|
||||
"getRepositoryIssuesFilters": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get Repository Issues",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/1",
|
||||
"repository_url": "https://api.github.com/repos/testowner/testrepo",
|
||||
"labels_url": "https://api.github.com/repos/testowner/testrepo/issues/1/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/testowner/testrepo/issues/1/comments",
|
||||
"events_url": "https://api.github.com/repos/testowner/testrepo/issues/1/events",
|
||||
"html_url": "https://github.com/testowner/testrepo/issues/1",
|
||||
"id": 1,
|
||||
"number": 1,
|
||||
"title": "Found a bug",
|
||||
"user": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045946,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/labels/bug",
|
||||
"name": "bug",
|
||||
"description": "Something isn't working",
|
||||
"color": "d73a49",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": null,
|
||||
"assignees": [],
|
||||
"milestone": null,
|
||||
"comments": 0,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "COLLABORATOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "I'm having a problem with this.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/1/reactions",
|
||||
"total_count": 0,
|
||||
"+1": 0,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/testowner/testrepo/issues/1/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/2",
|
||||
"repository_url": "https://api.github.com/repos/testowner/testrepo",
|
||||
"labels_url": "https://api.github.com/repos/testowner/testrepo/issues/2/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/testowner/testrepo/issues/2/comments",
|
||||
"events_url": "https://api.github.com/repos/testowner/testrepo/issues/2/events",
|
||||
"html_url": "https://github.com/testowner/testrepo/issues/2",
|
||||
"id": 2,
|
||||
"number": 2,
|
||||
"title": "Feature request",
|
||||
"user": {
|
||||
"login": "anotheruser",
|
||||
"id": 2,
|
||||
"node_id": "MDQ6VXNlcjI=",
|
||||
"avatar_url": "https://github.com/images/error/anotheruser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/anotheruser",
|
||||
"html_url": "https://github.com/anotheruser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045947,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDc=",
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/labels/enhancement",
|
||||
"name": "enhancement",
|
||||
"description": "New feature or request",
|
||||
"color": "a2eeef",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "assigneduser",
|
||||
"id": 3,
|
||||
"node_id": "MDQ6VXNlcjM=",
|
||||
"avatar_url": "https://github.com/images/error/assigneduser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/assigneduser",
|
||||
"html_url": "https://github.com/assigneduser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "assigneduser",
|
||||
"id": 3,
|
||||
"node_id": "MDQ6VXNlcjM=",
|
||||
"avatar_url": "https://github.com/images/error/assigneduser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/assigneduser",
|
||||
"html_url": "https://github.com/assigneduser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/milestones/1",
|
||||
"html_url": "https://github.com/testowner/testrepo/milestone/1",
|
||||
"labels_url": "https://api.github.com/repos/testowner/testrepo/milestones/1/labels",
|
||||
"id": 1002604,
|
||||
"number": 1,
|
||||
"state": "open",
|
||||
"title": "v1.0",
|
||||
"description": "Tracking milestone for version 1.0",
|
||||
"creator": {
|
||||
"login": "testowner",
|
||||
"id": 4,
|
||||
"node_id": "MDQ6VXNlcjQ=",
|
||||
"avatar_url": "https://github.com/images/error/testowner_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testowner",
|
||||
"html_url": "https://github.com/testowner",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"open_issues": 4,
|
||||
"closed_issues": 8,
|
||||
"created_at": "2011-04-10T20:09:31Z",
|
||||
"updated_at": "2014-03-03T18:58:10Z",
|
||||
"closed_at": null,
|
||||
"due_on": "2018-09-22T23:39:01Z",
|
||||
"node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA=="
|
||||
},
|
||||
"comments": 3,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "COLLABORATOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "It would be great if we could add this feature.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/2/reactions",
|
||||
"total_count": 5,
|
||||
"+1": 3,
|
||||
"-1": 1,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 1,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/testowner/testrepo/issues/2/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Repository Issues",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Repository Issues": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"name": "Github Repository getIssues Filtered Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "repository",
|
||||
"operation": "getIssues",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testowner",
|
||||
"mode": "name"
|
||||
},
|
||||
"repository": {
|
||||
"__rl": true,
|
||||
"value": "testrepo",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": true,
|
||||
"getRepositoryIssuesFilters": {
|
||||
"state": "closed",
|
||||
"labels": "bug",
|
||||
"assignee": "testuser"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get Repository Issues Filtered",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/3",
|
||||
"repository_url": "https://api.github.com/repos/testowner/testrepo",
|
||||
"labels_url": "https://api.github.com/repos/testowner/testrepo/issues/3/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/testowner/testrepo/issues/3/comments",
|
||||
"events_url": "https://api.github.com/repos/testowner/testrepo/issues/3/events",
|
||||
"html_url": "https://github.com/testowner/testrepo/issues/3",
|
||||
"id": 3,
|
||||
"number": 3,
|
||||
"title": "Fixed bug",
|
||||
"user": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045946,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/labels/bug",
|
||||
"name": "bug",
|
||||
"description": "Something isn't working",
|
||||
"color": "d73a49",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "closed",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": null,
|
||||
"comments": 2,
|
||||
"created_at": "2011-04-20T13:33:48Z",
|
||||
"updated_at": "2011-04-25T13:33:48Z",
|
||||
"closed_at": "2011-04-25T13:33:48Z",
|
||||
"author_association": "COLLABORATOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "This bug has been fixed.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/3/reactions",
|
||||
"total_count": 1,
|
||||
"+1": 1,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/testowner/testrepo/issues/3/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": "completed"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Repository Issues Filtered",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Repository Issues Filtered": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"name": "Github Repository getIssues Limit Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "repository",
|
||||
"operation": "getIssues",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testowner",
|
||||
"mode": "name"
|
||||
},
|
||||
"repository": {
|
||||
"__rl": true,
|
||||
"value": "testrepo",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": false,
|
||||
"limit": 1,
|
||||
"getRepositoryIssuesFilters": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get Repository Issues Limited",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/1",
|
||||
"repository_url": "https://api.github.com/repos/testowner/testrepo",
|
||||
"labels_url": "https://api.github.com/repos/testowner/testrepo/issues/1/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/testowner/testrepo/issues/1/comments",
|
||||
"events_url": "https://api.github.com/repos/testowner/testrepo/issues/1/events",
|
||||
"html_url": "https://github.com/testowner/testrepo/issues/1",
|
||||
"id": 1,
|
||||
"number": 1,
|
||||
"title": "Found a bug",
|
||||
"user": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045946,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/labels/bug",
|
||||
"name": "bug",
|
||||
"description": "Something isn't working",
|
||||
"color": "d73a49",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": null,
|
||||
"assignees": [],
|
||||
"milestone": null,
|
||||
"comments": 0,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "COLLABORATOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "I'm having a problem with this.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/testowner/testrepo/issues/1/reactions",
|
||||
"total_count": 0,
|
||||
"+1": 0,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/testowner/testrepo/issues/1/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Repository Issues Limited",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Repository Issues Limited": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
{
|
||||
"name": "Github User getUserIssues Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "getUserIssues",
|
||||
"returnAll": true,
|
||||
"getUserIssuesFilters": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get User Issues",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/issues/1",
|
||||
"repository_url": "https://api.github.com/repos/someowner/somerepo",
|
||||
"labels_url": "https://api.github.com/repos/someowner/somerepo/issues/1/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/someowner/somerepo/issues/1/comments",
|
||||
"events_url": "https://api.github.com/repos/someowner/somerepo/issues/1/events",
|
||||
"html_url": "https://github.com/someowner/somerepo/issues/1",
|
||||
"id": 1,
|
||||
"number": 1,
|
||||
"title": "Issue assigned to me",
|
||||
"user": {
|
||||
"login": "issueauthor",
|
||||
"id": 5,
|
||||
"node_id": "MDQ6VXNlcjU=",
|
||||
"avatar_url": "https://github.com/images/error/issueauthor_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/issueauthor",
|
||||
"html_url": "https://github.com/issueauthor",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045946,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/labels/bug",
|
||||
"name": "bug",
|
||||
"description": "Something isn't working",
|
||||
"color": "d73a49",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": null,
|
||||
"comments": 0,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "NONE",
|
||||
"active_lock_reason": null,
|
||||
"body": "This is an issue assigned to me.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/issues/1/reactions",
|
||||
"total_count": 0,
|
||||
"+1": 0,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/someowner/somerepo/issues/1/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null,
|
||||
"repository": {
|
||||
"id": 1296269,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
|
||||
"name": "somerepo",
|
||||
"full_name": "someowner/somerepo",
|
||||
"owner": {
|
||||
"login": "someowner",
|
||||
"id": 6,
|
||||
"node_id": "MDQ6VXNlcjY=",
|
||||
"avatar_url": "https://github.com/images/error/someowner_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/someowner",
|
||||
"html_url": "https://github.com/someowner",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/someowner/somerepo",
|
||||
"description": "Repository with issues assigned to testuser",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/someowner/somerepo",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"git_url": "git://github.com/someowner/somerepo.git",
|
||||
"ssh_url": "git@github.com:someowner/somerepo.git",
|
||||
"clone_url": "https://github.com/someowner/somerepo.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 9,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License",
|
||||
"spdx_id": "MIT",
|
||||
"url": "https://api.github.com/licenses/mit",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
"forks": 9,
|
||||
"open_issues": 0,
|
||||
"watchers": 9,
|
||||
"default_branch": "master"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5",
|
||||
"repository_url": "https://api.github.com/repos/anotherowner/anotherrepo",
|
||||
"labels_url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5/comments",
|
||||
"events_url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5/events",
|
||||
"html_url": "https://github.com/anotherowner/anotherrepo/issues/5",
|
||||
"id": 5,
|
||||
"number": 5,
|
||||
"title": "Enhancement request assigned to me",
|
||||
"user": {
|
||||
"login": "requestor",
|
||||
"id": 7,
|
||||
"node_id": "MDQ6VXNlcjc=",
|
||||
"avatar_url": "https://github.com/images/error/requestor_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/requestor",
|
||||
"html_url": "https://github.com/requestor",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045947,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDc=",
|
||||
"url": "https://api.github.com/repos/anotherowner/anotherrepo/labels/enhancement",
|
||||
"name": "enhancement",
|
||||
"description": "New feature or request",
|
||||
"color": "a2eeef",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": 208045948,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDg=",
|
||||
"url": "https://api.github.com/repos/anotherowner/anotherrepo/labels/good-first-issue",
|
||||
"name": "good first issue",
|
||||
"description": "Good for newcomers",
|
||||
"color": "7057ff",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": null,
|
||||
"comments": 1,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "CONTRIBUTOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "Please add this enhancement.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5/reactions",
|
||||
"total_count": 2,
|
||||
"+1": 2,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/anotherowner/anotherrepo/issues/5/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null,
|
||||
"repository": {
|
||||
"id": 1296270,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2Mjcw",
|
||||
"name": "anotherrepo",
|
||||
"full_name": "anotherowner/anotherrepo",
|
||||
"owner": {
|
||||
"login": "anotherowner",
|
||||
"id": 8,
|
||||
"node_id": "MDQ6VXNlcjg=",
|
||||
"avatar_url": "https://github.com/images/error/anotherowner_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/anotherowner",
|
||||
"html_url": "https://github.com/anotherowner",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/anotherowner/anotherrepo",
|
||||
"description": "Another repository with issues for testuser",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/anotherowner/anotherrepo",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"git_url": "git://github.com/anotherowner/anotherrepo.git",
|
||||
"ssh_url": "git@github.com:anotherowner/anotherrepo.git",
|
||||
"clone_url": "https://github.com/anotherowner/anotherrepo.git",
|
||||
"size": 256,
|
||||
"stargazers_count": 42,
|
||||
"watchers_count": 15,
|
||||
"language": "JavaScript",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 3,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 5,
|
||||
"license": {
|
||||
"key": "apache-2.0",
|
||||
"name": "Apache License 2.0",
|
||||
"spdx_id": "Apache-2.0",
|
||||
"url": "https://api.github.com/licenses/apache-2.0",
|
||||
"node_id": "MDc6TGljZW5zZWFwYWNoZS0yLjA="
|
||||
},
|
||||
"forks": 3,
|
||||
"open_issues": 5,
|
||||
"watchers": 15,
|
||||
"default_branch": "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get User Issues",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get User Issues": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
{
|
||||
"name": "Github User getUserIssues Filtered Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "getUserIssues",
|
||||
"returnAll": true,
|
||||
"getUserIssuesFilters": {
|
||||
"state": "closed",
|
||||
"labels": "enhancement"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get User Issues Filtered",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/testowner/closedrepo/issues/10",
|
||||
"repository_url": "https://api.github.com/repos/testowner/closedrepo",
|
||||
"labels_url": "https://api.github.com/repos/testowner/closedrepo/issues/10/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/testowner/closedrepo/issues/10/comments",
|
||||
"events_url": "https://api.github.com/repos/testowner/closedrepo/issues/10/events",
|
||||
"html_url": "https://github.com/testowner/closedrepo/issues/10",
|
||||
"id": 10,
|
||||
"number": 10,
|
||||
"title": "Completed enhancement",
|
||||
"user": {
|
||||
"login": "enhancementauthor",
|
||||
"id": 9,
|
||||
"node_id": "MDQ6VXNlcjk=",
|
||||
"avatar_url": "https://github.com/images/error/enhancementauthor_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/enhancementauthor",
|
||||
"html_url": "https://github.com/enhancementauthor",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045947,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDc=",
|
||||
"url": "https://api.github.com/repos/testowner/closedrepo/labels/enhancement",
|
||||
"name": "enhancement",
|
||||
"description": "New feature or request",
|
||||
"color": "a2eeef",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "closed",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": null,
|
||||
"comments": 5,
|
||||
"created_at": "2011-04-10T13:33:48Z",
|
||||
"updated_at": "2011-04-30T13:33:48Z",
|
||||
"closed_at": "2011-04-30T13:33:48Z",
|
||||
"author_association": "CONTRIBUTOR",
|
||||
"active_lock_reason": null,
|
||||
"body": "Enhancement has been completed successfully.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/testowner/closedrepo/issues/10/reactions",
|
||||
"total_count": 3,
|
||||
"+1": 2,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 1,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/testowner/closedrepo/issues/10/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": "completed",
|
||||
"repository": {
|
||||
"id": 1296271,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2Mjcx",
|
||||
"name": "closedrepo",
|
||||
"full_name": "testowner/closedrepo",
|
||||
"owner": {
|
||||
"login": "testowner",
|
||||
"id": 10,
|
||||
"node_id": "MDQ6VXNlcjEw",
|
||||
"avatar_url": "https://github.com/images/error/testowner_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testowner",
|
||||
"html_url": "https://github.com/testowner",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testowner/closedrepo",
|
||||
"description": "Repository with closed enhancement issues",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/testowner/closedrepo",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"git_url": "git://github.com/testowner/closedrepo.git",
|
||||
"ssh_url": "git@github.com:testowner/closedrepo.git",
|
||||
"clone_url": "https://github.com/testowner/closedrepo.git",
|
||||
"size": 128,
|
||||
"stargazers_count": 25,
|
||||
"watchers_count": 5,
|
||||
"language": "Python",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 2,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License",
|
||||
"spdx_id": "MIT",
|
||||
"url": "https://api.github.com/licenses/mit",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
"forks": 2,
|
||||
"open_issues": 0,
|
||||
"watchers": 5,
|
||||
"default_branch": "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get User Issues Filtered",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get User Issues Filtered": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"name": "Github User getUserIssues Limit Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "getUserIssues",
|
||||
"returnAll": false,
|
||||
"limit": 1,
|
||||
"getUserIssuesFilters": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get User Issues Limited",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Issues Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Issues Response": [
|
||||
{
|
||||
"json": {
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/issues/1",
|
||||
"repository_url": "https://api.github.com/repos/someowner/somerepo",
|
||||
"labels_url": "https://api.github.com/repos/someowner/somerepo/issues/1/labels{/name}",
|
||||
"comments_url": "https://api.github.com/repos/someowner/somerepo/issues/1/comments",
|
||||
"events_url": "https://api.github.com/repos/someowner/somerepo/issues/1/events",
|
||||
"html_url": "https://github.com/someowner/somerepo/issues/1",
|
||||
"id": 1,
|
||||
"number": 1,
|
||||
"title": "Issue assigned to me",
|
||||
"user": {
|
||||
"login": "issueauthor",
|
||||
"id": 5,
|
||||
"node_id": "MDQ6VXNlcjU=",
|
||||
"avatar_url": "https://github.com/images/error/issueauthor_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/issueauthor",
|
||||
"html_url": "https://github.com/issueauthor",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"labels": [
|
||||
{
|
||||
"id": 208045946,
|
||||
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/labels/bug",
|
||||
"name": "bug",
|
||||
"description": "Something isn't working",
|
||||
"color": "d73a49",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"assignee": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
"avatar_url": "https://github.com/images/error/testuser_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/testuser",
|
||||
"html_url": "https://github.com/testuser",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"milestone": null,
|
||||
"comments": 0,
|
||||
"created_at": "2011-04-22T13:33:48Z",
|
||||
"updated_at": "2011-04-22T13:33:48Z",
|
||||
"closed_at": null,
|
||||
"author_association": "NONE",
|
||||
"active_lock_reason": null,
|
||||
"body": "This is an issue assigned to me.",
|
||||
"reactions": {
|
||||
"url": "https://api.github.com/repos/someowner/somerepo/issues/1/reactions",
|
||||
"total_count": 0,
|
||||
"+1": 0,
|
||||
"-1": 0,
|
||||
"laugh": 0,
|
||||
"hooray": 0,
|
||||
"confused": 0,
|
||||
"heart": 0,
|
||||
"rocket": 0,
|
||||
"eyes": 0
|
||||
},
|
||||
"timeline_url": "https://api.github.com/repos/someowner/somerepo/issues/1/timeline",
|
||||
"performed_via_github_app": null,
|
||||
"state_reason": null,
|
||||
"repository": {
|
||||
"id": 1296269,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
|
||||
"name": "somerepo",
|
||||
"full_name": "someowner/somerepo",
|
||||
"owner": {
|
||||
"login": "someowner",
|
||||
"id": 6,
|
||||
"node_id": "MDQ6VXNlcjY=",
|
||||
"avatar_url": "https://github.com/images/error/someowner_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/someowner",
|
||||
"html_url": "https://github.com/someowner",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/someowner/somerepo",
|
||||
"description": "Repository with issues assigned to testuser",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/someowner/somerepo",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"git_url": "git://github.com/someowner/somerepo.git",
|
||||
"ssh_url": "git@github.com:someowner/somerepo.git",
|
||||
"clone_url": "https://github.com/someowner/somerepo.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 9,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License",
|
||||
"spdx_id": "MIT",
|
||||
"url": "https://api.github.com/licenses/mit",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
"forks": 9,
|
||||
"open_issues": 0,
|
||||
"watchers": 9,
|
||||
"default_branch": "master"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get User Issues Limited",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get User Issues Limited": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Issues Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"name": "Github User getRepositories Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "getRepositories",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testuser",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": true
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get User Repositories",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Repositories Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Repositories Response": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1296269,
|
||||
"name": "hello-world",
|
||||
"full_name": "testuser/hello-world",
|
||||
"owner": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"type": "User"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testuser/hello-world",
|
||||
"description": "My first repository on GitHub!",
|
||||
"fork": false,
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"clone_url": "https://github.com/testuser/hello-world.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"forks_count": 9,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License"
|
||||
},
|
||||
"visibility": "public",
|
||||
"default_branch": "master"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 1296270,
|
||||
"name": "my-app",
|
||||
"full_name": "testuser/my-app",
|
||||
"owner": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"type": "User"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testuser/my-app",
|
||||
"description": "My awesome application",
|
||||
"fork": false,
|
||||
"created_at": "2011-02-26T19:01:12Z",
|
||||
"updated_at": "2011-02-26T19:14:43Z",
|
||||
"pushed_at": "2011-02-26T19:06:43Z",
|
||||
"clone_url": "https://github.com/testuser/my-app.git",
|
||||
"size": 512,
|
||||
"stargazers_count": 156,
|
||||
"watchers_count": 45,
|
||||
"language": "JavaScript",
|
||||
"forks_count": 23,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 5,
|
||||
"license": {
|
||||
"key": "apache-2.0",
|
||||
"name": "Apache License 2.0"
|
||||
},
|
||||
"visibility": "public",
|
||||
"default_branch": "main"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get User Repositories",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get User Repositories": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Repositories Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"name": "Github User getRepositories Limit Test Workflow",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 0],
|
||||
"id": "trigger-id",
|
||||
"name": "When clicking 'Execute Workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "getRepositories",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testuser",
|
||||
"mode": "name"
|
||||
},
|
||||
"returnAll": false,
|
||||
"limit": 1
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 0],
|
||||
"id": "github-node-id",
|
||||
"name": "Get User Repositories Limited",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "credential-id",
|
||||
"name": "Test Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 0],
|
||||
"id": "noop-id",
|
||||
"name": "Repositories Response"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Repositories Response": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1296269,
|
||||
"name": "hello-world",
|
||||
"full_name": "testuser/hello-world",
|
||||
"owner": {
|
||||
"login": "testuser",
|
||||
"id": 1,
|
||||
"type": "User"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/testuser/hello-world",
|
||||
"description": "My first repository on GitHub!",
|
||||
"fork": false,
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"updated_at": "2011-01-26T19:14:43Z",
|
||||
"pushed_at": "2011-01-26T19:06:43Z",
|
||||
"clone_url": "https://github.com/testuser/hello-world.git",
|
||||
"size": 108,
|
||||
"stargazers_count": 80,
|
||||
"watchers_count": 9,
|
||||
"language": "C",
|
||||
"forks_count": 9,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 0,
|
||||
"license": {
|
||||
"key": "mit",
|
||||
"name": "MIT License"
|
||||
},
|
||||
"visibility": "public",
|
||||
"default_branch": "master"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute Workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get User Repositories Limited",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get User Repositories Limited": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Repositories Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Test Github Node - Dispatch and Wait', () => {
|
||||
describe('Workflow Dispatch and Wait', () => {
|
||||
const now = 1683028800000;
|
||||
const owner = 'Owner';
|
||||
const repository = 'test-github-actions';
|
||||
const workflowId = 145370278;
|
||||
const ref = 'test-branch';
|
||||
|
||||
const usersResponse = {
|
||||
total_count: 1,
|
||||
items: [
|
||||
{
|
||||
login: owner,
|
||||
id: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const repositoriesResponse = {
|
||||
total_count: 1,
|
||||
items: [
|
||||
{
|
||||
id: 3081286,
|
||||
name: repository,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const workflowsResponse = {
|
||||
total_count: 1,
|
||||
workflows: [
|
||||
{
|
||||
id: workflowId,
|
||||
node_id: 'MDg6V29ya2Zsb3cxNjEzMzU=',
|
||||
name: 'New Test Workflow',
|
||||
path: '.github/workflows/test.yaml',
|
||||
state: 'active',
|
||||
created_at: '2020-01-08T23:48:37.000-08:00',
|
||||
updated_at: '2020-01-08T23:50:21.000-08:00',
|
||||
url: `https://api.github.com/repos/${owner}/${repository}/actions/workflows/${workflowId}`,
|
||||
html_url: `https://github.com/${owner}/${repository}/blob/master/.github/workflows/test.yaml`,
|
||||
badge_url: `https://github.com/${owner}/${repository}/workflows/New%20Test%20Workflow/badge.svg`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const refsResponse = [{ ref: `refs/heads/${ref}` }];
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const baseUrl = 'https://api.github.com';
|
||||
nock.cleanAll();
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
|
||||
.get('/search/users')
|
||||
.query(true)
|
||||
.reply(200, usersResponse)
|
||||
.get('/search/repositories')
|
||||
.query(true)
|
||||
.reply(200, repositoriesResponse)
|
||||
.get(`/repos/${owner}/${repository}/actions/workflows`)
|
||||
.reply(200, workflowsResponse)
|
||||
.get(`/repos/${owner}/${repository}/git/refs`)
|
||||
.reply(200, refsResponse)
|
||||
.post(
|
||||
`/repos/${owner}/${repository}/actions/workflows/${workflowId}/dispatches`,
|
||||
(body) => {
|
||||
return body.ref === ref && body.inputs?.resumeUrl;
|
||||
},
|
||||
)
|
||||
.reply(200, {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['GithubDispatchAndWaitWorkflow.json'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,702 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
import nock from 'nock';
|
||||
|
||||
import * as utilities from '../../../../utils/utilities';
|
||||
import { Github } from '../../Github.node';
|
||||
|
||||
describe('Test Github Node', () => {
|
||||
describe('Workflow Dispatch', () => {
|
||||
const now = 1683028800000;
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
const usersResponse = {
|
||||
total_count: 12,
|
||||
items: [
|
||||
{
|
||||
login: 'testOwner',
|
||||
id: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
const repositoriesResponse = {
|
||||
total_count: 40,
|
||||
items: [
|
||||
{
|
||||
id: 3081286,
|
||||
name: 'testRepository',
|
||||
},
|
||||
],
|
||||
};
|
||||
const workflowsResponse = {
|
||||
total_count: 2,
|
||||
workflows: [
|
||||
{
|
||||
id: workflowId,
|
||||
node_id: 'MDg6V29ya2Zsb3cxNjEzMzU=',
|
||||
name: 'CI',
|
||||
path: '.github/workflows/blank.yaml',
|
||||
state: 'active',
|
||||
created_at: '2020-01-08T23:48:37.000-08:00',
|
||||
updated_at: '2020-01-08T23:50:21.000-08:00',
|
||||
url: 'https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335',
|
||||
html_url: 'https://github.com/octo-org/octo-repo/blob/master/.github/workflows/161335',
|
||||
badge_url: 'https://github.com/octo-org/octo-repo/workflows/CI/badge.svg',
|
||||
},
|
||||
{
|
||||
id: 269289,
|
||||
node_id: 'MDE4OldvcmtmbG93IFNlY29uZGFyeTI2OTI4OQ==',
|
||||
name: 'Linter',
|
||||
path: '.github/workflows/linter.yaml',
|
||||
state: 'active',
|
||||
created_at: '2020-01-08T23:48:37.000-08:00',
|
||||
updated_at: '2020-01-08T23:50:21.000-08:00',
|
||||
url: 'https://api.github.com/repos/octo-org/octo-repo/actions/workflows/269289',
|
||||
html_url: 'https://github.com/octo-org/octo-repo/blob/master/.github/workflows/269289',
|
||||
badge_url: 'https://github.com/octo-org/octo-repo/workflows/Linter/badge.svg',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
});
|
||||
|
||||
describe('removeTrailingSlash Function', () => {
|
||||
let githubNode: Github;
|
||||
let mockExecutionContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
mockExecutionContext = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Github' }),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
user: 'test',
|
||||
accessToken: 'test',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
requestWithAuthentication: jest.fn().mockResolvedValue({}),
|
||||
constructExecutionMetaData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
},
|
||||
};
|
||||
|
||||
jest.spyOn(utilities, 'removeTrailingSlash');
|
||||
jest.mock('../../../../utils/utilities', () => ({
|
||||
...jest.requireActual('../../../../utils/utilities'),
|
||||
getFileSha: jest.fn().mockResolvedValue('mockedSHA'),
|
||||
}));
|
||||
});
|
||||
|
||||
it('should call remove trailing slash', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'operation') {
|
||||
return 'list';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'file';
|
||||
}
|
||||
if (parameterName === 'filePath') {
|
||||
return 'path/to/file/';
|
||||
}
|
||||
if (parameterName === 'owner') {
|
||||
return 'me';
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return 'repo';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(utilities.removeTrailingSlash).toHaveBeenCalledWith('path/to/file/');
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'githubOAuth2Api',
|
||||
{
|
||||
body: {},
|
||||
headers: { 'User-Agent': 'n8n' },
|
||||
json: true,
|
||||
method: 'GET',
|
||||
qs: {},
|
||||
uri: 'https://api.github.com/repos/me/repo/contents/path%2Fto%2Ffile',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const baseUrl = 'https://api.github.com';
|
||||
nock(baseUrl)
|
||||
.persist()
|
||||
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
|
||||
.get('/search/users')
|
||||
.query(true)
|
||||
.reply(200, usersResponse)
|
||||
.get('/search/repositories')
|
||||
.query(true)
|
||||
.reply(200, repositoriesResponse)
|
||||
.get(`/repos/${owner}/${repository}/actions/workflows`)
|
||||
.reply(200, workflowsResponse)
|
||||
.post(`/repos/${owner}/${repository}/actions/workflows/${workflowId}/dispatches`, {
|
||||
ref: 'main',
|
||||
inputs: {},
|
||||
})
|
||||
.reply(200, {});
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['GithubTestWorkflow.json'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
let githubNode: Github;
|
||||
let mockExecutionContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
mockExecutionContext = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Github' }),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
putExecutionToWait: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
user: 'test',
|
||||
accessToken: 'test',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
httpRequest: jest.fn(),
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
requestWithAuthentication: jest
|
||||
.fn()
|
||||
.mockImplementation(async (_credentialType, options) => {
|
||||
if (options.uri.includes('dispatches') && options.method === 'POST') {
|
||||
const error: any = new Error('Not Found');
|
||||
error.statusCode = 404;
|
||||
error.message = 'Not Found';
|
||||
throw error;
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
request: jest.fn(),
|
||||
constructExecutionMetaData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
assertBinaryData: jest.fn(),
|
||||
prepareBinaryData: jest.fn(),
|
||||
},
|
||||
getWorkflowDataProxy: jest.fn().mockReturnValue({
|
||||
$execution: {
|
||||
resumeUrl: 'https://example.com/webhook',
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should throw NodeOperationError for invalid JSON inputs', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'inputs') {
|
||||
return 'invalid json';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'dispatchAndWait';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
}).rejects.toThrow(NodeOperationError);
|
||||
});
|
||||
|
||||
it('should throw NodeOperationError for 404 errors when dispatching a workflow', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 404,
|
||||
message: 'Not Found',
|
||||
});
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'inputs') {
|
||||
return '{}';
|
||||
}
|
||||
if (parameterName === 'ref') {
|
||||
return 'main';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'dispatchAndWait';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
}).rejects.toThrow(/The workflow to dispatch could not be found/);
|
||||
});
|
||||
|
||||
it('should throw NodeApiError for general API errors', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 500,
|
||||
message: 'Internal Server Error',
|
||||
});
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'inputs') {
|
||||
return '{}';
|
||||
}
|
||||
if (parameterName === 'ref') {
|
||||
return 'main';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'dispatch';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
}).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should throw NodeApiError for general API errors in dispatchAndWait operation', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.getWorkflowDataProxy = jest.fn().mockReturnValue({
|
||||
$execution: {
|
||||
resumeUrl: 'https://example.com/webhook',
|
||||
},
|
||||
});
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 500,
|
||||
message: 'Internal Server Error',
|
||||
});
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'inputs') {
|
||||
return '{}';
|
||||
}
|
||||
if (parameterName === 'ref') {
|
||||
return 'main';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'dispatchAndWait';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
}).rejects.toThrow(NodeApiError);
|
||||
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
uri: expect.stringContaining(
|
||||
`/repos/${owner}/${repository}/actions/workflows/${workflowId}/dispatches`,
|
||||
),
|
||||
body: expect.objectContaining({
|
||||
ref: 'main',
|
||||
inputs: expect.objectContaining({
|
||||
resumeUrl: 'https://example.com/webhook',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Workflow Operations', () => {
|
||||
let githubNode: Github;
|
||||
let mockExecutionContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
mockExecutionContext = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Github' }),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
user: 'test',
|
||||
accessToken: 'test',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
requestWithAuthentication: jest.fn().mockResolvedValue({}),
|
||||
constructExecutionMetaData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should use extractValue for workflowId in disable operation', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation(
|
||||
(parameterName: string, _itemIndex: number, defaultValue: string, options?: any) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
expect(options).toBeDefined();
|
||||
expect(options.extractValue).toBe(true);
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'disable';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
);
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'PUT',
|
||||
uri: `https://api.github.com/repos/${owner}/${repository}/actions/workflows/${workflowId}/disable`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should use extractValue for workflowId in enable operation', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation(
|
||||
(parameterName: string, _itemIndex: number, defaultValue: string, options?: any) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
expect(options).toBeDefined();
|
||||
expect(options.extractValue).toBe(true);
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'enable';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
);
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'PUT',
|
||||
uri: `https://api.github.com/repos/${owner}/${repository}/actions/workflows/${workflowId}/enable`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should use extractValue for workflowId in get operation', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation(
|
||||
(parameterName: string, _itemIndex: number, defaultValue: string, options?: any) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
expect(options).toBeDefined();
|
||||
expect(options.extractValue).toBe(true);
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'get';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
);
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: `https://api.github.com/repos/${owner}/${repository}/actions/workflows/${workflowId}`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should use extractValue for workflowId in getUsage operation', async () => {
|
||||
const owner = 'testOwner';
|
||||
const repository = 'testRepository';
|
||||
const workflowId = 147025216;
|
||||
|
||||
mockExecutionContext.getNodeParameter.mockImplementation(
|
||||
(parameterName: string, _itemIndex: number, defaultValue: string, options?: any) => {
|
||||
if (parameterName === 'owner') {
|
||||
return owner;
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return repository;
|
||||
}
|
||||
if (parameterName === 'workflowId') {
|
||||
expect(options).toBeDefined();
|
||||
expect(options.extractValue).toBe(true);
|
||||
return workflowId;
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'workflow';
|
||||
}
|
||||
if (parameterName === 'operation') {
|
||||
return 'getUsage';
|
||||
}
|
||||
if (parameterName === 'authentication') {
|
||||
return 'accessToken';
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
);
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: `https://api.github.com/repos/${owner}/${repository}/actions/workflows/${workflowId}/timing`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Parameter Extraction', () => {
|
||||
it('should use extractValue for workflowId parameter', () => {
|
||||
const githubNode = new Github();
|
||||
const description = githubNode.description;
|
||||
|
||||
const workflowIdParam = description.properties.find((prop) => prop.name === 'workflowId');
|
||||
|
||||
expect(workflowIdParam).toBeDefined();
|
||||
expect(workflowIdParam?.type).toBe('resourceLocator');
|
||||
|
||||
const workflowOperations = description.properties.find(
|
||||
(prop) =>
|
||||
prop.name === 'operation' && prop.displayOptions?.show?.resource?.includes('workflow'),
|
||||
);
|
||||
|
||||
expect(workflowOperations).toBeDefined();
|
||||
expect(workflowOperations?.options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ value: 'disable' }),
|
||||
expect.objectContaining({ value: 'dispatch' }),
|
||||
expect.objectContaining({ value: 'enable' }),
|
||||
expect.objectContaining({ value: 'get' }),
|
||||
expect.objectContaining({ value: 'getUsage' }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('User Operations', () => {
|
||||
let githubNode: Github;
|
||||
let mockExecutionContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
mockExecutionContext = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Github' }),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
user: 'test',
|
||||
accessToken: 'test',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
requestWithAuthentication: jest.fn().mockResolvedValue({}),
|
||||
constructExecutionMetaData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should fetch open issues by default (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getUserIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return {};
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [
|
||||
{ id: 1, title: 'Issue 1', state: 'open' },
|
||||
{ id: 2, title: 'Issue 2', state: 'open' },
|
||||
],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.not.objectContaining({ state: 'closed' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch closed issues when state filter is set to closed (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getUserIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return { state: 'closed' };
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [{ id: 3, title: 'Issue 3', state: 'closed' }],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.objectContaining({ state: 'closed' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch issues with a specific label (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getUserIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return { labels: 'bug' };
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [{ id: 4, title: 'Issue 4', state: 'open', labels: ['bug'] }],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.objectContaining({ labels: 'bug' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { Github } from '../../Github.node';
|
||||
|
||||
describe('Github Node - Webhook Method', () => {
|
||||
let githubNode: Github;
|
||||
let mockWebhookFunctions: IWebhookFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getRequestObject: jest.fn(),
|
||||
getResponseObject: jest.fn(),
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: jest.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn(),
|
||||
},
|
||||
} as unknown as IWebhookFunctions;
|
||||
});
|
||||
|
||||
it('should process webhook request and return workflowData', async () => {
|
||||
const sampleWebhookBody = {
|
||||
action: 'opened',
|
||||
issue: {
|
||||
number: 123,
|
||||
title: 'Test Issue',
|
||||
body: 'This is a test issue',
|
||||
user: {
|
||||
login: 'testuser',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
name: 'test-repo',
|
||||
owner: {
|
||||
login: 'test-owner',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockRequestObject = {
|
||||
body: sampleWebhookBody,
|
||||
headers: {
|
||||
'x-github-event': 'issues',
|
||||
'x-github-delivery': '72d3162e-cc78-11e3-81ab-4c9367dc0958',
|
||||
},
|
||||
};
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue(mockRequestObject);
|
||||
(mockWebhookFunctions.helpers.returnJsonArray as jest.Mock).mockReturnValue([
|
||||
sampleWebhookBody,
|
||||
]);
|
||||
|
||||
const result = await githubNode.webhook.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toEqual({
|
||||
workflowData: [[sampleWebhookBody]],
|
||||
});
|
||||
|
||||
expect(mockWebhookFunctions.getRequestObject).toHaveBeenCalled();
|
||||
expect(mockWebhookFunctions.helpers.returnJsonArray).toHaveBeenCalledWith(sampleWebhookBody);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [200, -360],
|
||||
"id": "0889ff06-41e2-4786-a0fe-ca330c3711e7",
|
||||
"name": "When clicking ‘Execute workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "workflow",
|
||||
"operation": "dispatchAndWait",
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "Owner",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Owner",
|
||||
"cachedResultUrl": "https://github.com/Owner"
|
||||
},
|
||||
"repository": {
|
||||
"__rl": true,
|
||||
"value": "test-github-actions",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-github-actions",
|
||||
"cachedResultUrl": "https://github.com/Owner/test-github-actions"
|
||||
},
|
||||
"workflowId": {
|
||||
"__rl": true,
|
||||
"value": 145370278,
|
||||
"mode": "list",
|
||||
"cachedResultName": "New Test Workflow"
|
||||
},
|
||||
"ref": {
|
||||
"__rl": true,
|
||||
"value": "test-branch",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-branch"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1.1,
|
||||
"position": [220, 0],
|
||||
"id": "105cb5f0-bcc3-4397-ba06-bda8cf46c71b",
|
||||
"name": "Dispatch and Wait for Completion",
|
||||
"webhookId": "02bfeade-db6c-412a-8627-fe3a9952e8ee",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "RtvkwCTqGZ2sLhB8",
|
||||
"name": "GitHub account 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Dispatch and Wait for Completion",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"Dispatch and Wait for Completion": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-300, 260],
|
||||
"id": "b14bf20f-78b0-490a-bbc6-d02b1af4c03c",
|
||||
"name": "When clicking ‘Execute workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "workflow",
|
||||
"workflowId": {
|
||||
"__rl": true,
|
||||
"value": 147025216,
|
||||
"mode": "list",
|
||||
"cachedResultName": "CI"
|
||||
},
|
||||
"owner": {
|
||||
"__rl": true,
|
||||
"value": "testOwner",
|
||||
"mode": "name"
|
||||
},
|
||||
"repository": {
|
||||
"__rl": true,
|
||||
"value": "testRepository",
|
||||
"mode": "name"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.github",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, 260],
|
||||
"id": "061752c9-507c-4b27-ba18-47b21d487aed",
|
||||
"name": "GitHub",
|
||||
"credentials": {
|
||||
"githubApi": {
|
||||
"id": "1",
|
||||
"name": "GitHub account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [120, 260],
|
||||
"id": "3bc54e8f-eeba-496d-a95f-bb8927eff671",
|
||||
"name": "No Operation, do nothing"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "GitHub",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"GitHub": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
import { GithubTrigger } from '../../GithubTrigger.node';
|
||||
import * as GenericFunctions from '../../GenericFunctions';
|
||||
import * as GithubTriggerHelpers from '../../GithubTriggerHelpers';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
describe('GithubTrigger Node', () => {
|
||||
describe('checkExists webhook method', () => {
|
||||
let webhookData: Record<string, any>;
|
||||
let mockThis: any;
|
||||
|
||||
beforeEach(() => {
|
||||
webhookData = {
|
||||
webhookId: '123456',
|
||||
webhookEvents: ['push'],
|
||||
};
|
||||
|
||||
mockThis = {
|
||||
getWorkflowStaticData: () => webhookData,
|
||||
getNodeParameter: jest.fn().mockImplementation((name: string) => {
|
||||
if (name === 'owner') return 'some-owner';
|
||||
if (name === 'repository') return 'some-repo';
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should delete webhook data and return false when webhook is not found (404)', async () => {
|
||||
jest.spyOn(GenericFunctions, 'githubApiRequest').mockRejectedValue({ httpCode: '404' });
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhookMethods.default.checkExists.call(mockThis);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(webhookData.webhookId).toBeUndefined();
|
||||
expect(webhookData.webhookEvents).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create webhook method', () => {
|
||||
let mockThis: any;
|
||||
let webhookData: Record<string, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
webhookData = {};
|
||||
mockThis = {
|
||||
getNodeWebhookUrl: () => 'https://example.com/webhook',
|
||||
getNodeParameter: jest.fn().mockImplementation((name: string) => {
|
||||
if (name === 'owner') return 'some-owner';
|
||||
if (name === 'repository') return 'some-repo';
|
||||
if (name === 'events') return ['push'];
|
||||
if (name === 'options') return { insecureSSL: false };
|
||||
}),
|
||||
getWorkflowStaticData: () => webhookData,
|
||||
getNode: () => ({}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should return true and set webhookId and webhookSecret when creation succeeds', async () => {
|
||||
const createdWebhook = { id: '789', active: true };
|
||||
|
||||
jest.spyOn(GenericFunctions, 'githubApiRequest').mockResolvedValueOnce(createdWebhook);
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhookMethods.default.create.call(mockThis);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(webhookData.webhookId).toBe('789');
|
||||
expect(webhookData.webhookSecret).toBeDefined();
|
||||
expect(typeof webhookData.webhookSecret).toBe('string');
|
||||
expect(webhookData.webhookSecret.length).toBe(64); // 32 bytes in hex
|
||||
});
|
||||
|
||||
it('should send the secret to GitHub API when creating webhook', async () => {
|
||||
const createdWebhook = { id: '789', active: true };
|
||||
|
||||
const apiRequestSpy = jest
|
||||
.spyOn(GenericFunctions, 'githubApiRequest')
|
||||
.mockResolvedValueOnce(createdWebhook);
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
await trigger.webhookMethods.default.create.call(mockThis);
|
||||
|
||||
expect(apiRequestSpy).toHaveBeenCalledWith(
|
||||
'POST',
|
||||
'/repos/some-owner/some-repo/hooks',
|
||||
expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
secret: expect.any(String),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 422 by checking for existing matching webhook (no secret stored)', async () => {
|
||||
const existingWebhook = {
|
||||
id: '123',
|
||||
events: ['push'],
|
||||
config: { url: 'https://example.com/webhook' },
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(GenericFunctions, 'githubApiRequest')
|
||||
.mockRejectedValueOnce({ httpCode: '422' }) // POST fails
|
||||
.mockResolvedValueOnce([existingWebhook]); // GET returns matching
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhookMethods.default.create.call(mockThis);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(webhookData.webhookId).toBe('123');
|
||||
// Existing webhook won't have secret stored (backwards compatibility)
|
||||
expect(webhookData.webhookSecret).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should throw NodeOperationError if repo is not found (404)', async () => {
|
||||
jest.spyOn(GenericFunctions, 'githubApiRequest').mockRejectedValue({ httpCode: '404' });
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
|
||||
await expect(trigger.webhookMethods.default.create.call(mockThis)).rejects.toThrow(
|
||||
NodeOperationError,
|
||||
);
|
||||
|
||||
await expect(trigger.webhookMethods.default.create.call(mockThis)).rejects.toThrow(
|
||||
/Check that the repository exists/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete webhook method', () => {
|
||||
let webhookData: Record<string, any>;
|
||||
let mockThis: any;
|
||||
|
||||
beforeEach(() => {
|
||||
webhookData = {
|
||||
webhookId: '123456',
|
||||
webhookEvents: ['push'],
|
||||
webhookSecret: 'test-secret',
|
||||
};
|
||||
|
||||
mockThis = {
|
||||
getWorkflowStaticData: () => webhookData,
|
||||
getNodeParameter: jest.fn().mockImplementation((name: string) => {
|
||||
if (name === 'owner') return 'some-owner';
|
||||
if (name === 'repository') return 'some-repo';
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should delete webhook data including secret when deletion succeeds', async () => {
|
||||
jest.spyOn(GenericFunctions, 'githubApiRequest').mockResolvedValueOnce({});
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhookMethods.default.delete.call(mockThis);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(webhookData.webhookId).toBeUndefined();
|
||||
expect(webhookData.webhookEvents).toBeUndefined();
|
||||
expect(webhookData.webhookSecret).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('webhook method', () => {
|
||||
let mockThis: any;
|
||||
let webhookData: Record<string, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
webhookData = {
|
||||
webhookSecret: 'test-secret',
|
||||
};
|
||||
|
||||
mockThis = {
|
||||
getWorkflowStaticData: () => webhookData,
|
||||
getBodyData: jest.fn().mockReturnValue({ action: 'opened' }),
|
||||
getHeaderData: jest.fn().mockReturnValue({}),
|
||||
getQueryData: jest.fn().mockReturnValue({}),
|
||||
getResponseObject: jest.fn().mockReturnValue({
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
}),
|
||||
getRequestObject: jest.fn().mockReturnValue({
|
||||
header: jest.fn(),
|
||||
rawBody: '{}',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockImplementation((data) => data),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should reject with 401 when signature verification fails', async () => {
|
||||
jest.spyOn(GithubTriggerHelpers, 'verifySignature').mockReturnValueOnce(false);
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhook.call(mockThis);
|
||||
|
||||
expect(result).toEqual({ noWebhookResponse: true });
|
||||
expect(mockThis.getResponseObject).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should process webhook when signature verification succeeds', async () => {
|
||||
jest.spyOn(GithubTriggerHelpers, 'verifySignature').mockReturnValueOnce(true);
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhook.call(mockThis);
|
||||
|
||||
expect(result).toHaveProperty('workflowData');
|
||||
});
|
||||
|
||||
it('should return OK for ping events when signature verification succeeds', async () => {
|
||||
jest.spyOn(GithubTriggerHelpers, 'verifySignature').mockReturnValueOnce(true);
|
||||
mockThis.getBodyData.mockReturnValue({ hook_id: '123' });
|
||||
|
||||
const trigger = new GithubTrigger();
|
||||
const result = await trigger.webhook.call(mockThis);
|
||||
|
||||
expect(result).toEqual({ webhookResponse: 'OK' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
|
||||
describe('Test Github Oauth2 Credentials Expression', () => {
|
||||
new NodeTestHarness().setupTests();
|
||||
});
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"name": "My workflow 255",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-300, 160],
|
||||
"id": "ab490d05-67aa-4061-b01d-4478e9984c75",
|
||||
"name": "When clicking ‘Execute workflow’"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "13c9caa1-e4e6-4365-8282-747dd318abce",
|
||||
"name": "server",
|
||||
"value": "https://github.example.com/api/v3",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [-40, 160],
|
||||
"id": "562791a3-7ff5-4f29-901b-5b1061cc2da2",
|
||||
"name": "Edit Fields"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "13c9caa1-e4e6-4365-8282-747dd318abce",
|
||||
"name": "authUrl",
|
||||
"value": "={{$json[\"server\"] === \"https://api.github.com\" ? \"https://github.com\" : $json[\"server\"].split(\"://\")[0] + \"://\" + $json[\"server\"].split(\"://\")[1].split(\"/\")[0]}}/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "02bd6a24-1375-490e-9683-7faedea36e20",
|
||||
"name": "accessTokenUrl",
|
||||
"value": "={{$json[\"server\"] === \"https://api.github.com\" ? \"https://github.com\" : $json[\"server\"].split(\"://\")[0] + \"://\" + $json[\"server\"].split(\"://\")[1].split(\"/\")[0]}}/login/oauth/access_token",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [200, 160],
|
||||
"id": "73059a97-269e-4c1a-b034-b114f3f5e671",
|
||||
"name": "Edit Fields1"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Edit Fields1": [
|
||||
{
|
||||
"json": {
|
||||
"authUrl": "https://github.example.com/login/oauth/authorize",
|
||||
"accessTokenUrl": "https://github.example.com/login/oauth/access_token"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "2acb676c-cdfb-4f4d-9000-decf4e2fdb93",
|
||||
"meta": {
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "s9LDuRXe2e5LF5jP",
|
||||
"tags": []
|
||||
}
|
||||
Reference in New Issue
Block a user