From ff4afd71dd11c44d958ef1f6117d053568271976 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:05:14 +0200 Subject: [PATCH] Preparations --- .../lib/dtos/create-account-balance.dto.ts | 4 +-- libs/common/src/lib/helper.spec.ts | 31 ++++++++++++++++ libs/common/src/lib/helper.ts | 6 ++++ .../is-not-in-the-future.spec.ts | 35 ------------------- .../is-not-in-the-future.ts | 26 -------------- .../is-valid-account-balance-date.spec.ts | 11 ++++++ .../is-valid-account-balance-date.ts | 22 ++++++++++++ 7 files changed, 72 insertions(+), 63 deletions(-) delete mode 100644 libs/common/src/lib/validator-constraints/is-not-in-the-future.spec.ts delete mode 100644 libs/common/src/lib/validator-constraints/is-not-in-the-future.ts create mode 100644 libs/common/src/lib/validator-constraints/is-valid-account-balance-date.spec.ts create mode 100644 libs/common/src/lib/validator-constraints/is-valid-account-balance-date.ts diff --git a/libs/common/src/lib/dtos/create-account-balance.dto.ts b/libs/common/src/lib/dtos/create-account-balance.dto.ts index 39d06f0205..5ca4b85479 100644 --- a/libs/common/src/lib/dtos/create-account-balance.dto.ts +++ b/libs/common/src/lib/dtos/create-account-balance.dto.ts @@ -1,4 +1,4 @@ -import { IsNotInTheFutureConstraint } from '@ghostfolio/common/validator-constraints/is-not-in-the-future'; +import { IsValidAccountBalanceDateConstraint } from '@ghostfolio/common/validator-constraints/is-valid-account-balance-date'; import { IsISO8601, IsNumber, IsUUID, Validate } from 'class-validator'; @@ -10,6 +10,6 @@ export class CreateAccountBalanceDto { balance: number; @IsISO8601() - @Validate(IsNotInTheFutureConstraint) + @Validate(IsValidAccountBalanceDateConstraint) date: string; } diff --git a/libs/common/src/lib/helper.spec.ts b/libs/common/src/lib/helper.spec.ts index cc67c5c141..b4ef699e5b 100644 --- a/libs/common/src/lib/helper.spec.ts +++ b/libs/common/src/lib/helper.spec.ts @@ -18,6 +18,7 @@ import { isCurrencySymbol, isDarkColorScheme, isSplitRatio, + isValidAccountBalanceDate, isValidCurrencyCode, isValidCustomAssetProfileSymbol, isValidDateAfter1970, @@ -558,6 +559,36 @@ describe('Helper', () => { }); }); + describe('Is valid account balance date', () => { + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date('2026-09-03T23:30:00.000Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('Accepts a date in the past', () => { + expect(isValidAccountBalanceDate('2026-09-02')).toBe(true); + }); + + it('Accepts the current date', () => { + expect(isValidAccountBalanceDate('2026-09-03')).toBe(true); + }); + + it('Accepts the current date of a user in a time zone ahead of the instance', () => { + expect(isValidAccountBalanceDate('2026-09-04')).toBe(true); + }); + + it('Refuses a date in the future', () => { + expect(isValidAccountBalanceDate('2026-09-05')).toBe(false); + }); + + it('Refuses a date which is not valid', () => { + expect(isValidAccountBalanceDate('2026-02-30')).toBe(false); + }); + }); + describe('Is valid currency code', () => { it('Currency code in lower case', () => { expect(isValidCurrencyCode('usd')).toEqual(false); diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 174a2ec167..242cf4ef57 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -735,6 +735,12 @@ export function isUserSettingOfAuthenticatedUser(aKey: string) { ); } +export function isValidAccountBalanceDate(aDate: string) { + const date = parseISO(aDate); + + return isValid(date) && !isAccountBalanceInFuture({ date: resetHours(date) }); +} + export function isValidCurrencyCode(aCurrency: string) { if (!aCurrency) { return false; diff --git a/libs/common/src/lib/validator-constraints/is-not-in-the-future.spec.ts b/libs/common/src/lib/validator-constraints/is-not-in-the-future.spec.ts deleted file mode 100644 index 4ab85b2942..0000000000 --- a/libs/common/src/lib/validator-constraints/is-not-in-the-future.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { IsNotInTheFutureConstraint } from './is-not-in-the-future'; - -describe('IsNotInTheFutureConstraint', () => { - const constraint = new IsNotInTheFutureConstraint(); - - beforeAll(() => { - jest.useFakeTimers().setSystemTime(new Date('2026-09-03T23:30:00.000Z')); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - it('Accepts a date in the past', () => { - expect(constraint.validate('2026-09-02')).toBe(true); - }); - - it('Accepts the current date', () => { - expect(constraint.validate('2026-09-03')).toBe(true); - }); - - it('Accepts the current date of a user in a time zone ahead of the instance', () => { - expect(constraint.validate('2026-09-04')).toBe(true); - }); - - it('Refuses a date in the future', () => { - expect(constraint.validate('2026-09-05')).toBe(false); - }); - - it('Leaves a value which is not a string to IsISO8601', () => { - expect(constraint.validate(undefined)).toBe(true); - expect(constraint.validate(null)).toBe(true); - expect(constraint.validate(123)).toBe(true); - }); -}); diff --git a/libs/common/src/lib/validator-constraints/is-not-in-the-future.ts b/libs/common/src/lib/validator-constraints/is-not-in-the-future.ts deleted file mode 100644 index 0ea6685e45..0000000000 --- a/libs/common/src/lib/validator-constraints/is-not-in-the-future.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - isAccountBalanceInFuture, - resetHours -} from '@ghostfolio/common/helper'; - -import { - ValidatorConstraint, - ValidatorConstraintInterface -} from 'class-validator'; -import { parseISO } from 'date-fns'; - -@ValidatorConstraint({ name: 'isNotInTheFuture' }) -export class IsNotInTheFutureConstraint implements ValidatorConstraintInterface { - public defaultMessage() { - return '$property must not be a date in the future'; - } - - public validate(aDate: unknown) { - // IsISO8601 refuses a value which is not a string - if (typeof aDate !== 'string') { - return true; - } - - return !isAccountBalanceInFuture({ date: resetHours(parseISO(aDate)) }); - } -} diff --git a/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.spec.ts b/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.spec.ts new file mode 100644 index 0000000000..39199bcb6d --- /dev/null +++ b/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.spec.ts @@ -0,0 +1,11 @@ +import { IsValidAccountBalanceDateConstraint } from './is-valid-account-balance-date'; + +describe('IsValidAccountBalanceDateConstraint', () => { + const constraint = new IsValidAccountBalanceDateConstraint(); + + it('Leaves a value which is not a string to IsISO8601', () => { + expect(constraint.validate(undefined)).toBe(true); + expect(constraint.validate(null)).toBe(true); + expect(constraint.validate(123)).toBe(true); + }); +}); diff --git a/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.ts b/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.ts new file mode 100644 index 0000000000..2ea61570ff --- /dev/null +++ b/libs/common/src/lib/validator-constraints/is-valid-account-balance-date.ts @@ -0,0 +1,22 @@ +import { isValidAccountBalanceDate } from '@ghostfolio/common/helper'; + +import { + ValidatorConstraint, + ValidatorConstraintInterface +} from 'class-validator'; + +@ValidatorConstraint({ name: 'isValidAccountBalanceDate' }) +export class IsValidAccountBalanceDateConstraint implements ValidatorConstraintInterface { + public defaultMessage() { + return '$property must be a valid date which is not in the future'; + } + + public validate(aDate: unknown) { + // IsISO8601 refuses a value which is not a string + if (typeof aDate !== 'string') { + return true; + } + + return isValidAccountBalanceDate(aDate); + } +}