Skip to content

Commit

Permalink
fix: false-positive results in some rare cases with circular types
Browse files Browse the repository at this point in the history
  • Loading branch information
ncpa0cpl committed Mar 8, 2023
1 parent a93f974 commit 605884d
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 6 deletions.
56 changes: 56 additions & 0 deletions __tests__/validation-algorithms/create-validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3096,6 +3096,62 @@ describe("createValidator", () => {
expect(() => validate(data2)).not.toThrowError();
expect(validate(data2)).toEqual(true);
});

it("multiple neighboring circular types should correctly validate", () => {
const typeDef = DataType.RecordOf({
a: DataType.Circular((self) =>
DataType.RecordOf({
tag: DataType.String,
children: DataType.ArrayOf(self),
})
),
b: DataType.Circular((self) =>
DataType.RecordOf({
tag: DataType.Number,
})
),
});

const validate = createValidator(typeDef);

const a = {
tag: "div",
children: [],
};

const data = {
a: a,
b: a,
};

expect(validate(data)).toEqual(false);

const typeDef2 = DataType.RecordOf({
a: DataType.Circular((self) =>
DataType.RecordOf({
tag: DataType.String,
children: DataType.ArrayOf(self),
notSelf: DataType.Circular((s) =>
DataType.RecordOf({ foo: DataType.String })
),
})
),
});

const validate2 = createValidator(typeDef2);

const data2 = {
a: {
tag: "div",
children: [],
notSelf: {},
},
};

data2.a.notSelf = data2.a;

expect(validate2(data2)).toEqual(false);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { AnyDataType } from "@DataTypes/types";

/**
* Collection of values that have been ran through the validator
* already, and were expected to possibly contain circular
Expand All @@ -10,4 +12,21 @@
*
* This Set should be cleared after each validation pass.
*/
export const validatedCircularValues = new Set();
export const validatedCircularValues = new Map<AnyDataType, Set<any>>();

export const wasCircValidated = (type: AnyDataType, data: unknown) => {
let set = validatedCircularValues.get(type);

if (!set) {
set = new Set([data]);
validatedCircularValues.set(type, set);
return false;
}

if (set.has(data)) {
return true;
}

set.add(data);
return false;
};
8 changes: 3 additions & 5 deletions src/validation-algorithms/validators/validate-circular.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type { Circular, CircularRef } from "@DataTypes/data-types";
import type { AnyDataType } from "@DataTypes/types";
import type { Path } from "@Validation/path";
import { validatedCircularValues } from "@Validation/validators/helper-validated-circ-values";
import { wasCircValidated } from "@Validation/validators/helper-validated-circ-values";
import { validatorsLookupMap } from "@Validation/validators/validate-type";

export const validateCircular = (path: Path, type: Circular, data: unknown) => {
if (validatedCircularValues.has(data)) {
if (wasCircValidated(type, data)) {
return;
}
validatedCircularValues.add(data);

const childType = type.type as AnyDataType;
return validatorsLookupMap.get(childType.kind)!(path, childType, data);
Expand All @@ -19,10 +18,9 @@ export const validateCircularRef = (
type: CircularRef,
data: unknown
) => {
if (validatedCircularValues.has(data)) {
if (wasCircValidated(type._getReferencedType(), data)) {
return;
}
validatedCircularValues.add(data);

const refType = type._getReferencedType() as AnyDataType;
return validatorsLookupMap.get(refType.kind)!(path, refType, data);
Expand Down

0 comments on commit 605884d

Please sign in to comment.