UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

379 lines (378 loc) 18 kB
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; 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(); }); }); });