@apistudio/apim-cli
Version:
CLI for API Management Products
205 lines (180 loc) • 6.01 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
import { AssetValidator } from '../../src/service/validation-service.js';
import { Logger, SchemaHandler, YamlContent } from '@apic/studio-shared';
import type { ValidateFunction, ErrorObject } from 'ajv';
// Mock SchemaHandler specifically for this test while preserving Logger mock from setup.ts
jest.mock('@apic/studio-shared', () => {
const mockLogger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
log: jest.fn(),
createChildLogger: jest.fn().mockReturnThis(),
};
return {
Logger: mockLogger,
LogComponent: jest.fn(() => (target: any) => target),
Component: {
Build: 'Build',
},
SchemaHandler: jest.fn().mockImplementation(() => {
return {
getSchema: jest.fn((kind: string): string | null => {
// Return different schemas based on test cases
if (kind === 'InvalidSchema') {
return 'invalid-json';
} else if (kind === 'EmptySchema') {
return null;
} else if (kind === 'StagedPolicySequence') {
return JSON.stringify({ type: 'object', properties: {} });
} else if (kind === 'ValidationFailure') {
return JSON.stringify({
type: 'object',
required: ['requiredProperty'],
properties: {
requiredProperty: { type: 'string' },
},
});
} else {
return JSON.stringify({ type: 'object', properties: {} });
}
}),
};
}),
};
});
jest.mock('ajv', () => {
return {
Ajv: jest.fn().mockImplementation(() => {
return {
addFormat: jest.fn(),
compile: jest.fn((): ValidateFunction => {
const validate = ((data: any): boolean => {
if (data.kind === 'ValidationFailure') {
validate.errors = [
{
instancePath: '/path/to/error',
schemaPath: '#/required',
keyword: 'required',
message: 'must have required property',
params: { missingProperty: 'requiredProperty' },
} as ErrorObject,
];
return false;
}
return true;
}) as ValidateFunction;
validate.errors = null;
return validate;
}),
};
}),
};
});
jest.mock('ajv-formats', () => {
return {
default: jest.fn(() => {}),
};
});
describe('Asset Validate', () => {
let assetValidator: AssetValidator;
beforeEach(() => {
const schemaHandler = new SchemaHandler();
assetValidator = new AssetValidator(schemaHandler);
jest.clearAllMocks();
});
test('should return invalid when kind is missing', () => {
const genObj = {} as YamlContent;
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(false);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]).toContain(
`Error validating asset: Cannot read properties of undefined (reading 'namespace')`
);
expect(Logger.error).toHaveBeenCalledWith(expect.any(String), expect.any(Error));
});
test('should return invalid when schema is not found', () => {
const genObj = {
kind: 'EmptySchema',
apiVersion: 'v1',
metadata: {
labels: {
gatewayTypes: ['test-gateway'],
},
},
} as unknown as YamlContent;
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(false);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]).toContain('Schema not found for kind');
expect(Logger.error).toHaveBeenCalledWith(expect.any(String));
});
test('should skip validation for StagedPolicySequence', () => {
const genObj = {
kind: 'StagedPolicySequence',
apiVersion: 'v1',
metadata: {
labels: {
gatewayTypes: ['test-gateway'],
},
},
} as unknown as YamlContent;
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
test('should return invalid when validation fails', () => {
const genObj = {
kind: 'ValidationFailure',
apiVersion: 'v1',
metadata: {
labels: {
gatewayTypes: ['test-gateway'],
},
},
} as unknown as YamlContent;
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(false);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]).toContain(
'The asset undefined:undefined:undefined has one or more problems.'
);
expect(result.errors[0]).toContain('must have required property');
expect(result.errors[0]).toContain('Review the error and try publishing again');
expect(Logger.error).toHaveBeenCalledWith(expect.any(String));
});
test('should return valid when validation passes', () => {
const genObj = {
kind: 'ValidKind',
apiVersion: 'v1',
metadata: {
labels: {
gatewayTypes: ['test-gateway'],
},
},
} as unknown as YamlContent;
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
test('should handle exceptions during validation', () => {
const genObj = {
kind: 'InvalidSchema',
apiVersion: 'v1',
metadata: {
labels: {
gatewayTypes: ['test-gateway'],
},
},
} as unknown as YamlContent;
// This will cause an exception when trying to parse the schema
const result = assetValidator.validateAssets(genObj);
expect(result.valid).toBe(false);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]).toContain('Error validating asset');
expect(Logger.error).toHaveBeenCalledWith(expect.any(String), expect.any(Error));
});
});