From d79760110965d94b7572fe010cc5a254d4934c2d Mon Sep 17 00:00:00 2001 From: Brijesh Date: Thu, 6 Aug 2026 16:27:26 -0700 Subject: [PATCH 1/5] #535 - align plan version PDF dates --- .../unit/PDFHelper.planVersionDates.spec.js | 21 +++++++++++++++++++ __tests__/unit/dayjs.spec.js | 8 +++++++ src/libs/bcgov-shim.ts | 4 ++++ src/router/controllers_v1/PDFGeneration.ts | 3 ++- src/router/helpers/PDFHelper.ts | 18 ++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 __tests__/unit/PDFHelper.planVersionDates.spec.js diff --git a/__tests__/unit/PDFHelper.planVersionDates.spec.js b/__tests__/unit/PDFHelper.planVersionDates.spec.js new file mode 100644 index 00000000..b17e05b4 --- /dev/null +++ b/__tests__/unit/PDFHelper.planVersionDates.spec.js @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; +import { formatPlanVersionDates } from '../../src/router/helpers/PDFHelper.ts'; + +describe('formatPlanVersionDates', () => { + it('uses the BC timezone for dates near UTC midnight', () => { + const plan = { + originalApproval: { date: '2024-03-16T06:30:00.000Z' }, + amendmentSubmissions: [ + { + createdAt: '2024-03-16T06:30:00.000Z', + approvedAt: '2024-03-16T07:30:00.000Z', + }, + ], + }; + + expect(formatPlanVersionDates(plan)).toEqual({ + originalApproval: { date: '2024-03-15' }, + amendmentSubmissions: [{ createdAt: '2024-03-15', approvedAt: '2024-03-16' }], + }); + }); +}); diff --git a/__tests__/unit/dayjs.spec.js b/__tests__/unit/dayjs.spec.js index 70563c4a..67ff94e1 100644 --- a/__tests__/unit/dayjs.spec.js +++ b/__tests__/unit/dayjs.spec.js @@ -7,8 +7,12 @@ describe('dayjs', () => { dayjs = (await import('dayjs')).default; const isBetween = (await import('dayjs/plugin/isBetween.js')).default; const advancedFormat = (await import('dayjs/plugin/advancedFormat.js')).default; + const utc = (await import('dayjs/plugin/utc.js')).default; + const timezone = (await import('dayjs/plugin/timezone.js')).default; dayjs.extend(isBetween); dayjs.extend(advancedFormat); + dayjs.extend(utc); + dayjs.extend(timezone); }); it('formats dates', () => { @@ -17,6 +21,10 @@ describe('dayjs', () => { expect(d.format('MMMM D')).toBe('March 15'); }); + it('formats plan version dates in the BC timezone', () => { + expect(dayjs.utc('2024-03-16T06:30:00.000Z').tz('America/Vancouver').format('YYYY-MM-DD')).toBe('2024-03-15'); + }); + it('formats with ordinal Do', () => { const d = dayjs('2024-03-15'); expect(d.format('MMMM Do')).toBe('March 15th'); diff --git a/src/libs/bcgov-shim.ts b/src/libs/bcgov-shim.ts index e8f86483..7b224bc7 100644 --- a/src/libs/bcgov-shim.ts +++ b/src/libs/bcgov-shim.ts @@ -2,9 +2,13 @@ import pino from 'pino'; import _dayjs from 'dayjs'; import isBetween from 'dayjs/plugin/isBetween.js'; import advancedFormat from 'dayjs/plugin/advancedFormat.js'; +import utc from 'dayjs/plugin/utc.js'; +import timezone from 'dayjs/plugin/timezone.js'; _dayjs.extend(isBetween); _dayjs.extend(advancedFormat); +_dayjs.extend(utc); +_dayjs.extend(timezone); export const dayjs = _dayjs; diff --git a/src/router/controllers_v1/PDFGeneration.ts b/src/router/controllers_v1/PDFGeneration.ts index b220ecee..f279c0ba 100644 --- a/src/router/controllers_v1/PDFGeneration.ts +++ b/src/router/controllers_v1/PDFGeneration.ts @@ -1,9 +1,10 @@ // @ts-nocheck import Cdogs from '../../libs/cdogs.js'; import Agreement from '../../libs/db2/model/agreement.js'; -import { AdditionalDetailsGenerator } from '../helpers/PDFHelper.js'; +import { AdditionalDetailsGenerator, formatPlanVersionDates } from '../helpers/PDFHelper.js'; export const generatePlanPDF = async (plan) => { + formatPlanVersionDates(plan); const templateFile = Agreement.isGrazingSchedule(plan.agreement) ? './planTemplate_GrazingSchedule.docx' : './planTemplate_HaycuttingSchedule.docx'; diff --git a/src/router/helpers/PDFHelper.ts b/src/router/helpers/PDFHelper.ts index e292a21c..6c888910 100644 --- a/src/router/helpers/PDFHelper.ts +++ b/src/router/helpers/PDFHelper.ts @@ -3,6 +3,24 @@ import { dayjs as moment } from '../../libs/bcgov-shim.js'; import { DAYS_ON_THE_AVERAGE, NOT_PROVIDED } from '../../constants.js'; import Agreement from '../../libs/db2/model/agreement.js'; +export const formatPlanVersionDate = (date) => + date ? moment.utc(date).tz('America/Vancouver').format('YYYY-MM-DD') : date; + +export const formatPlanVersionDates = (plan) => { + if (plan.originalApproval) { + plan.originalApproval.date = formatPlanVersionDate(plan.originalApproval.date); + } + + if (plan.amendmentSubmissions) { + plan.amendmentSubmissions.forEach((submission) => { + submission.createdAt = formatPlanVersionDate(submission.createdAt); + submission.approvedAt = formatPlanVersionDate(submission.approvedAt); + }); + } + + return plan; +}; + const shift = (number, precision) => { const numArray = `${number}`.split('e'); return +`${numArray[0]}e${numArray[1] ? +numArray[1] + precision : precision}`; From 524ec941142c350667bbca730ebe67f2b264609a Mon Sep 17 00:00:00 2001 From: Brijesh Date: Thu, 6 Aug 2026 16:31:34 -0700 Subject: [PATCH 2/5] #535 - protect development database during tests --- package.json | 4 ++-- src/config/index.ts | 7 ++++++- src/libs/db2/migrate.ts | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index bee665cd..22ee19ef 100644 --- a/package.json +++ b/package.json @@ -50,8 +50,8 @@ "build": "rm -rf build && tsc", "build:doc": "./node_modules/.bin/apidoc -i src/ -o doc/api", "db:migrate": "tsx src/libs/db2/migrate.ts", - "test": "cross-env NODE_ENV=unit_test vitest run --coverage", - "test:watch": "cross-env NODE_ENV=unit_test vitest", + "test": "cross-env NODE_ENV=test vitest run --coverage", + "test:watch": "cross-env NODE_ENV=test vitest", "test:lint": "eslint src __tests__ __testHelpers__ __mocks__ scripts", "import": "node build/scripts/import.js", "plan_extension_exemption": "node build/scripts/plan_extension_exemption.js", diff --git a/src/config/index.ts b/src/config/index.ts index f9979bd5..45a4118b 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -7,6 +7,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const env = process.env.NODE_ENV || 'development'; +const isTestEnvironment = env === 'test' || env === 'unit_test'; if (env === 'development') { dotenv.config(); @@ -16,6 +17,10 @@ if (!process.env.SSO_URL) { throw new Error('SSO_URL is not set'); } +if (isTestEnvironment && !process.env.POSTGRESQL_DATABASE_TEST) { + throw new Error('POSTGRESQL_DATABASE_TEST must be set when running tests'); +} + interface DBConfig { user: string; password: string; @@ -63,7 +68,7 @@ const config: Config = { user: process.env.POSTGRESQL_USER || '', password: process.env.POSTGRESQL_PASSWORD || '', database: - env === 'test' && process.env.POSTGRESQL_DATABASE_TEST + isTestEnvironment && process.env.POSTGRESQL_DATABASE_TEST ? process.env.POSTGRESQL_DATABASE_TEST : process.env.POSTGRESQL_DATABASE || '', host: process.env.POSTGRESQL_HOST || '', diff --git a/src/libs/db2/migrate.ts b/src/libs/db2/migrate.ts index e8f82a8a..bf593f57 100644 --- a/src/libs/db2/migrate.ts +++ b/src/libs/db2/migrate.ts @@ -75,7 +75,8 @@ function createDb(): Kysely { host: process.env.POSTGRESQL_HOST, port: parseInt(process.env.POSTGRESQL_PORT || '5432', 10), database: - process.env.NODE_ENV === 'test' && process.env.POSTGRESQL_DATABASE_TEST + (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'unit_test') && + process.env.POSTGRESQL_DATABASE_TEST ? process.env.POSTGRESQL_DATABASE_TEST : process.env.POSTGRESQL_DATABASE, user: process.env.POSTGRESQL_USER, From 0a1778cd4e269be172390faafb37e5884126b25a Mon Sep 17 00:00:00 2001 From: Brijesh Date: Fri, 7 Aug 2026 13:39:12 -0700 Subject: [PATCH 3/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- __tests__/unit/PDFHelper.planVersionDates.spec.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/__tests__/unit/PDFHelper.planVersionDates.spec.js b/__tests__/unit/PDFHelper.planVersionDates.spec.js index b17e05b4..25dc3925 100644 --- a/__tests__/unit/PDFHelper.planVersionDates.spec.js +++ b/__tests__/unit/PDFHelper.planVersionDates.spec.js @@ -1,8 +1,11 @@ -import { describe, expect, it } from 'vitest'; -import { formatPlanVersionDates } from '../../src/router/helpers/PDFHelper.ts'; +import { describe, expect, it, vi } from 'vitest'; + +vi.mock('../../src/libs/db2/model/agreement.js', () => ({ default: {} })); describe('formatPlanVersionDates', () => { - it('uses the BC timezone for dates near UTC midnight', () => { + it('uses the BC timezone for dates near UTC midnight', async () => { + const { formatPlanVersionDates } = await import('../../src/router/helpers/PDFHelper.ts'); + const plan = { originalApproval: { date: '2024-03-16T06:30:00.000Z' }, amendmentSubmissions: [ From 6ef16834e155ad5cac463504e0ca9fe2e1630923 Mon Sep 17 00:00:00 2001 From: Brijesh Date: Fri, 7 Aug 2026 13:41:53 -0700 Subject: [PATCH 4/5] #535 - protect migration database selection --- __tests__/unit/migrate.spec.js | 19 +++++++++++++++++++ src/libs/db2/migrate.ts | 13 +++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 __tests__/unit/migrate.spec.js diff --git a/__tests__/unit/migrate.spec.js b/__tests__/unit/migrate.spec.js new file mode 100644 index 00000000..21663cd5 --- /dev/null +++ b/__tests__/unit/migrate.spec.js @@ -0,0 +1,19 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { createDb } from '../../src/libs/db2/migrate.ts'; + +const originalNodeEnv = process.env.NODE_ENV; +const originalTestDatabase = process.env.POSTGRESQL_DATABASE_TEST; + +afterEach(() => { + process.env.NODE_ENV = originalNodeEnv; + process.env.POSTGRESQL_DATABASE_TEST = originalTestDatabase; +}); + +describe('createDb', () => { + it.each(['test', 'unit_test'])('rejects %s when the test database is not configured', (nodeEnv) => { + process.env.NODE_ENV = nodeEnv; + delete process.env.POSTGRESQL_DATABASE_TEST; + + expect(() => createDb()).toThrow('POSTGRESQL_DATABASE_TEST must be set'); + }); +}); diff --git a/src/libs/db2/migrate.ts b/src/libs/db2/migrate.ts index bf593f57..c3fce99f 100644 --- a/src/libs/db2/migrate.ts +++ b/src/libs/db2/migrate.ts @@ -69,16 +69,17 @@ function parseUpSql(content: string): string { const { Pool } = pg; -function createDb(): Kysely { +export function createDb(): Kysely { + const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'unit_test'; + if (isTestEnvironment && !process.env.POSTGRESQL_DATABASE_TEST) { + throw new Error('POSTGRESQL_DATABASE_TEST must be set when running migrations in a test environment'); + } + const dialect = new PostgresDialect({ pool: new Pool({ host: process.env.POSTGRESQL_HOST, port: parseInt(process.env.POSTGRESQL_PORT || '5432', 10), - database: - (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'unit_test') && - process.env.POSTGRESQL_DATABASE_TEST - ? process.env.POSTGRESQL_DATABASE_TEST - : process.env.POSTGRESQL_DATABASE, + database: isTestEnvironment ? process.env.POSTGRESQL_DATABASE_TEST : process.env.POSTGRESQL_DATABASE, user: process.env.POSTGRESQL_USER, password: process.env.POSTGRESQL_PASSWORD, max: 10, From 27aa0897390e967e31ef8c64e573224b6e20a7ae Mon Sep 17 00:00:00 2001 From: Brijesh Date: Fri, 7 Aug 2026 13:44:43 -0700 Subject: [PATCH 5/5] #535 - preserve unit test environment convention --- __tests__/unit/config.spec.js | 1 + package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/__tests__/unit/config.spec.js b/__tests__/unit/config.spec.js index e84b1058..193efe62 100644 --- a/__tests__/unit/config.spec.js +++ b/__tests__/unit/config.spec.js @@ -5,6 +5,7 @@ describe('config', () => { process.env.POSTGRESQL_USER = 'test_user'; process.env.POSTGRESQL_PASSWORD = 'test_pass'; process.env.POSTGRESQL_DATABASE = 'test_db'; + process.env.POSTGRESQL_DATABASE_TEST = 'test_db'; process.env.POSTGRESQL_HOST = 'test_host'; process.env.POSTGRESQL_PORT = '5432'; process.env.SSO_URL = 'https://sso.example.com'; diff --git a/package.json b/package.json index 22ee19ef..bee665cd 100644 --- a/package.json +++ b/package.json @@ -50,8 +50,8 @@ "build": "rm -rf build && tsc", "build:doc": "./node_modules/.bin/apidoc -i src/ -o doc/api", "db:migrate": "tsx src/libs/db2/migrate.ts", - "test": "cross-env NODE_ENV=test vitest run --coverage", - "test:watch": "cross-env NODE_ENV=test vitest", + "test": "cross-env NODE_ENV=unit_test vitest run --coverage", + "test:watch": "cross-env NODE_ENV=unit_test vitest", "test:lint": "eslint src __tests__ __testHelpers__ __mocks__ scripts", "import": "node build/scripts/import.js", "plan_extension_exemption": "node build/scripts/plan_extension_exemption.js",