Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add Unittests #54

Merged
merged 13 commits into from
Sep 9, 2024
147 changes: 147 additions & 0 deletions __tests__/unittest/defaults.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
const defaults = require('../../lib/defaults');

describe('defaults', () => {
describe('$schema', () => {
const test$schemaDefaultValue = 'https://sap.github.io/open-resource-discovery/spec-v1/interfaces/Document.schema.json';
it('should return default value', () => {
expect(defaults.$schema).toEqual(test$schemaDefaultValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let's have a meeting on this how we generally want to write unit-tests.

I think a test like this is not giving us more value than a snapshot test (that we already have), but is more cumbersome to maintain. For the "isEqual" tests, I think we can just rely on the Snapshot test to ensure that we don't accidentally change something without first reviewing and accepting the change.

For the real unit-tests, I would prefer to have them test more logical things, like that there's no "undefined" string somewhere in a generated ORD document (we had such issues creep in before), or that the ORD ID of the package assignment can be resolved within the ORD document.

We could test equals assumptions where it's really critical or has special meaning, like that the default policy level of default is "sap:base:v1".

@zongqichen , @ductaily , @aramovic79

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Fannon, thank you for valuable comments. We need a meeting discussing about unittest and code style with team agreement. From my understanding and experiences, uniitest should be simple, quick, fragile, readable and understandable. If the new codes break the unittest, we have a chance to think twice, either the issue from codes or tests. This is the front safeguard for us. And maintaining unittest should be as same important as production code, we should not worry about maintaining test.

The "snapshot test" you mentioned is more like integration test for me. The purpose of integration test is to watch how it looks like in general at the end with different environment settings or complex scenarios, which unittest can not accomplish.

Comparing with snapshot test, I feel the test you mentioned has more values in different scenarios:

  • The fine-grained unittest is easier and quicker to locate the issue. And during development, we can also run the fine-grained test independently, I think it's better to debug as well.
  • Later, I want to clean and refactor the code for fixing naming issue, reducing function complexity and duplicable. The fine-grained unittest can support this process.
  • I have same feeling with you. This PR is more like uniitest proposal and skeleton. We need to improve it with more test cases and more logics. Later, I'm pretty sure, we can reuse snapshot to reduce the line of codes. Back to this test, the target function is simply enough, I assume more flexible and testcases will be enough.

btw:
I found the bug undefined during writing unittest:

// TODO: fix undefined. Root cause: get the value from ${entity['@ODM.entityName']} without setting default value
, it's missing error handling or default condition.

});
});

describe('openResourceDiscovery', () => {
const testOpenResourceDiscoveryDefaultValue = '1.9';
it('should return default value', () => {
expect(defaults.openResourceDiscovery).toEqual(testOpenResourceDiscoveryDefaultValue);
});
});

describe('policyLevel', () => {
const testPolicyLevelDefaultValue = 'none';
it('should return default value', () => {
expect(defaults.policyLevel).toEqual(testPolicyLevelDefaultValue);
});
});

describe('description', () => {
const testDescriptionDefaultValue = 'this is an application description';
it('should return default value', () => {
expect(defaults.description).toEqual(testDescriptionDefaultValue);
});
});

describe('products', () => {
const testProductsName = 'My Product';
it('should return default value', () => {
expect(defaults.products(testProductsName)).toEqual([
{
ordId: 'customer:product:My.Product:',
title: 'My Product',
shortDescription: 'Description for My Product',
vendor: 'customer:vendor:customer:'
}
]);
});
});

describe('groupTypeId', () => {
const testGroupTypeIdDefaultValue = 'sap.cds:service';
it('should return default value', () => {
expect(defaults.groupTypeId).toEqual(testGroupTypeIdDefaultValue);
});
});

describe('packages', () => {
const testGetPackageDataName = 'My Package';
const testGetPackageCapNamespace = 'sample';
it('should return default value if policyLevel contains sap', () => {
const testPolicyLevel = 'sap:policy';
const testCreatePackage = (_, tag) => {
return {
ordId: `MyPackage:package:${testGetPackageCapNamespace}${tag}`,
title: `My Package`,
shortDescription: 'Short description for My Package',
description: 'Description for My Package',
version: '1.0.0',
partOfProducts: [`customer:product:My.Package:`],
vendor: 'customer:vendor:Customer:'
};
};
const testCreatePackageReturnValue = [testCreatePackage(testGetPackageDataName, '-api:v1'), testCreatePackage(testGetPackageDataName, '-event:v1')];

expect(defaults.packages(testGetPackageDataName, testPolicyLevel, testGetPackageCapNamespace)).toEqual(testCreatePackageReturnValue);
});

it('should return default value if policyLevel does not contain sap', () => {
const testPolicyLevel = 'policy';
const testCreatePackage = (_, tag) => {
return {
ordId: `MyPackage:package:${testGetPackageCapNamespace}${tag}`,
title: `My Package`,
shortDescription: 'Short description for My Package',
description: 'Description for My Package',
version: '1.0.0',
partOfProducts: [`customer:product:My.Package:`],
vendor: 'customer:vendor:Customer:'
};
};
const testCreatePackageReturnValue = [testCreatePackage(testGetPackageDataName, ':v1')];

expect(defaults.packages(testGetPackageDataName, testPolicyLevel, testGetPackageCapNamespace)).toEqual(testCreatePackageReturnValue);
});
});

describe('consumptionBundles', () => {
const testConsumptionBundlesName = 'My Consumption Bundle';
it('should return default value', () => {
expect(defaults.consumptionBundles(testConsumptionBundlesName)).toEqual([
{
ordId: 'MyConsumptionBundle:consumptionBundle:unknown:v1',
version: '1.0.0',
title: 'Unprotected resources',
shortDescription:
'If we have another protected API then it will be another object',
description:
'This Consumption Bundle contains all resources of the reference app which are unprotected and do not require authentication',
}
]);
});
});

describe('apiResources', () => {
it('should return default value', () => {
expect(defaults.apiResources).toEqual([]);
});
});

describe('eventResources', () => {
it('should return default value', () => {
expect(defaults.eventResources).toEqual([]);
});
});

describe('entityTypes', () => {
it('should return default value', () => {
expect(defaults.entityTypes).toEqual([]);
});
});

describe('baseTemplate', () => {
const testBaseTemplateDefaultValue = {
openResourceDiscoveryV1: {
documents: [
{
url: '/open-resource-discovery/v1/documents/1',
accessStrategies: [
{
type: 'open',
},
],
},
],
},
};
it('should return default value', () => {
expect(defaults.baseTemplate).toEqual(testBaseTemplateDefaultValue);
});
});
});
5 changes: 5 additions & 0 deletions __tests__/unittest/metaData.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe('metaData', () => {
it('test placeholder', () => {
expect(true).toBe(true);
});
});
5 changes: 5 additions & 0 deletions __tests__/unittest/ord.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe('ord', () => {
it('test placeholder', () => {
expect(true).toBe(true);
});
});
5 changes: 5 additions & 0 deletions __tests__/unittest/plugin.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe('plugin', () => {
it('test placeholder', () => {
expect(true).toBe(true);
});
});
197 changes: 197 additions & 0 deletions __tests__/unittest/templates.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
const cds = require('@sap/cds');
const templates = require('../../lib/templates');

describe('templates', () => {
let linkedModel;

beforeEach(() => {
linkedModel = cds.linked(`
namespace customer.testNamespace123;
entity Books {
key ID: UUID;
title: String;
}
`);
});

describe('fCreateEntityTypeTemplate', () => {
it('should return default value', () => {
expect(templates.fCreateEntityTypeTemplate(linkedModel)).toEqual({
// TODO: fix undefined. Root cause: get the value from ${entity['@ODM.entityName']} without setting default value
ordId: 'sap.odm:entityType:undefined:v1'
});
});
});

describe('checkEntityFunctionAction', () => {
it('should return entity', () => {
const testResult = [
{
type: 'entity',
name: 'customer.testNamespace123.Books' ,
entityType: 'customer.testNamespace123.Books',
entitySet: 'customer.testNamespace123.Books',
entityTypeMapping: 'undefined:entityType:customer.testNamespace123.Books:v1',
entitySetMapping: 'undefined:entitySet:customer.testNamespace123.Books:v1',
}
]
expect(templates.checkEntityFunctionAction(linkedModel, global)).toEqual(testResult);
});

it('should return actions', () => {
});

it('should return function', () => {
});

})

describe('fReplaceSpecialCharacters', () => {
it('should return replaced dot', () => {
const testString = 'customer.testNamespace.123';
expect(templates.fReplaceSpecialCharacters(testString)).toEqual('customer.testNamespace123');
});

it('should return replaced dash', () => {
const testString = 'customer.testNamespace-123';
expect(templates.fReplaceSpecialCharacters(testString)).toEqual('customer.testNamespace123');
});

// TODO: is that correct?
it('should return dash when there is no customer', () => {
const testString = 'testNamespace-123';
expect(templates.fReplaceSpecialCharacters(testString)).toEqual('testNamespace-123');
});
})

describe('fCreateGroupsTemplateForService', () => {
it('should return default value when groupIds do not have groupId', () => {
const testSrv = 'testServiceName';
global.namespace = 'customer';
const testGroupIds = new Set();
const testResult = {
groupId: 'sap.cds:service:customer:undefined.testServiceName',
groupTypeId: 'sap.cds:service',
title: 'test Service Title'
};
expect(templates.fCreateGroupsTemplateForService(testSrv, linkedModel, testGroupIds)).toEqual(testResult);
});

it('should return null when groupIds has groupId', () => {
const testSrv = 'testServiceName';
global.namespace = 'customer';
const testGroupIds = new Set(['sap.cds:service:customer:undefined.testServiceName']);
const testResult = null;
expect(templates.fCreateGroupsTemplateForService(testSrv, linkedModel, testGroupIds)).toEqual(testResult);
});
});

describe('fCreateGroupsTemplateForEvent', () => {
it('should return default value when groupIds do not have groupId', () => {
const tesEvent = 'testEventName';
global.namespace = 'customer';
const testGroupIds = new Set();
const testResult = {
groupId: 'sap.cds:service:customer:undefined.testEventName',
groupTypeId: 'sap.cds:service',
// TODO: space because of service name missing
title: ' Service Title'
};
expect(templates.fCreateGroupsTemplateForEvent(tesEvent, linkedModel, testGroupIds)).toEqual(testResult);
});

it('should return null when groupIds has groupId', () => {
const tesEvent = 'testEventName';
global.namespace = 'customer';
const testGroupIds = new Set(['sap.cds:service:customer:undefined.testEventName']);
const testResult = null;
expect(templates.fCreateGroupsTemplateForEvent(tesEvent, linkedModel, testGroupIds)).toEqual(testResult);
});
});

describe('fCreateAPIResourceTemplate', () => {
it('should create API resource template correctly', () => {
const srv = 'MyService';
const srvDefinition = linkedModel
global.namespace = 'customer';
const packageIds = ['package1'];

const testResult = [
{
ordId: 'customer:apiResource:undefined.MyService:v1',
title: 'The service is for MyService',
shortDescription: 'Here we have the shortDescription for MyService',
description: 'Here we have the description for MyService',
version: '1.0.0',
visibility: 'public',
partOfPackage: undefined,
partOfGroups: ['sap.cds:service:customer:undefined.MyService'],
releaseStatus: 'active',
apiProtocol: 'odata-v4',
resourceDefinitions: [
{
type: 'openapi-v3',
mediaType: 'application/json',
url: '/.well-known/open-resource-discovery/v1/api-metadata/MyService.oas3.json',
accessStrategies: [{ type: 'open' }],
},
{
type: 'edmx',
mediaType: 'application/xml',
url: '/.well-known/open-resource-discovery/v1/api-metadata/MyService.edmx',
accessStrategies: [{ type: 'open' }],
},
],
entryPoints: ['/odata/v4/my'],
extensible: {
supported: 'no',
},
entityTypeMappings: [{ entityTypeTargets: undefined }],
},
];
expect(templates.fCreateAPIResourceTemplate(srv, srvDefinition, global, packageIds)).toEqual(testResult);
});
});

describe('fCreateEventResourceTemplate', () => {
it('should create API resource template correctly', () => {
const srv = 'MyService';
const srvDefinition = linkedModel
console.log('srvDefinition:', srvDefinition._service);
global.namespace = 'customer';
global.appName = 'testAppName';
const packageIds = ['package1'];

// TODO: temporary solution, fix it
srvDefinition._service = {
name: 'MyService',
};

const testResult = {
ordId: 'customer:eventResource:undefined.MyService:v1',
title: 'ODM testAppName Events',
shortDescription: 'Example ODM Event',
description: 'This is an example event catalog that contains only a partial ODM testAppName V1 event',
version: '1.0.0',
releaseStatus: 'beta',
partOfPackage: undefined,
partOfGroups: ['sap.cds:service:customer:undefined.MyService'],
visibility: 'public',
resourceDefinitions: [
{
type: 'asyncapi-v2',
mediaType: 'application/json',
url: '/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json',
accessStrategies: [
{
type: 'open',
},
],
},
],
extensible: { supported: 'no' },
};
expect(templates.fCreateEventResourceTemplate(srv, srvDefinition, global, packageIds)).toEqual(testResult);
});
});
});
Loading