Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ testRule('xgen-IPA-104-resource-has-GET', [
patch: {},
delete: {},
},
'/custom/{exampleId}:method': {
post: {},
},
'/custom:method': {
post: {},
},
Expand Down Expand Up @@ -78,6 +81,9 @@ testRule('xgen-IPA-104-resource-has-GET', [
patch: {},
delete: {},
},
'/custom/{exampleId}:method': {
post: {},
},
'/custom:method': {
post: {},
},
Expand Down
88 changes: 88 additions & 0 deletions tools/spectral/ipa/__tests__/utils/resourceEvaluation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { describe, expect, it } from '@jest/globals';
import {
getResourcePaths,
isSingletonResource,
isStandardResource,
} from '../../rulesets/functions/utils/resourceEvaluation';

const standardResourcePaths = ['/standard', '/standard/{id}'];

const nestedStandardResourcePaths = ['/standard/{exampleId}/nested', '/standard/{exampleId}/nested/{exampleId}'];

const standardResourceWithCustomPaths = ['/customStandard', '/customStandard/{id}', '/customStandard:method'];

const singletonResourcePaths = ['/singleton'];

const singletonResourceWithCustomPaths = ['/customSingleton', '/customSingleton:method'];

const nestedSingletonResourcePaths = ['/standard/{exampleId}/nestedSingleton'];

describe('tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js', () => {
describe('getResourcePaths', () => {
it('returns the paths for a resource based on parent path', () => {
const allPaths = standardResourcePaths.concat(
nestedStandardResourcePaths,
standardResourceWithCustomPaths,
singletonResourcePaths,
singletonResourceWithCustomPaths,
nestedSingletonResourcePaths
);
expect(getResourcePaths('/standard', allPaths)).toEqual(standardResourcePaths);
expect(getResourcePaths('/standard/{exampleId}/nested', allPaths)).toEqual(nestedStandardResourcePaths);
expect(getResourcePaths('/customStandard', allPaths)).toEqual(standardResourceWithCustomPaths);
expect(getResourcePaths('/singleton', allPaths)).toEqual(singletonResourcePaths);
expect(getResourcePaths('/customSingleton', allPaths)).toEqual(singletonResourceWithCustomPaths);
expect(getResourcePaths('/standard/{exampleId}/nestedSingleton', allPaths)).toEqual(nestedSingletonResourcePaths);
});
});
describe('isStandardResource', () => {
it('returns true for a standard resource', () => {
expect(isStandardResource(standardResourcePaths)).toBe(true);
});

it('returns true for a standard resource with custom methods', () => {
expect(isStandardResource(standardResourceWithCustomPaths)).toBe(true);
});

it('returns true for a nested standard resource', () => {
expect(isStandardResource(nestedStandardResourcePaths)).toBe(true);
});

it('returns false for a singleton resource', () => {
expect(isStandardResource(singletonResourcePaths)).toBe(false);
});

it('returns false for a singleton resource with custom methods', () => {
expect(isStandardResource(singletonResourceWithCustomPaths)).toBe(false);
});

it('returns false for a nested singleton resource', () => {
expect(isStandardResource(nestedSingletonResourcePaths)).toBe(false);
});
});
describe('isSingletonResource', () => {
it('returns true for a singleton resource', () => {
expect(isSingletonResource(singletonResourcePaths)).toBe(true);
});

it('returns true for a singleton resource with custom methods', () => {
expect(isSingletonResource(singletonResourceWithCustomPaths)).toBe(true);
});

it('returns true for a nested singleton resource', () => {
expect(isSingletonResource(nestedSingletonResourcePaths)).toBe(true);
});

it('returns false for a standard resource', () => {
expect(isSingletonResource(standardResourcePaths)).toBe(false);
});

it('returns false for a standard resource with custom methods', () => {
expect(isSingletonResource(standardResourceWithCustomPaths)).toBe(false);
});

it('returns false for a nested standard resource', () => {
expect(isSingletonResource(nestedStandardResourcePaths)).toBe(false);
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

can we unit test these, and if not can we add some examples to things like isSingletonResource or isStandardResource it's a bit hard to follow the intention of the method and some examples either in text or as actual tests would be of great help in the future if we need to debug or change those functions

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, makes sense, I'll add unit tests for them as well (getResourcePaths, isStandardResource and isSingletonResource as they are probably the tricky ones)

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export function isCustomMethod(path) {

/**
* Checks if a resource is a singleton resource ({@link https://docs.devprod.prod.corp.mongodb.com/ipa/113 IPA-113}) based on the paths for the
* resource. The resource may have custom methods.
* resource. The resource may have custom methods. Use {@link getResourcePaths} to get all paths of a resource.
*
* @param resourcePaths all paths for the resource as an array of strings
* @param resourcePaths all paths for the resource to be evaluated as an array of strings
* @returns {boolean}
*/
export function isSingletonResource(resourcePaths) {
Expand All @@ -23,9 +23,9 @@ export function isSingletonResource(resourcePaths) {

/**
* Checks if a resource is a standard resource ({@link https://docs.devprod.prod.corp.mongodb.com/ipa/103 IPA-103}) based on the paths for the
* resource. The resource may have custom methods.
* resource. The resource may have custom methods. Use {@link getResourcePaths} to get all paths of a resource.
*
* @param resourcePaths all paths for the resource as an array of strings
* @param resourcePaths all paths for the resource to be evaluated as an array of strings
* @returns {boolean}
*/
export function isStandardResource(resourcePaths) {
Expand Down Expand Up @@ -58,6 +58,9 @@ export function hasGetMethod(pathObject) {
*/
export function getResourcePaths(parent, allPaths) {
const childPathPattern = new RegExp(`^${parent}/{[a-zA-Z]+}$`);
const customMethodPattern = new RegExp(`^${parent}/{[a-zA-Z]+}:+[a-zA-Z]+$`);
return allPaths.filter((p) => parent === p || childPathPattern.test(p) || customMethodPattern.test(p));
const customChildMethodPattern = new RegExp(`^${parent}/{[a-zA-Z]+}:+[a-zA-Z]+$`);
const customMethodPattern = new RegExp(`^${parent}:+[a-zA-Z]+$`);
return allPaths.filter(
(p) => parent === p || childPathPattern.test(p) || customMethodPattern.test(p) || customChildMethodPattern.test(p)
Comment on lines +61 to +64
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The added tests revealed that we were missing a custom case, as it can be either /path/{id}:method or /path:method

);
}
Loading