Skip to content

Commit

Permalink
Feature/55891 add ToE code and pupil source to ps report (#2484)
Browse files Browse the repository at this point in the history
* Upgrade patch deps for tslib, and functions

* Upgrade dotenv to v16.0.3

* upgrade uuid v9.0.0

* upgrade axios to v1.3.4

* axios integration test fix

* upgrade ioredis to v5.3.1

* upgrade @azure/data-tables to v13.2.1

* upgrade various dev-deps

* Upgrade dev deps

* axios fix for jest

* db - add ToECode to ps report

* ws only

* Upgrage @azure/functions to v3.5.0

* db - add migration for importedFromCensus flag

* Add ToE code and ImportedToCensus to PS report

* Update documentation

* db - add isEdited col to pupil

* Add pupil isEdited trigger

* add integration tests

* add fakerjs as dev dep

* add integration tests

* rm newline

* Re-add faker-js a dev dep required for integration tests

* Rm extra whitespace

* whitespace change

* fix trigger

* Update post_check_window_steps.rb

---------

Co-authored-by: Mohsen Qureshi <[email protected]>
  • Loading branch information
jon-shipley and activemq authored Apr 12, 2023
1 parent a01c9a5 commit 88924ef
Show file tree
Hide file tree
Showing 42 changed files with 1,982 additions and 1,352 deletions.
3 changes: 2 additions & 1 deletion admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"watch:integration": "yarn jest --watch --coverage=no --config ./tests-integration/jest.integration.config.js"
},
"mtc": {
"assets-version": "5aac26a8929ffe763043da4a8a2bcbd8"
"assets-version": "e214052e162d4bb534f3f53033a72164"
},
"engines": {
"node": ">= 16"
Expand Down Expand Up @@ -103,6 +103,7 @@
"@babel/core": "^7.10.5",
"@babel/preset-env": "^7.10.4",
"@babel/preset-typescript": "^7.16.0",
"@faker-js/faker": "^7.6.0",
"@types/adm-zip": "^0.4.34",
"@types/dompurify": "^2.3.1",
"@types/fs-extra": "^9.0.1",
Expand Down
274 changes: 274 additions & 0 deletions admin/tests-integration/pupil-edit.service.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
'use strict'
/* global expect describe test afterAll */

/**
* @file Integration Tests for Pupil Edit Service
*/

const pupilAddService = require('../services/pupil-add-service')
const { faker } = require('@faker-js/faker')
const upnService = require('../services/upn.service')
const pupilTestService = require('./test-support/pupil-test.service')
const moment = require('moment')
const sqlService = require('../services/data-access/sql.service')
const redisCacheService = require('../services/data-access/redis-cache.service')

const currentUTCDate = moment.utc()
const currentYear = currentUTCDate.year()
const academicYear = currentUTCDate.isBetween(moment.utc(`${currentYear}-01-01`), moment.utc(`${currentYear}-08-31`), null, '[]')
? currentYear - 1
: currentYear

function createFakeUpn () {
const base = '201' + faker.datatype.number({ min: 100000000, max: 900000000 })
const checkLetter = upnService.calculateCheckLetter(base)
return checkLetter + base.toString()
}

function fakePupilData () {
const gender = faker.helpers.arrayElement(['M', 'F'])
const dob = moment(`${academicYear}-08-31`)
.subtract(8, 'years')
.subtract(faker.datatype.number({ min: 0, max: 364 }), 'days') // randomise dob

return {
school_id: 1,
upn: createFakeUpn(),
foreName: faker.name.firstName(gender === 'M' ? 'male' : 'female'),
middleNames: faker.name.middleName(gender === 'M' ? 'male' : 'female'),
lastName: faker.name.lastName(),
foreNameAlias: '',
lastNameAlias: '',
gender,
ageReason: '',
'dob-year': dob.year().toString(),
'dob-month': (dob.month() + 1).toString().padStart(2, '0'), // months are zero-indexed
'dob-day': dob.date().toString().padStart(2, '0')
}
}

describe('pupilEditService', () => {
const schoolId = 1
const userId = 1

afterAll(async () => {
await sqlService.drainPool()
redisCacheService.disconnect()
})

test('it sets the isEdited flag when the foreName changes', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateForename(dbPupilData.id, 'Testforename')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.foreName).toBe('Testforename')
})

test('it sets the isEdited flag when the middlesNames change', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateMiddlenames(dbPupilData.id, 'New Middle Names')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.middleNames).toBe('New Middle Names')
})

test('it sets the isEdited flag when the lastName changes', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateLastname(dbPupilData.id, 'NewLastName')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.lastName).toBe('NewLastName')
})

test('it sets the isEdited flag when the gender changes', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateGender(dbPupilData.id, dbPupilData.gender === 'M' ? 'F' : 'M')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.gender).toBe(dbPupilData.gender === 'M' ? 'F' : 'M')
})

test('it sets the isEdited flag when the UPN changes', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
const newUpn = createFakeUpn()
await pupilTestService.updateUPN(dbPupilData.id, newUpn)

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(newUpn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.upn).toBe(newUpn)
})

test('it sets the isEdited flag when the date of birth changes', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
const newDob = moment('2023-01-01')
await pupilTestService.updateDob(dbPupilData.id, newDob)

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.dateOfBirth.format('YYYY-MM-DD')).toBe(newDob.format('YYYY-MM-DD'))
})

test('it does not set the isEdited flag when the forename alias is changed', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateForenameAlias(dbPupilData.id, 'first alias')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.isEdited).toBe(false) // does NOT get set!
expect(newPupilData.foreNameAlias).toBe('first alias')
})

test('it does not set the isEdited flag when the lastname alias is changed', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test - confirm the isEdited flag state
expect(dbPupilData.isEdited).toBe(false)

// Apply a change, that will cause the functionality (in this case the pupil trigger for update)
await pupilTestService.updateLastnameAlias(dbPupilData.id, 'last alias')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.isEdited).toBe(false) // does NOT get set!
expect(newPupilData.lastNameAlias).toBe('last alias')
})

test('it updates the updatedAt timestamp when edited ', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

const ts1 = dbPupilData.updatedAt

// Apply any change
await pupilTestService.updateLastnameAlias(dbPupilData.id, 'last alias')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.updatedAt.valueOf()).toBeGreaterThan(ts1.valueOf()) // compare in milliseconds from moment objects.
expect(newPupilData.lastNameAlias).toBe('last alias')
})

test('updating an already-edited pupil record keeps the isEdited field set to true', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test
expect(dbPupilData.isEdited).toBe(false)

// Apply any change
await pupilTestService.updateLastname(dbPupilData.id, 'Newman')

// Check the isEdited flag is now applied. As it is a trigger it will run on changes from the admin app, and even DB Admin.
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.isEdited).toBe(true)
expect(newPupilData.lastName).toBe('Newman')

// update again
await pupilTestService.updateForename(newPupilData.id, 'Zwei')
const updatedPupilData = await pupilTestService.findPupilByUPN(newPupilData.upn)

// final checks
expect(updatedPupilData.isEdited).toBe(true)
expect(updatedPupilData.foreName).toBe('Zwei')
})

test('updating an already-edited pupil record keeps the isEdited field set to false (it was already false)e', async () => {
// setup - add a fresh pupil
const pupilData = fakePupilData()
await pupilAddService.addPupil(pupilData, schoolId, userId)
const dbPupilData = await pupilTestService.findPupilByUPN(pupilData.upn)

// Pre-test
expect(dbPupilData.isEdited).toBe(false)

// Apply a change that does not set the isEdited flag to true
await pupilTestService.updateForenameAlias(dbPupilData.id, 'first alias')

// Review the results
await pupilTestService.updateForenameAlias(dbPupilData.id, 'first alias')
const newPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(newPupilData.isEdited).toBe(false)
expect(newPupilData.foreNameAlias).toBe('first alias')

// Re-apply a change (that does not set the isEdited flag to true
await pupilTestService.updateForenameAlias(dbPupilData.id, 'updated alias')

// review
const updatedPupilData = await pupilTestService.findPupilByUPN(dbPupilData.upn)
expect(updatedPupilData.isEdited).toBe(false)
expect(updatedPupilData.foreNameAlias).toBe('updated alias')
})
})
86 changes: 86 additions & 0 deletions admin/tests-integration/test-support/pupil-test.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict'
const sqlService = require('../../services/data-access/sql.service')
const { TYPES } = require('../../services/data-access/sql.service')

const pupilTestService = {
findPupilByUPN: async function (upn) {
const sql = 'SELECT * from mtc_admin.pupil where upn = @upn'
const params = [{ name: 'upn', value: upn, type: TYPES.Char(13) }]
const data = await sqlService.query(sql, params)
return data[0]
},

updateForename: async function (id, foreName) {
const sql = 'UPDATE mtc_admin.pupil set foreName = @s1 where id = @id'
const params = [
{ name: 's1', value: foreName, type: TYPES.NVarChar(128) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateMiddlenames: async function (id, foreName) {
const sql = 'UPDATE mtc_admin.pupil set middleNames = @s1 where id = @id'
const params = [
{ name: 's1', value: foreName, type: TYPES.NVarChar(128) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateLastname: async function (id, lastname) {
const sql = 'UPDATE mtc_admin.pupil set lastName = @s1 where id = @id'
const params = [
{ name: 's1', value: lastname, type: TYPES.NVarChar(128) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateGender: async function (id, gender) {
const sql = 'UPDATE mtc_admin.pupil set gender = @s1 where id = @id'
const params = [
{ name: 's1', value: gender, type: TYPES.Char(1) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateUPN: async function (id, gender) {
const sql = 'UPDATE mtc_admin.pupil set upn = @s1 where id = @id'
const params = [
{ name: 's1', value: gender, type: TYPES.Char(13) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateDob: async function (id, date) {
const sql = 'UPDATE mtc_admin.pupil set dateOfBirth = @s1 where id = @id'
const params = [
{ name: 's1', value: date.toDate(), type: TYPES.DateTimeOffset },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateForenameAlias: async function (id, alias) {
const sql = 'UPDATE mtc_admin.pupil set foreNameAlias = @s1 where id = @id'
const params = [
{ name: 's1', value: alias, type: TYPES.NVarChar(128) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
},

updateLastnameAlias: async function (id, alias) {
const sql = 'UPDATE mtc_admin.pupil set lastNameAlias = @s1 where id = @id'
const params = [
{ name: 's1', value: alias, type: TYPES.NVarChar(128) },
{ name: 'id', value: id, type: TYPES.Int }
]
await sqlService.modify(sql, params)
}
}

module.exports = pupilTestService
5 changes: 5 additions & 0 deletions admin/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7"
integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==

"@faker-js/faker@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.6.0.tgz#9ea331766084288634a9247fcd8b84f16ff4ba07"
integrity sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==

"@fast-csv/[email protected]":
version "4.3.5"
resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3"
Expand Down
Loading

0 comments on commit 88924ef

Please sign in to comment.