Skip to content

Commit

Permalink
chore: writing fastcheck tests for parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
aryanjassal committed Oct 15, 2024
1 parent aac9445 commit c6ceead
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 28 deletions.
24 changes: 10 additions & 14 deletions src/utils/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import * as gestaltsUtils from 'polykey/dist/gestalts/utils';
import * as networkUtils from 'polykey/dist/network/utils';
import * as nodesUtils from 'polykey/dist/nodes/utils';

const vaultNameRegex = /^([\w-.]+)$/;
const secretPathRegex = /^([^\0\\=]+)?$/;
const vaultNameRegex = /^([\w\-.]+)$/;
const secretPathRegex = /^([\w\/;,.]+)?$/;
const secretPathValueRegex = /^([a-zA-Z_][\w]+)?$/;
const environmentVariableRegex = /^([a-zA-Z_]+[a-zA-Z0-9_]*)?$/;

Expand Down Expand Up @@ -95,24 +95,20 @@ function parseSecretPath(secretPath: string): [string, string?, string?] {
// The colon character `:` is prohibited in vaultName, so it's first occurence
// means that this is the delimiter between vaultName and secretPath.
const colonIndex = splitSecretPath.indexOf(':');
// If no colon exists, treat entire string as vault name
if (colonIndex === -1) {
return [parseVaultName(splitSecretPath), undefined, value];
}
// Calculate contents before the `=` separator
const vaultNamePart =
colonIndex === -1
? splitSecretPath
: splitSecretPath.substring(0, colonIndex);
const secretPathPart =
colonIndex === -1 ? undefined : splitSecretPath.substring(colonIndex + 1);

if (secretPathPart && !secretPathRegex.test(secretPathPart)) {
const vaultNamePart = splitSecretPath.substring(0, colonIndex);
const secretPathPart = splitSecretPath.substring(colonIndex + 1);
if (secretPathPart != null && !secretPathRegex.test(secretPathPart)) {
throw new commander.InvalidArgumentError(
`${secretPath} is not of the format <vaultName>[:<secretPath>][=<value>]`,
);
}
const parsedVaultName = parseVaultName(vaultNamePart);
const parsedSecretPath =
secretPathPart == null
? undefined
: secretPathPart.match(secretPathRegex)![1];
const parsedSecretPath = secretPathPart.match(secretPathRegex)?.[1];
return [parsedVaultName, parsedSecretPath, value];
}

Expand Down
80 changes: 66 additions & 14 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import type { Host, Port } from 'polykey/dist/network/types';
import ErrorPolykey from 'polykey/dist/ErrorPolykey';
import { test } from '@fast-check/jest';
import * as ids from 'polykey/dist/ids';
import * as nodesUtils from 'polykey/dist/nodes/utils';
import * as polykeyErrors from 'polykey/dist/errors';
import * as fc from 'fast-check';
import * as binUtils from '@/utils/utils';
import * as binParsers from '@/utils/parsers';
import path from 'path';

const nonPrintableCharArb = fc
.oneof(
fc.integer({ min: 0, max: 0x1f }),
fc.integer({ min: 0x7f, max: 0x9f }),
)
.map((code) => String.fromCharCode(code));
describe('outputFormatters', () => {
const nonPrintableCharArb = fc
.oneof(
fc.integer({ min: 0, max: 0x1f }),
fc.integer({ min: 0x7f, max: 0x9f }),
)
.map((code) => String.fromCharCode(code));

const stringWithNonPrintableCharsArb = fc.stringOf(
fc.oneof(fc.char(), nonPrintableCharArb),
);
const stringWithNonPrintableCharsArb = fc.stringOf(
fc.oneof(fc.char(), nonPrintableCharArb),
);

describe('bin/utils', () => {
test('list in human and json format', () => {
// List
expect(
Expand Down Expand Up @@ -164,7 +167,7 @@ describe('bin/utils', () => {
' key9\tvalue\n',
);
});
test('outputFormatter should encode non-printable characters within a dict', () => {
test('should encode non-printable characters within a dict', () => {
fc.assert(
fc.property(
stringWithNonPrintableCharsArb,
Expand All @@ -174,15 +177,12 @@ describe('bin/utils', () => {
type: 'dict',
data: { [key]: value },
});

const expectedKey = binUtils.encodeEscapedWrapped(key);

// Construct the expected output
let expectedValue = value;
expectedValue = binUtils.encodeEscapedWrapped(expectedValue);
expectedValue = expectedValue.replace(/(?:\r\n|\n)$/, '');
expectedValue = expectedValue.replace(/(\r\n|\n)/g, '$1\t');

const maxKeyLength = Math.max(
...Object.keys({ [key]: value }).map((k) => k.length),
);
Expand Down Expand Up @@ -342,3 +342,55 @@ describe('bin/utils', () => {
);
});
});

describe('parsers', () => {
const vaultNameArb = fc.stringOf(
fc.char().filter((c) => binParsers.vaultNameRegex.test(c)),
{ minLength: 1, maxLength: 100 },
);
const singleSecretPathArb = fc.stringOf(
fc.char().filter((c) => binParsers.secretPathRegex.test(c)),
{ minLength: 1, maxLength: 25 },
);
const secretPathArb = fc
.array(singleSecretPathArb, { minLength: 1, maxLength: 5 })
.map((segments) => path.join(...segments));
const valueFirstCharArb = fc.char().filter((c) => /^[a-zA-Z_]$/.test(c));
const valueRestCharArb = fc.stringOf(
fc.char().filter((c) => /^[\w]$/.test(c)),
{ minLength: 1, maxLength: 100 },
);
const valueDataArb = fc
.tuple(valueFirstCharArb, valueRestCharArb)
.map((components) => components.join(''));

test.prop([vaultNameArb], { numRuns: 100 })(
'should parse vault name',
async (vaultName) => {
expect(binParsers.parseVaultName(vaultName)).toEqual(vaultName);
},
);
test.prop([vaultNameArb], { numRuns: 10 })(
'should parse secret path with only vault name',
async (vaultName) => {
const result = [vaultName, undefined, undefined];
expect(binParsers.parseSecretPath(vaultName)).toEqual(result);
},
);
test.prop([vaultNameArb, secretPathArb], { numRuns: 100 })(
'should parse full secret path with vault name',
async (vaultName, secretPath) => {
const query = `${vaultName}:${secretPath}`;
const result = [vaultName, secretPath, undefined];
expect(binParsers.parseSecretPath(query)).toEqual(result);
},
);
test.prop([vaultNameArb, secretPathArb, valueDataArb], { numRuns: 100 })(
'should parse full secret path with vault name and value',
async (vaultName, secretPath, valueData) => {
const query = `${vaultName}:${secretPath}=${valueData}`;
const result = [vaultName, secretPath, valueData];
expect(binParsers.parseSecretPathValue(query)).toEqual(result);
},
);
});

0 comments on commit c6ceead

Please sign in to comment.