Skip to content
This repository has been archived by the owner on Jan 29, 2022. It is now read-only.

[#586] Fix wrong dates due to offset of time #127

Merged
merged 4 commits into from
Dec 12, 2016

Conversation

georgiana-b
Copy link
Contributor

opentrials/opentrials#686

After a charming journey among the horrors of JS date handling, this is the best thing I could come up with.
Imho expressed in this node-pg-types issue, the Date-only fields should not have any time information attached to them. However, seems that most JS developers (including the ones at node-elasticsearch) don't share my opinion so I had to settle with this for consistency.

@vitorbaptista vitorbaptista temporarily deployed to ot-api-next-pr-127 December 4, 2016 19:32 Inactive
@coveralls
Copy link

Coverage Status

Coverage decreased (-1.1%) to 90.892% when pulling 72d6a48 on hotfix/dates-without-offset into 9f168a9 on master.

Copy link
Contributor

@vitorbaptista vitorbaptista left a comment

Choose a reason for hiding this comment

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

This test shouldn't be here written for trials.registration_date, as it's not only related to it. Instead, extract the pgTypes configuration to a separate file (e.g. config/pg_types.js or similar) and write a test for that (e.g. test/config/pg_types.js).

You can write the test by using the pgTypes.getTypeParser() function and unit testing both functions. This would both guarantee that they're set up correctly and that they behaviour is correct.

const pgTypes = require('pg').types;

function parseDate(val) {
return new Date(Date.parse(val));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible for parseDate() to be called with null values as well? If so, it needs to handle this case, similarly to parseDateArray

return p.parse();
}
pgTypes.setTypeParser(1082, parseDate);
pgTypes.setTypeParser(1182, parseDateArray);
Copy link
Contributor

Choose a reason for hiding this comment

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

When you move this to a separate file, please remove these magic numbers 1082 and 1182, using a constant like DATE_OID, so it gets more readable.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.3%) to 92.365% when pulling de9a25f on hotfix/dates-without-offset into ae7d4ce on master.

Copy link
Contributor

@vitorbaptista vitorbaptista left a comment

Choose a reason for hiding this comment

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

Hi @georgiana-b, please check my comments inline. Also, instead of getting the methods on each test (i.e. pgTypes.getTypeParser(DATE_OID), you could get them at the beginning of the test, next to where you define the DATE_OID constant, like const parseDate = pgTypes.getTypeParser(DATE_OID); to make the code a bit more DRY.


it('returns null when value is null', () => {
let testDate = null;
should.equal(pgTypes.getTypeParser(DATE_OID)(testDate), null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Use should(pgTypes.getTypeParser(DATE_OID)(testDate)).equal(null) instead, to follow the pattern of the rest of the tests


it('raises when value is invalid', () => {
let testDate = 'not a date';
pgTypes.getTypeParser(DATE_OID).bind(null, testDate).should.throw(/^Invalid Date/);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are you using .bind() here instead of simply calling pgTypes.getTypeParser(DATE_OID)(testDate)? Did you have issues with the latter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used .bind() here because if I just do pgTypes.getTypeParser(DATE_OID)(testDate).should.throw(/^Invalid Date/) the tests fail with this error:

  1) parseDate raises when value is invalid:
     Error: Invalid Date
      at parseDate (config/pg_types.js:9:650)
      at Context.it (test/config/pg_types.js:25:36)

let testArray = `{${testDate}}`;
let expectedDate = new Date(testDate).toISOString();
let resultedArray = pgTypes.getTypeParser(DATE_ARRAY_OID)(testArray);
resultedArray.map((date) => date.toISOString()).should.containEql(expectedDate);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of testing that it contains the date, you can use deepEquals, to make sure the entire array is what you expect, not that it has a single element. Like:

should(resultedArray).deepEqual([
  expectedDate,
])

I don't think you need to convert toISOString() either.


it('returns null when value is null', () => {
let testArray = null;
should.equal(pgTypes.getTypeParser(DATE_ARRAY_OID)(testArray), null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Use should(foo).equal(bar)

it('returns empty array when there are no values', () => {
let testArray = `{}`;
let resultedArray = pgTypes.getTypeParser(DATE_ARRAY_OID)(testArray);
resultedArray.should.be.empty;
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to call the function, like should(resultedArray).be.empty(), otherwise it isn't testing anything


it('raises when one of the values is invalid', () => {
let testArray = `{2016-07-08, null}`;
pgTypes.getTypeParser(DATE_ARRAY_OID).bind(null, testArray).should.throw(/^Invalid Date/);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this is really throwing an exception. The should.throw() takes a block, like:

should(() => pgTypes.getTypeParser(DATE_ARRAY_OID)(testArray)).throw(/^Invalid Date/)

Also, from the code, it doesn't look like it'll throw an error here, but simply return an array [Date(2016-07-08), null], which seems like the correct behaviour to me.

if (!value) {
date = null;
} else {
throw new Error('Invalid Date');
Copy link
Contributor

Choose a reason for hiding this comment

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

As this is coming from Postgres, I don't think we need to validate dates here, as Postgres would already have done that for us (if I understand it correctly). Here we just need to deal with null values (and maybe undefined, but we can do a general if (!value) { return null; } to cover both cases)

@coveralls
Copy link

Coverage Status

Coverage increased (+0.3%) to 92.322% when pulling ce60542 on hotfix/dates-without-offset into ae7d4ce on master.

@georgiana-b
Copy link
Contributor Author

@nightsh Please take a look at this and approve if you think it's ok.


const p = pgTypes.arrayParser.create(value, (entry) => {
let parsedEntry = entry;
if (String(entry).toLowerCase() !== String(null)) {
Copy link
Member

Choose a reason for hiding this comment

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

To clarify why is this here: null value only enters the function as the string 'null'. This condition works with either version of it.

Copy link
Member

@nightsh nightsh left a comment

Choose a reason for hiding this comment

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

Left some comments, please check them out before merging (and push changes if you see fit).

});

it('returns empty array when there are no values', () => {
let testArray = `{}`;
Copy link
Member

Choose a reason for hiding this comment

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

I would've named this testPgArray, just to avoid any confusions with "regular" arrays delimited by square brackets.


});

describe('parseDateArray', () => {
Copy link
Member

Choose a reason for hiding this comment

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

it('calls parseDate() on single array items')

For when you pass it an array of a single item. So we'd avoid creating an interval (unless we really want to).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

An array of dates does not represent an interval in this case. It's just a normal array with date values.

return parsedEntry;
});

return p.parse();
Copy link
Member

Choose a reason for hiding this comment

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

A bit unclear what p means, unless you go back up to see it's a parser. Might be worth renaming it to parser.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.3%) to 92.322% when pulling 9a4fab7 on hotfix/dates-without-offset into ae7d4ce on master.

@georgiana-b georgiana-b merged commit bef5c75 into master Dec 12, 2016
@georgiana-b georgiana-b deleted the hotfix/dates-without-offset branch December 12, 2016 15:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants