Skip to content

Commit

Permalink
chore: updated tests for critter capture row validator
Browse files Browse the repository at this point in the history
  • Loading branch information
MacQSL committed Jan 21, 2025
1 parent 5c73943 commit 716ee44
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ export function importCaptureCSV(): RequestHandler {
const connection = getDBConnection(req.keycloak_token);

const mediaFile = parseMulterFile(rawFile);
const workbook = constructXLSXWorkbook(mediaFile);
const worksheet = getDefaultWorksheet(workbook);
const worksheet = getDefaultWorksheet(constructXLSXWorkbook(mediaFile));

try {
await connection.open();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { CSVCellValidator, CSVError } from '../../../utils/csv-utils/csv-config-
import { updateCSVRowState } from '../../../utils/csv-utils/csv-header-configs';
import { NestedRecord } from '../../../utils/nested-record';
import {
CBQualitativeMeasurement,
CBQualitativeMeasurementTypeDefinition,
CBQuantitativeMeasurement,
CBQuantitativeMeasurementTypeDefinition,
ICritterDetailed
} from '../../critterbase-service';
Expand Down Expand Up @@ -44,7 +46,7 @@ export const getDynamicMeasurementCellValidator = (
if (!taxonMeasurements) {
return [

Check warning on line 47 in api/src/services/import-services/measurement/measurement-header-configs.ts

View check run for this annotation

Codecov / codecov/patch

api/src/services/import-services/measurement/measurement-header-configs.ts#L47

Added line #L47 was not covered by tests
{
error: `No measurements exist for this taxon TSN: ${critterTsn}`,
error: `Taxon: ${critterTsn} has no reference measurements`,
solution: 'Make sure the taxon has reference measurements'
}
];
Expand Down Expand Up @@ -119,16 +121,14 @@ export const getQuantitativeMeasurementCellValidator = (
});
}

/**
* Update the row state with the taxon measurement id and value
* Why an object ([params.header]: {...})?
* Prevents overriding state if multiple measurements are stored in the same row.
*/
// Update the row state with the taxon measurement id and value
updateCSVRowState(params.row, {

Check warning on line 125 in api/src/services/import-services/measurement/measurement-header-configs.ts

View check run for this annotation

Codecov / codecov/patch

api/src/services/import-services/measurement/measurement-header-configs.ts#L125

Added line #L125 was not covered by tests
// Using header to prevent overwriting other measurements
// ie: This function will be called once for each dynamic header
[params.header]: {
taxon_measurement_id: measurement.taxon_measurement_id,
value: params.cell
}
} satisfies Partial<CBQuantitativeMeasurement>
});

return cellErrors;

Check warning on line 134 in api/src/services/import-services/measurement/measurement-header-configs.ts

View check run for this annotation

Codecov / codecov/patch

api/src/services/import-services/measurement/measurement-header-configs.ts#L134

Added line #L134 was not covered by tests
Expand Down Expand Up @@ -172,16 +172,14 @@ export const getQualitativeMeasurementCellValidator = (
];
}

/**
* Update the row state with the taxon measurement id and qualitative option id
* Why an object ([params.header]: {...})?
* Prevents overriding state if multiple measurements are stored in the same row.
*/
// Update the row state with the taxon measurement id and qualitative option id
updateCSVRowState(params.row, {

Check warning on line 176 in api/src/services/import-services/measurement/measurement-header-configs.ts

View check run for this annotation

Codecov / codecov/patch

api/src/services/import-services/measurement/measurement-header-configs.ts#L176

Added line #L176 was not covered by tests
// Using header to prevent overwriting other measurements
// ie: This function will be called once for each dynamic header
[params.header]: {
taxon_measurement_id: measurement.taxon_measurement_id,
qualitative_measurement_id: matchingOptionValue.qualitative_option_id
}
qualitative_option_id: matchingOptionValue.qualitative_option_id
} satisfies Partial<CBQualitativeMeasurement>
});

return [];

Check warning on line 185 in api/src/services/import-services/measurement/measurement-header-configs.ts

View check run for this annotation

Codecov / codecov/patch

api/src/services/import-services/measurement/measurement-header-configs.ts#L185

Added line #L185 was not covered by tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { CSVRowState } from '../csv-config-validation.interface';
import { getCritterCaptureRowValidator } from './critter-capture-row-validator';

chai.use(sinonChai);

describe.only('getCritterCaptureRowValidator', () => {
afterEach(() => {
sinon.restore();
});

it('should return an error if the critter alias is not found in the survey alias map', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('BAD_ALIAS');

const surveyAliasMap = new Map<string, any>();

const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const errors = rowValidator({ row: {} } as any);

expect(errors).to.have.lengthOf(1);
expect(errors[0].error).to.contain('matching survey animal');
expect(errors[0].cell).to.equal('BAD_ALIAS');
expect(errors[0].header).to.be.equal('HEADER');
});

it('should return an error if the critter has no captures', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('critter');

const surveyAliasMap = new Map<string, any>([['critter', { captures: [] }]]);

const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const errors = rowValidator({ row: {} } as any);

expect(errors).to.have.lengthOf(1);
expect(errors[0].error).to.contain('no captures');
expect(errors[0].cell).to.equal('critter');
expect(errors[0].header).to.be.equal('HEADER');
});

it('should return an error if the capture date is not found in the critter captures', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('critter');
getCellValueStub.onCall(1).returns('2024-01-01');
getCellValueStub.onCall(2).returns('10:10:00');

const surveyAliasMap = new Map<string, any>([['critter', { captures: [{ capture_date: '2025-01-01' }] }]]);
const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const errors = rowValidator({ row: {} } as any);

expect(errors).to.have.lengthOf(1);
expect(errors[0].error).to.contain('Capture not found');
expect(errors[0].cell).to.equal('2024-01-01');
expect(errors[0].header).to.be.equal('HEADER');
});

it('should return an error if multiple captures are found for the critter', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('critter');
getCellValueStub.onCall(1).returns('2024-01-01');
getCellValueStub.onCall(2).returns('10:10:00');

const surveyAliasMap = new Map<string, any>([
[
'critter',
{
captures: [
{
capture_date: '2024-01-01',
capture_time: '10:10:00'
},
{
capture_date: '2024-01-01',
capture_time: '10:10:00'
}
]
}
]
]);
const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const errors = rowValidator({ row: {} } as any);

expect(errors).to.have.lengthOf(1);
expect(errors[0].error).to.contain('Multiple captures found');
expect(errors[0].cell).to.equal('2024-01-01');
expect(errors[0].header).to.be.equal('HEADER');
});

it('should return an empty array if the critter capture is valid', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('critter');
getCellValueStub.onCall(1).returns('2024-01-01');
getCellValueStub.onCall(2).returns('10:10:00');

const surveyAliasMap = new Map<string, any>([
[
'critter',
{
captures: [
{
capture_date: '2024-01-01',
capture_time: '10:10:00'
}
]
}
]
]);
const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const errors = rowValidator({ row: {} } as any);

expect(errors).to.have.lengthOf(0);
});

it('should update the row state with the critter_id and capture_id', () => {
const getCellValueStub = sinon.stub();
const getWorksheetHeaderStub = sinon.stub().returns('HEADER');

const utils = {
getCellValue: getCellValueStub,
getWorksheetHeader: getWorksheetHeaderStub
};

getCellValueStub.onCall(0).returns('critter');
getCellValueStub.onCall(1).returns('2024-01-01');
getCellValueStub.onCall(2).returns('10:10:00');

const surveyAliasMap = new Map<string, any>([
[
'critter',
{
critter_id: 'critter_id',
captures: [
{
capture_id: 'capture_id',
capture_date: '2024-01-01',
capture_time: '10:10:00'
}
]
}
]
]);
const rowValidator = getCritterCaptureRowValidator(surveyAliasMap, utils as any);

const row = {};

rowValidator({ row } as any);

expect(row[CSVRowState].critter_id).to.equal('critter_id');
expect(row[CSVRowState].capture_id).to.equal('capture_id');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ export const getCritterCaptureRowValidator = (
): CSVRowValidator => {
return (params) => {
const alias = String(utils.getCellValue(headers.alias, params.row));
const captureDate = String(utils.getCellValue(headers.captureDate, params.row));
const captureTime = utils.getCellValue(headers.captureTime, params.row);

// Safely convert the capture time to a string
const captureTimeStr = captureTime ? String(captureTime) : undefined;

const critter = surveyAliasMap.get(alias.toLowerCase());

// If the alias is not found in the survey alias map ie: critter does not exist in the survey with this alias
Expand All @@ -69,6 +63,10 @@ export const getCritterCaptureRowValidator = (
];
}

const captureDate = String(utils.getCellValue(headers.captureDate, params.row));
const captureTime = utils.getCellValue(headers.captureTime, params.row);
const captureTimeStr = captureTime ? String(captureTime) : undefined;

const foundCaptures = findCapturesFromDateTime(critter.captures, captureDate, captureTimeStr);

// If unable to map the capture date and time to a specific critter capture
Expand Down

0 comments on commit 716ee44

Please sign in to comment.