n8n-nodes-awx
Version:
n8n node to interact with Ansible AWX/Tower with improved type safety
295 lines • 11.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const JobResources_1 = require("../src/resources/JobResources");
const mockApiClient = {
get: jest.fn(),
post: jest.fn(),
put: jest.fn(),
delete: jest.fn(),
};
jest.mock('../src/ApiClient', () => {
return jest.fn().mockImplementation(() => mockApiClient);
});
const createMockJob = (overrides = {}) => ({
id: 100,
type: 'job',
url: '/api/v2/jobs/100/',
related: {
job_template: '/api/v2/job_templates/42/',
cancel: '/api/v2/jobs/100/cancel/',
stdout: '/api/v2/jobs/100/stdout/',
},
summary_fields: {
job_template: { id: 42, name: 'Deploy Web Application' },
inventory: { id: 15, name: 'Production Servers' },
project: { id: 10, name: 'Web App Project' },
created_by: { username: 'admin', id: 1 },
credentials: [
{ id: 5, name: 'AWS Production', credential_type: 1 }
]
},
name: 'Deploy Web Application',
description: 'Deploy the web application to production',
job_type: 'run',
status: 'successful',
failed: false,
started: '2023-04-15T10:00:00Z',
finished: '2023-04-15T10:05:00Z',
elapsed: 300,
job_template: 42,
inventory: 15,
project: 10,
playbook: 'deploy.yml',
scm_revision: 'a1b2c3d4',
execution_environment: null,
execution_node: 'node1.example.com',
result_stdout: 'PLAY [Deploy web application] ***************************************',
result_traceback: '',
job_slice_number: 0,
job_slice_count: 1,
job_explanation: '',
allow_simultaneous: false,
artifacts: {},
scm_branch: 'main',
webhook_service: null,
webhook_credential: null,
webhook_guid: null,
diff_mode: false,
job_vars: {},
limit: 'all',
extra_vars: '{}',
job_tags: '',
skip_tags: '',
force_handlers: false,
start_at_task: null,
timeout: 0,
use_fact_cache: false,
organization: 1,
scm_type: 'git',
job_slice: null,
job_args: '',
job_cwd: '',
job_env: {},
job_status: 'successful',
result_stdout_file: '/api/v2/jobs/100/stdout/',
event_processing_finished: true,
hosts: {},
event_data: {},
playbook_counts: {
play_count: 1,
task_count: 5,
role_count: 0,
handler_count: 1,
},
custom_virtualenv: null,
instance_group: 1,
diff: false,
host_status_counts: {
ok: 5,
failures: 0,
dark: 0,
changed: 2,
skipped: 0,
rescued: 0,
ignored: 0,
},
...overrides
});
describe('JobResources', () => {
let jobResources;
beforeEach(() => {
jest.clearAllMocks();
jobResources = new JobResources_1.JobResources(mockApiClient);
mockApiClient.get.mockImplementation((url) => {
if (url.includes('/api/v2/jobs/')) {
if (url.includes('name=')) {
return Promise.resolve({
data: {
count: 1,
next: null,
previous: null,
results: [createMockJob()]
}
});
}
else if (url.endsWith('/stdout/')) {
return Promise.resolve({
data: 'Mocked job output\nLine 1\nLine 2\nLine 3'
});
}
else if (url.match(/\/api\/v2\/jobs\/\d+\/$/)) {
return Promise.resolve({
data: createMockJob()
});
}
return Promise.resolve({
data: {
count: 1,
next: null,
previous: null,
results: [createMockJob()]
}
});
}
return Promise.reject(new Error(`Unexpected URL: ${url}`));
});
mockApiClient.post.mockImplementation((url) => {
if (url.endsWith('/cancel/')) {
return Promise.resolve({
data: createMockJob({
status: 'canceled',
finished: new Date().toISOString()
})
});
}
return Promise.reject(new Error(`Unexpected URL: ${url}`));
});
});
describe('list', () => {
it('should list all jobs', async () => {
const jobs = await jobResources.list();
expect(jobs).toBeInstanceOf(Array);
expect(jobs).toHaveLength(1);
expect(jobs[0]).toMatchObject({
id: 100,
name: 'Deploy Web Application',
status: 'successful'
});
expect(mockApiClient.get).toHaveBeenCalledWith('/api/v2/jobs/');
});
it('should support filtering parameters', async () => {
await jobResources.list({ name: 'Deploy', status: 'successful' });
expect(mockApiClient.get).toHaveBeenCalledWith(expect.stringContaining('name=Deploy&status=successful'));
});
});
describe('get', () => {
it('should get a job by ID', async () => {
const job = await jobResources.get(100);
expect(job).toBeDefined();
expect(job.id).toBe(100);
expect(job.name).toBe('Deploy Web Application');
expect(mockApiClient.get).toHaveBeenCalledWith('/api/v2/jobs/100/');
});
it('should throw an error if job ID is not provided', async () => {
await expect(jobResources.get(0)).rejects.toThrow('Job ID is required');
});
});
describe('getByName', () => {
it('should get a job by name', async () => {
const job = await jobResources.getByName('Deploy Web Application');
expect(job).toBeDefined();
expect(job === null || job === void 0 ? void 0 : job.id).toBe(100);
expect(job === null || job === void 0 ? void 0 : job.name).toBe('Deploy Web Application');
expect(mockApiClient.get).toHaveBeenCalledWith(expect.stringContaining('name=Deploy%20Web%20Application'));
});
it('should return null if job is not found', async () => {
mockApiClient.get.mockResolvedValueOnce({
data: {
count: 0,
results: []
}
});
const job = await jobResources.getByName('Nonexistent Job');
expect(job).toBeNull();
});
it('should throw an error if multiple jobs are found', async () => {
mockApiClient.get.mockResolvedValueOnce({
data: {
count: 2,
results: [
createMockJob({ id: 100 }),
createMockJob({ id: 101 })
]
}
});
await expect(jobResources.getByName('Duplicate Job')).rejects.toThrow('Multiple jobs found with name: Duplicate Job');
});
});
describe('cancel', () => {
it('should cancel a running job', async () => {
const canceledJob = await jobResources.cancel(100);
expect(canceledJob).toBeDefined();
expect(canceledJob.status).toBe('canceled');
expect(mockApiClient.post).toHaveBeenCalledWith('/api/v2/jobs/100/cancel/', {});
});
it('should throw an error when job ID is 0', async () => {
await expect(jobResources.cancel(0)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is undefined', async () => {
await expect(jobResources.cancel(undefined)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is null', async () => {
await expect(jobResources.cancel(null)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is NaN', async () => {
await expect(jobResources.cancel(NaN)).rejects.toThrow('Job ID is required');
});
});
describe('getStdout', () => {
it('should get job stdout', async () => {
const stdout = await jobResources.getStdout(100);
expect(stdout).toBe('Mocked job output\nLine 1\nLine 2\nLine 3');
expect(mockApiClient.get).toHaveBeenCalledWith('/api/v2/jobs/100/stdout/');
});
it('should throw an error when job ID is 0', async () => {
await expect(jobResources.getStdout(0)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is undefined', async () => {
await expect(jobResources.getStdout(undefined)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is null', async () => {
await expect(jobResources.getStdout(null)).rejects.toThrow('Job ID is required');
});
it('should throw an error when job ID is NaN', async () => {
await expect(jobResources.getStdout(NaN)).rejects.toThrow('Job ID is required');
});
});
describe('buildUrl', () => {
const testBuildUrl = (jobResources, path, params) => {
const resourcesWithPrivate = jobResources;
return resourcesWithPrivate.buildUrl(path, params);
};
it('should return the path when no params are provided', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/');
expect(result).toBe('/api/v2/jobs/');
});
it('should handle empty params object', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/', {});
expect(result).toBe('/api/v2/jobs/');
});
it('should handle null params', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/', null);
expect(result).toBe('/api/v2/jobs/');
});
it('should append query parameters correctly', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/', {
name: 'test job',
status: 'successful',
page: 1
});
expect(result).toContain('name=test%20job');
expect(result).toContain('status=successful');
expect(result).toContain('page=1');
expect(result).toMatch(/^\/api\/v2\/jobs\/\?.*/);
});
it('should handle special characters in parameters', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/', {
search: 'job@example.com',
filter: 'status=running&name=test'
});
expect(result).toContain('search=job%40example.com');
expect(result).toContain('filter=status%3Drunning%26name%3Dtest');
});
it('should handle undefined and null parameter values', () => {
const result = testBuildUrl(jobResources, '/api/v2/jobs/', {
name: 'test',
status: undefined,
filter: null
});
expect(result).toContain('name=test');
expect(result).not.toContain('status=');
expect(result).not.toContain('filter=');
});
});
});
//# sourceMappingURL=JobResources.test.js.map