mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Add estimate_temp range validation and docs note
This commit is contained in:
@@ -2,7 +2,12 @@ import { beforeEach, beforeAll, afterAll, describe, it, expect } from '@jest/glo
|
|||||||
import { Locality, LocalityDetailsType, SpeciesDetailsType } from '../../../../frontend/src/shared/types'
|
import { Locality, LocalityDetailsType, SpeciesDetailsType } from '../../../../frontend/src/shared/types'
|
||||||
import { LogRow } from '../../services/write/writeOperations/types'
|
import { LogRow } from '../../services/write/writeOperations/types'
|
||||||
import { ValidationObject } from '../../../../frontend/src/shared/validators/validator'
|
import { ValidationObject } from '../../../../frontend/src/shared/validators/validator'
|
||||||
import { invalidPollenCreateLocality, invalidPollenTotalCreateLocality, newLocalityBasis } from './data'
|
import {
|
||||||
|
invalidEstimateTempCreateLocality,
|
||||||
|
invalidPollenCreateLocality,
|
||||||
|
invalidPollenTotalCreateLocality,
|
||||||
|
newLocalityBasis,
|
||||||
|
} from './data'
|
||||||
import { login, logout, resetDatabase, send, testLogRows, resetDatabaseTimeout, noPermError } from '../utils'
|
import { login, logout, resetDatabase, send, testLogRows, resetDatabaseTimeout, noPermError } from '../utils'
|
||||||
import { pool } from '../../utils/db'
|
import { pool } from '../../utils/db'
|
||||||
|
|
||||||
@@ -122,6 +127,23 @@ describe('Creating new locality works', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('Creation fails when estimate temperature is out of range', async () => {
|
||||||
|
const { body, status } = await send<ValidationObject[]>('locality', 'PUT', {
|
||||||
|
locality: invalidEstimateTempCreateLocality,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(status).toEqual(403)
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
{
|
||||||
|
name: 'Estimated temperature',
|
||||||
|
error: 'Estimated temperature must be between -999.9 and 999.9',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('Species update also logged correctly', async () => {
|
it('Species update also logged correctly', async () => {
|
||||||
const speciesId = createdLocality!.now_ls.find(ls => ls.com_species.species_name === 'Newspecies')?.species_id
|
const speciesId = createdLocality!.now_ls.find(ls => ls.com_species.species_name === 'Newspecies')?.species_id
|
||||||
const speciesResult = await send<SpeciesDetailsType>(`species/${speciesId}`, 'GET')
|
const speciesResult = await send<SpeciesDetailsType>(`species/${speciesId}`, 'GET')
|
||||||
|
|||||||
@@ -256,6 +256,17 @@ export const invalidPollenTotalCreateLocality = {
|
|||||||
pers_pollen_other: 20,
|
pers_pollen_other: 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const invalidEstimateTempCreateLocality = {
|
||||||
|
...newLocalityBasis,
|
||||||
|
estimate_temp: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const invalidEstimateTempUpdateLocality = {
|
||||||
|
...editedLocality,
|
||||||
|
estimate_temp: -1000,
|
||||||
|
}
|
||||||
|
|
||||||
export const invalidPollenUpdateLocality = {
|
export const invalidPollenUpdateLocality = {
|
||||||
...editedLocality,
|
...editedLocality,
|
||||||
pers_pollen_ap: 101,
|
pers_pollen_ap: 101,
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ import { beforeEach, beforeAll, afterAll, describe, it, expect } from '@jest/glo
|
|||||||
import { LocalityDetailsType, SpeciesDetailsType } from '../../../../frontend/src/shared/types'
|
import { LocalityDetailsType, SpeciesDetailsType } from '../../../../frontend/src/shared/types'
|
||||||
import { LogRow } from '../../services/write/writeOperations/types'
|
import { LogRow } from '../../services/write/writeOperations/types'
|
||||||
import { ValidationObject } from '../../../../frontend/src/shared/validators/validator'
|
import { ValidationObject } from '../../../../frontend/src/shared/validators/validator'
|
||||||
import { editedLocality, invalidPollenTotalUpdateLocality, invalidPollenUpdateLocality } from './data'
|
import {
|
||||||
|
editedLocality,
|
||||||
|
invalidEstimateTempUpdateLocality,
|
||||||
|
invalidPollenTotalUpdateLocality,
|
||||||
|
invalidPollenUpdateLocality,
|
||||||
|
} from './data'
|
||||||
import { login, resetDatabase, send, testLogRows, resetDatabaseTimeout } from '../utils'
|
import { login, resetDatabase, send, testLogRows, resetDatabaseTimeout } from '../utils'
|
||||||
import { pool } from '../../utils/db'
|
import { pool } from '../../utils/db'
|
||||||
|
|
||||||
@@ -118,6 +123,23 @@ describe('Locality update works', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('Update fails when estimate temperature is out of range', async () => {
|
||||||
|
const { body, status } = await send<ValidationObject[]>('locality', 'PUT', {
|
||||||
|
locality: invalidEstimateTempUpdateLocality,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(status).toEqual(403)
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
{
|
||||||
|
name: 'Estimated temperature',
|
||||||
|
error: 'Estimated temperature must be between -999.9 and 999.9',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('Editing locality without changing anything should succeed', async () => {
|
it('Editing locality without changing anything should succeed', async () => {
|
||||||
const locality = editedLocality
|
const locality = editedLocality
|
||||||
// fixing correct species_id, was 85729
|
// fixing correct species_id, was 85729
|
||||||
|
|||||||
@@ -58,6 +58,15 @@ Instead of calling the `setFieldsWithErrors` function directly with the new erro
|
|||||||
|
|
||||||
This ensures that when multiple components try to update the state at the same time, they don't overwrite each other. The react documentation goes into more detail: https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state
|
This ensures that when multiple components try to update the state at the same time, they don't overwrite each other. The react documentation goes into more detail: https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state
|
||||||
|
|
||||||
|
|
||||||
|
**Decimal fields and runtime type caveat**
|
||||||
|
|
||||||
|
Some DB fields use MariaDB `DECIMAL` and are represented as `Decimal` in Prisma-level TypeScript types (for example `estimate_temp` in `frontend/src/shared/types/data.ts`). In runtime data sent to the frontend, these values are JSON-serialized via Decimal.js `toJSON()`, so they arrive as strings (for example `"5.1"`).
|
||||||
|
|
||||||
|
- Keep this mismatch in mind when adding UI logic for decimal fields.
|
||||||
|
- Prefer explicit parsing/conversion at usage points if numeric operations are required.
|
||||||
|
- `estimate_temp` additionally has validator bounds to avoid DB errors from out-of-range values (`-999.9` to `999.9`).
|
||||||
|
|
||||||
**Detailed documentation for editingComponents**
|
**Detailed documentation for editingComponents**
|
||||||
|
|
||||||
- [RadioSelector](./components/editingComponents/RadioSelector.md)
|
- [RadioSelector](./components/editingComponents/RadioSelector.md)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const pollenFields = ['pers_pollen_ap', 'pers_pollen_nap', 'pers_pollen_other']
|
|||||||
|
|
||||||
export const pollenTotalValidationError =
|
export const pollenTotalValidationError =
|
||||||
'Combined Arboreal (AP%), Non-arboreal (NAP%), and Other pollen (OP%) must be less than or equal to 100'
|
'Combined Arboreal (AP%), Non-arboreal (NAP%), and Other pollen (OP%) must be less than or equal to 100'
|
||||||
|
export const estimateTempRangeValidationError = 'Estimated temperature must be between -999.9 and 999.9'
|
||||||
|
|
||||||
const validatePollenPercentage = (name: string, num: number) => {
|
const validatePollenPercentage = (name: string, num: number) => {
|
||||||
if (!Number.isInteger(num)) return `${name} must be an integer value`
|
if (!Number.isInteger(num)) return `${name} must be an integer value`
|
||||||
@@ -185,6 +186,13 @@ export const validateLocality = (
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
estimate_temp: {
|
||||||
|
name: 'Estimated temperature',
|
||||||
|
asNumber: (num: number) => {
|
||||||
|
if (num <= -1000 || num >= 1000) return estimateTempRangeValidationError
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return validator<EditDataType<LocalityDetailsType>>(validators, editData, fieldName)
|
return validator<EditDataType<LocalityDetailsType>>(validators, editData, fieldName)
|
||||||
|
|||||||
Reference in New Issue
Block a user