Skip to content

Commit

Permalink
fix(validate): edge case, Date object w/ invalid raw field; add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gadicc committed Feb 3, 2021
1 parent c012cc4 commit a98d306
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 4 deletions.
149 changes: 149 additions & 0 deletions src/lib/validateAndCoerceTypes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import validateAndCoerceTypes from './validateAndCoerceTypes';
import { InvalidOptionsError, FailedYahooValidationError } from './errors';

const QUERY_RESULT_SCHEMA_KEY = "#/definitions/QuoteSummaryResult";

const priceResult = {
price: {
maxAge: 1,
preMarketSource: 'FREE_REALTIME',
// 0.006599537 from RawNumberObj
postMarketChangePercent: { raw: 0.006599537, fmt: "6.5%" }, //
postMarketChange: 5.76001,
// new Date("2021-02-03T00:59:57.000Z"),
postMarketTime: 1612313997, // <---------------- Date: epoch
postMarketPrice: 878.55,
postMarketSource: 'DELAYED',
regularMarketChangePercent: 0.039270766,
regularMarketChange: 32.97998,
// new Date("2021-02-02T21:00:01.000Z")
regularMarketTime: '2021-02-02T21:00:01.000Z', // Date: ISODate
priceHint: 2, // test this as regular number
regularMarketPrice: 872.79,
regularMarketDayHigh: 880.5,
regularMarketDayLow: 842.2006,
regularMarketVolume: 24346213,
regularMarketPreviousClose: 839.81,
regularMarketSource: 'FREE_REALTIME',
regularMarketOpen: 844.68,
exchange: 'NMS',
exchangeName: 'NasdaqGS',
exchangeDataDelayedBy: 0,
marketState: 'PREPRE',
quoteType: 'EQUITY',
symbol: 'TSLA',
underlyingSymbol: null,
shortName: 'Tesla, Inc.',
longName: 'Tesla, Inc.',
currency: 'USD',
quoteSourceName: 'Delayed Quote',
currencySymbol: '$',
fromCurrency: null,
toCurrency: null,
lastMarket: null,
marketCap: 827318468608
}
};

describe('validateAndCoerceTypes', () => {

describe('coersion', () => {

describe('numbers', () => {

it('passes regular numbers', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY);
expect(result.price.priceHint).toBe(2);
});

it('corerces rawNumberObjs', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
result.price.postMarketChangePercent = { raw: 0.006599537, fmt: "6.5%" }
validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY);
expect(result.price.postMarketChangePercent).toBe(0.006599537);
});

});

describe('dates', () => {

it('coerces rawNumberObjs', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
// @ts-ignore
result.price.regularMarketTime = { raw: 1612313997 };

validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY);
// @ts-ignore
expect(result.price.regularMarketTime.getTime())
.toBe(1612313997 * 1000);
});

it('coerces epochs', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY);
// @ts-ignore
// @ts-ignore
expect(result.price.regularMarketTime.getTime())
.toBe(new Date(priceResult.price.regularMarketTime).getTime());
});

it('coerces strings', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY);
// @ts-ignore
expect(result.price.regularMarketTime.getTime())
.toBe(new Date(priceResult.price.regularMarketTime).getTime());
});

});

describe('failures', () => {

it('fails on error', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
// @ts-ignore
result.price.regularMarketTime = { weird: 1612313997 };
console.log(result);

expect(
() => validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY)
).toThrow("Failed Yahoo Schema validation");

// @ts-ignore
const error = validateAndCoerceTypes.errors[0];
expect(error).toBeDefined();
expect(error.keyword).toBe('yahooFinanceType');
expect(error.message).toBe('No matching type');
expect(error.params).toBeDefined();
expect(error.params.schema).toBe('date');
expect(error.params.data).toBe(result.price.regularMarketTime);
expect(error.dataPath).toBe('/price/regularMarketTime');
expect(error.schemaPath).toBe('#/definitions/Price/properties/regularMarketTime/yahooFinanceType');
});

it('fails Date objects (catch bad tests)', () => {
const result = Object.assign({}, priceResult);
result.price = Object.assign({}, result.price);
// @ts-ignore
result.price.postMarketTime = new Date();
expect(
() => validateAndCoerceTypes(result, QUERY_RESULT_SCHEMA_KEY)
).toThrow("Failed Yahoo Schema validation");

// @ts-ignore
const error = validateAndCoerceTypes.errors[0];
expect(error.message).toBe('Got a real Date object??? Bad test?');
});

});

});

});
23 changes: 19 additions & 4 deletions src/lib/validateAndCoerceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ajv.addKeyword('yahooFinanceType', {
validate.errors = [];

if (schema === 'number') {

if (typeof data === 'number')
return true;

Expand All @@ -37,20 +38,31 @@ ajv.addKeyword('yahooFinanceType', {
if (typeof data.raw === 'number')
return set(data.raw);
}
}

if (schema === 'date') {
} else if (schema === 'date') {

if (data instanceof Date) {
/* @ts-ignore */
validate.errors.push({
keyword: "yahooFinanceType",
message: "Got a real Date object??? Bad test?",
params: { schema, data }
});
return false;
}
if (typeof data === 'number')
return set(new Date(data * 1000));
if (data === null)
return null;
if (typeof data === 'object')
return set(null);
if (typeof data === 'object' && typeof data.raw === 'number')
return set(new Date(data.raw * 1000));
if (typeof data === 'string') {
if (data.match(/^\d{4,4}-\d{2,2}-\d{2,2}$/) ||
data.match(/^\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}:\d{2,2}:\d{2,2}\.\d{3,3}Z$/))
return set(new Date(data));
data//?
}

}

/* @ts-ignore */
Expand Down Expand Up @@ -78,6 +90,9 @@ function validate(object: object, key: string, module?: string): void {
const valid = validator(object);
if (valid) return;

// @ts-ignore
validate.errors = validator.errors;

if (!module) {

const title = encodeURIComponent("Failed validation: " + key);
Expand Down

0 comments on commit a98d306

Please sign in to comment.