@apistudio/apim-cli
Version:
CLI for API Management Products
433 lines (378 loc) • 15.6 kB
text/typescript
import { RuntimeInventory } from './runtimeInventory.js';
import { jest, describe, it, expect, beforeEach } from '@jest/globals';
// Mock the fs module
jest.mock('fs', () => ({
existsSync: jest.fn(),
readFileSync: jest.fn()
}));
describe('RuntimeInventory', () => {
let inventory: RuntimeInventory;
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Create a new instance of RuntimeInventory
inventory = new RuntimeInventory();
// Set up test data directly in the instance
inventory['schemaDefinitions'] = {
'api.ibm.com-v1_api.json': '{"schema": "api schema content"}',
'1.0.0_authorizeuser.json': '{"schema": "authorize user schema content"}',
'api.ibm.com-v1_plan.json': '{"schema": "plan schema content"}'
};
inventory['defaultVersionMap'] = {
'api': 'api.ibm.com-v1',
'authorizeuser': '1.0.0',
'plan': 'api.ibm.com-v1'
};
inventory['masterContent'] = {
'gateway-type-name': 'LWGW',
'policy-sequences': {
'staged': [
{
key: 'transport',
label: 'Transport',
assets: [
{ kind: 'transport_protocol', defautlVersion: '1.0.0' }
]
},
{
key: 'security',
label: 'Security',
assets: [
{ kind: 'identify_and_authorize', defautlVersion: '1.0.0' }
]
}
],
'free-flow': [
{
name: 'security',
type: 'group',
policies: [
{ name: 'identity_and_authorize', defaultVersion: '1.0.0', type: 'policy' }
]
}
]
},
'assetProperties': {
'api.ibm.com_v1_transport_protocol': {
'isDepricated': false,
'isMandatory': true,
'isCustomComponent': true
},
'api.ibm.com_v1_identify_and_authorize': {
'isDepricated': false,
'isMandatory': true,
'isCustomComponent': true
}
}
};
});
// Test getSchema method
describe('getSchema', () => {
it('should return schema when name and version are provided', () => {
const schema = inventory.getSchema('api', 'api.ibm.com-v1');
expect(schema).toBe('{"schema": "api schema content"}');
});
it('should return schema using default version when only name is provided', () => {
const schema = inventory.getSchema('api');
expect(schema).toBe('{"schema": "api schema content"}');
});
it('should return schema for a different resource with version', () => {
const schema = inventory.getSchema('plan', 'api.ibm.com-v1');
expect(schema).toBe('{"schema": "plan schema content"}');
});
it('should return schema for policy with version', () => {
const schema = inventory.getSchema('authorizeuser', '1.0.0');
expect(schema).toBe('{"schema": "authorize user schema content"}');
});
it('should return undefined for non-existent schema', () => {
const schema = inventory.getSchema('nonexistent', '1.0.0');
expect(schema).toBeUndefined();
});
it('should return undefined when no version is provided and no default exists', () => {
const schema = inventory.getSchema('nonexistent');
expect(schema).toBeUndefined();
});
});
// Test getSchemaFromDestination method
describe('getSchemaFromDestination', () => {
it('should return undefined as destination schemas are not supported', () => {
const schema = inventory.getSchemaFromDestination('api', 'api.ibm.com-v1');
expect(schema).toBeUndefined();
});
});
// Test getTypescript method
describe('getTypescript', () => {
it('should return undefined as TypeScript definitions are not supported', () => {
const typescript = inventory.getTypescript('api', 'api.ibm.com-v1');
expect(typescript).toBeUndefined();
});
});
// Test getLintRuleset method
describe('getLintRuleset', () => {
// Store the original implementation
const originalGetLintRuleset = RuntimeInventory.prototype.getLintRuleset;
beforeEach(() => {
// Create a mock implementation
jest.spyOn(inventory, 'getLintRuleset').mockImplementation((name, version) => {
if (name === 'api' && (version === 'api.ibm.com-v1' || !version)) {
return {
extends: './global-assets-rule.spectral.yaml',
rules: {
'test-rule': { severity: 'error' },
'invalid-kind-value': {
severity: 'error',
given: '$',
then: {
field: 'kind',
function: 'enum',
functionOptions: {
values: [
'API', 'CORS', 'Quota', 'Product', 'Plan', 'StagedPolicySequence',
// ... other valid kinds
]
}
}
},
'invalid-api-version': {
severity: 'error',
given: '$',
then: {
field: 'apiVersion',
function: 'enum',
functionOptions: {
values: [
'api.ibm.com/v1'
]
}
}
}
}
};
}
return undefined;
});
});
afterEach(() => {
// Restore the original implementation
jest.restoreAllMocks();
});
it('should return ruleset when name and version are provided', () => {
const ruleset = inventory.getLintRuleset('api', 'api.ibm.com-v1');
expect(ruleset).toBeDefined();
expect(ruleset?.rules).toBeDefined();
expect(ruleset?.rules['test-rule']).toBeDefined();
expect(ruleset?.rules['test-rule'].severity).toBe('error');
});
it('should return ruleset using default version when only name is provided', () => {
const ruleset = inventory.getLintRuleset('api');
expect(ruleset).toBeDefined();
expect(ruleset?.rules).toBeDefined();
});
it('should return undefined for non-existent ruleset', () => {
const ruleset = inventory.getLintRuleset('nonexistent', '1.0.0');
expect(ruleset).toBeUndefined();
});
it('should include the kind validation rule', () => {
const ruleset = inventory.getLintRuleset('api');
expect(ruleset).toBeDefined();
expect(ruleset?.rules['invalid-kind-value']).toBeDefined();
expect(ruleset?.rules['invalid-kind-value'].severity).toBe('error');
expect(ruleset?.rules['invalid-kind-value'].then.function).toBe('enum');
expect(Array.isArray(ruleset?.rules['invalid-kind-value'].then.functionOptions.values)).toBe(true);
});
it('should include the apiVersion validation rule', () => {
const ruleset = inventory.getLintRuleset('api');
expect(ruleset).toBeDefined();
expect(ruleset?.rules['invalid-api-version']).toBeDefined();
expect(ruleset?.rules['invalid-api-version'].severity).toBe('error');
expect(ruleset?.rules['invalid-api-version'].then.function).toBe('enum');
expect(Array.isArray(ruleset?.rules['invalid-api-version'].then.functionOptions.values)).toBe(true);
expect(ruleset?.rules['invalid-api-version'].then.functionOptions.values).toContain('api.ibm.com/v1');
});
});
// Test getStagedPolicies method
describe('getStagedPolicies', () => {
it('should return staged policies with stage and policy information', () => {
const stagedPolicies = inventory.getStagedPolicies();
expect(stagedPolicies).toBeDefined();
expect(stagedPolicies?.transport).toBeDefined();
expect(stagedPolicies?.transport.stage).toBe('transport');
expect(Array.isArray(stagedPolicies?.transport.policies)).toBe(true);
expect(stagedPolicies?.transport.policies[0].name).toBe('transport_protocol');
expect(stagedPolicies?.transport.policies[0].defaultVersion).toBe('1.0.0');
expect(stagedPolicies?.transport.policies[0].type).toBe('staged');
});
it('should return undefined if no staged policies exist', () => {
inventory['masterContent'] = { 'gateway-type-name': 'LWGW' };
const stagedPolicies = inventory.getStagedPolicies();
expect(stagedPolicies).toBeUndefined();
});
});
// Test getFreeFlowPolicies method
describe('getFreeFlowPolicies', () => {
it('should return free flow policies with group and policy information', () => {
const freeFlowPolicies = inventory.getFreeFlowPolicies();
expect(freeFlowPolicies).toBeDefined();
expect(freeFlowPolicies?.security).toBeDefined();
expect(freeFlowPolicies?.security.group).toBe('security');
expect(freeFlowPolicies?.security.type).toBe('group');
expect(Array.isArray(freeFlowPolicies?.security.policies)).toBe(true);
expect(freeFlowPolicies?.security.policies[0].name).toBe('identity_and_authorize');
expect(freeFlowPolicies?.security.policies[0].defaultVersion).toBe('1.0.0');
expect(freeFlowPolicies?.security.policies[0].type).toBe('free-flow');
});
it('should return undefined if no free-flow policies exist', () => {
inventory['masterContent'] = { 'gateway-type-name': 'LWGW' };
const freeFlowPolicies = inventory.getFreeFlowPolicies();
expect(freeFlowPolicies).toBeUndefined();
});
it('should return undefined if free-flow is not an array', () => {
inventory['masterContent'] = {
'policy-sequences': {
// @ts-ignore - Intentionally setting an invalid type for testing
'free-flow': 'not-an-array'
}
};
const freeFlowPolicies = inventory.getFreeFlowPolicies();
expect(freeFlowPolicies).toBeUndefined();
});
});
// Test getMasterContents method
describe('getMasterContents', () => {
it('should return master contents', () => {
const masterContents = inventory.getMasterContents();
expect(masterContents).toBeDefined();
expect(masterContents?.['gateway-type-name']).toBe('LWGW');
expect(masterContents?.assetProperties).toBeDefined();
});
});
// Test getPolicySequenceType method
describe('getPolicySequenceType', () => {
it('should return available sequence types', () => {
const sequenceTypes = inventory.getPolicySequenceType();
expect(sequenceTypes).toBeDefined();
expect(Array.isArray(sequenceTypes?.sequenceTypes)).toBe(true);
expect(sequenceTypes?.sequenceTypes).toContain('staged');
expect(sequenceTypes?.sequenceTypes).toContain('free-flow');
});
it('should return undefined if no sequence types exist', () => {
inventory['masterContent'] = { 'gateway-type-name': 'LWGW' };
const sequenceTypes = inventory.getPolicySequenceType();
expect(sequenceTypes).toBeUndefined();
});
});
// Test getPolicyDefaultVersion method
describe('getPolicyDefaultVersion', () => {
it('should return default version for a staged policy', () => {
const defaultVersion = inventory.getPolicyDefaultVersion('staged', 'transport', 'transport_protocol');
expect(defaultVersion).toBe('1.0.0');
});
it('should return default version for a free flow policy', () => {
const defaultVersion = inventory.getPolicyDefaultVersion('free-flow', 'security', 'identity_and_authorize');
expect(defaultVersion).toBe('1.0.0');
});
it('should return undefined for non-existent policy', () => {
const defaultVersion = inventory.getPolicyDefaultVersion('staged', 'nonexistent', 'nonexistent');
expect(defaultVersion).toBeUndefined();
});
});
// Test getPolicyInfo method
describe('getPolicyInfo', () => {
it('should return policy info for a staged policy', () => {
const policyInfo = inventory.getPolicyInfo('staged', 'transport', 'transport_protocol');
expect(policyInfo).toBeDefined();
expect(policyInfo?.name).toBe('transport_protocol');
expect(policyInfo?.sequenceType).toBe('staged');
expect(policyInfo?.group).toBe('transport');
expect(policyInfo?.defaultVersion).toBe('1.0.0');
expect(policyInfo?.policy).toBeDefined();
});
it('should return policy info for a free flow policy', () => {
const policyInfo = inventory.getPolicyInfo('free-flow', 'security', 'identity_and_authorize');
expect(policyInfo).toBeDefined();
expect(policyInfo?.name).toBe('identity_and_authorize');
expect(policyInfo?.sequenceType).toBe('free-flow');
expect(policyInfo?.group).toBe('security');
expect(policyInfo?.defaultVersion).toBe('1.0.0');
expect(policyInfo?.policy).toBeDefined();
});
it('should return undefined for non-existent policy', () => {
const policyInfo = inventory.getPolicyInfo('staged', 'nonexistent', 'nonexistent');
expect(policyInfo).toBeUndefined();
});
});
// Test flattenPolicies method (indirectly)
describe('flattenPolicies', () => {
it('should flatten nested policy groups', () => {
// Create a nested policy structure
const nestedPolicies = [
{ name: 'policy1', type: 'policy' },
{
name: 'group1',
type: 'group',
policies: [
{ name: 'policy2', type: 'policy' },
{
name: 'group2',
type: 'group',
policies: [
{ name: 'policy3', type: 'policy' }
]
}
]
}
];
// Set up the master content with the nested structure
inventory['masterContent'] = {
'policy-sequences': {
'free-flow': [
{
name: 'test-group',
type: 'group',
policies: nestedPolicies
}
]
}
};
// Get the free flow policies which will use flattenPolicies internally
const freeFlowPolicies = inventory.getFreeFlowPolicies();
// Verify all policies were flattened
expect(freeFlowPolicies).toBeDefined();
expect(freeFlowPolicies?.['test-group'].policies.length).toBe(3);
// Check that all policies are present
const policyNames = freeFlowPolicies?.['test-group'].policies.map(p => p.name);
expect(policyNames).toContain('policy1');
expect(policyNames).toContain('policy2');
expect(policyNames).toContain('policy3');
});
});
// Test extendRulesetDefinitions method
describe('extendRulesetDefinitions', () => {
it('should override existing ruleset definitions when overrideExisting is true', () => {
// Set up initial ruleset
const initialRuleset = {
'test-ruleset.yaml': {
rules: {
'test-rule': { severity: 'error' }
}
}
};
// Set up override ruleset
const overrideRuleset = {
'test-ruleset.yaml': {
rules: {
'test-rule': { severity: 'warning' }
}
}
};
// Create a spy to verify the method is called
const spy = jest.spyOn(inventory, 'extendRulesetDefinitions');
// Call the method with overrideExisting = true
inventory.extendRulesetDefinitions(overrideRuleset, true);
// Verify the method was called with the correct arguments
expect(spy).toHaveBeenCalledWith(overrideRuleset, true);
// Clean up
spy.mockRestore();
});
});
});