Skip to content

Commit

Permalink
refactor(central-scan): move "next sheet" endpoint to grout
Browse files Browse the repository at this point in the history
  • Loading branch information
eventualbuddha committed Oct 5, 2024
1 parent 30e86c3 commit 086f212
Show file tree
Hide file tree
Showing 10 changed files with 601 additions and 709 deletions.
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import {
buildMockDippedSmartCardAuth,
DippedSmartCardAuthApi,
} from '@votingworks/auth';
import { electionGridLayoutNewHampshireTestBallotFixtures } from '@votingworks/fixtures';
import { Logger, mockBaseLogger } from '@votingworks/logging';
import {
AdjudicationReason,
BallotMetadata,
BallotType,
DEFAULT_SYSTEM_SETTINGS,
InterpretedHmpbPage,
PageInterpretationWithFiles,
SheetOf,
TEST_JURISDICTION,
} from '@votingworks/types';
import { Scan } from '@votingworks/api';
import { createMockUsbDrive, MockUsbDrive } from '@votingworks/usb-drive';
import { Application } from 'express';
import * as fs from 'node:fs/promises';
import { Server } from 'node:http';
import request from 'supertest';
import { dirSync } from 'tmp';
import { v4 as uuid } from 'uuid';
import { typedAs } from '@votingworks/basics';
import {
buildMockDippedSmartCardAuth,
DippedSmartCardAuthApi,
} from '@votingworks/auth';
import { Server } from 'node:http';
import { Logger, mockBaseLogger } from '@votingworks/logging';
import { MockUsbDrive, createMockUsbDrive } from '@votingworks/usb-drive';
import { buildMockLogger } from '../test/helpers/setup_app';
import { makeMock, makeMockScanner } from '../test/util/mocks';
import { buildCentralScannerApp } from './app';
import { Importer } from './importer';
import { createWorkspace, Workspace } from './util/workspace';
import { buildCentralScannerApp } from './app';
import { buildMockLogger } from '../test/helpers/setup_app';

jest.mock('./importer');

Expand Down Expand Up @@ -152,7 +148,7 @@ const sheet: SheetOf<PageInterpretationWithFiles> = (() => {
];
})();

test('GET /scan/hmpb/ballot/:ballotId/:side/image', async () => {
test('GET /central-scan/hmpb/ballot/:ballotId/:side/image', async () => {
const batchId = workspace.store.addBatch();
const sheetId = workspace.store.addSheet(uuid(), batchId, sheet);
workspace.store.finishBatch({ batchId });
Expand All @@ -166,7 +162,7 @@ test('GET /scan/hmpb/ballot/:ballotId/:side/image', async () => {
.expect(200, await fs.readFile(backImagePath));
});

test('GET /scan/hmpb/ballot/:sheetId/image 404', async () => {
test('GET /central-scan/hmpb/ballot/:sheetId/image 404', async () => {
await request(app)
.get(`/central-scanner/scan/hmpb/ballot/111/front/image`)
.expect(404);
Expand All @@ -175,128 +171,3 @@ test('GET /scan/hmpb/ballot/:sheetId/image 404', async () => {
test('GET /', async () => {
await request(app).get('/').expect(404);
});

test('get next sheet', async () => {
jest.spyOn(workspace.store, 'getNextAdjudicationSheet').mockReturnValueOnce({
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: { type: 'BlankPage' },
},
back: {
image: { url: '/url/back' },
interpretation: { type: 'BlankPage' },
},
});

await request(app)
.get(`/central-scanner/scan/hmpb/review/next-sheet`)
.expect(
200,
typedAs<Scan.GetNextReviewSheetResponse>({
interpreted: {
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: { type: 'BlankPage' },
},
back: {
image: { url: '/url/back' },
interpretation: { type: 'BlankPage' },
},
},
layouts: {},
definitions: {},
})
);
});

test('get next sheet layouts', async () => {
const metadata: BallotMetadata = {
ballotHash:
electionGridLayoutNewHampshireTestBallotFixtures.electionDefinition
.ballotHash,
ballotType: BallotType.Precinct,
ballotStyleId: 'card-number-3',
precinctId: 'town-id-00701-precinct-id-default',
isTestMode: false,
};
const frontInterpretation: InterpretedHmpbPage = {
type: 'InterpretedHmpbPage',
metadata: {
...metadata,
pageNumber: 1,
},
markInfo: {
ballotSize: { width: 1, height: 1 },
marks: [],
},
adjudicationInfo: {
requiresAdjudication: true,
enabledReasons: [AdjudicationReason.Overvote],
enabledReasonInfos: [
{
type: AdjudicationReason.Overvote,
contestId: 'contest-id',
expected: 1,
optionIds: ['option-id', 'option-id-2'],
optionIndexes: [0, 1],
},
],
ignoredReasonInfos: [],
},
votes: {},
layout: {
pageSize: { width: 1, height: 1 },
metadata: {
...metadata,
pageNumber: 1,
},
contests: [],
},
};
const backInterpretation: InterpretedHmpbPage = {
...frontInterpretation,
metadata: {
...frontInterpretation.metadata,
pageNumber: 2,
},
};
jest.spyOn(workspace.store, 'getNextAdjudicationSheet').mockReturnValueOnce({
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: frontInterpretation,
},
back: {
image: { url: '/url/back' },
interpretation: backInterpretation,
},
});

const response = await request(app)
.get(`/central-scanner/scan/hmpb/review/next-sheet`)
.expect(200);

expect(response.body).toEqual<Scan.GetNextReviewSheetResponse>({
interpreted: {
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: frontInterpretation,
},
back: {
image: { url: '/url/back' },
interpretation: backInterpretation,
},
},
layouts: {
front: frontInterpretation.layout,
back: backInterpretation.layout,
},
definitions: {
front: { contestIds: expect.any(Array) },
back: { contestIds: expect.any(Array) },
},
});
});
139 changes: 137 additions & 2 deletions apps/central-scan/backend/src/app.scanning.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { electionFamousNames2021Fixtures } from '@votingworks/fixtures';
import {
electionFamousNames2021Fixtures,
electionGridLayoutNewHampshireTestBallotFixtures,
} from '@votingworks/fixtures';
import {
AdjudicationReason,
BallotMetadata,
BallotType,
BatchInfo,
DEFAULT_SYSTEM_SETTINGS,
InterpretedHmpbPage,
TEST_JURISDICTION,
} from '@votingworks/types';
import { mockElectionManagerAuth } from '../test/helpers/auth';
import { withApp } from '../test/helpers/setup_app';
import { generateBmdBallotFixture } from '../test/helpers/ballots';
import { withApp } from '../test/helpers/setup_app';
import { ScannedSheetInfo } from './fujitsu_scanner';

const jurisdiction = TEST_JURISDICTION;
Expand Down Expand Up @@ -53,6 +60,134 @@ test('scanBatch with multiple sheets', async () => {
});
});

test('get next sheet', async () => {
await withApp(async ({ workspace, apiClient }) => {
jest
.spyOn(workspace.store, 'getNextAdjudicationSheet')
.mockReturnValueOnce({
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: { type: 'BlankPage' },
},
back: {
image: { url: '/url/back' },
interpretation: { type: 'BlankPage' },
},
});

expect(await apiClient.nextSheet()).toEqual<
Awaited<ReturnType<typeof apiClient.nextSheet>>
>({
interpreted: {
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: { type: 'BlankPage' },
},
back: {
image: { url: '/url/back' },
interpretation: { type: 'BlankPage' },
},
},
layouts: {},
definitions: {},
});
});
});

test('get next sheet layouts', async () => {
const metadata: BallotMetadata = {
ballotHash:
electionGridLayoutNewHampshireTestBallotFixtures.electionDefinition
.ballotHash,
ballotType: BallotType.Precinct,
ballotStyleId: 'card-number-3',
precinctId: 'town-id-00701-precinct-id-default',
isTestMode: false,
};
const frontInterpretation: InterpretedHmpbPage = {
type: 'InterpretedHmpbPage',
metadata: {
...metadata,
pageNumber: 1,
},
markInfo: {
ballotSize: { width: 1, height: 1 },
marks: [],
},
adjudicationInfo: {
requiresAdjudication: true,
enabledReasons: [AdjudicationReason.Overvote],
enabledReasonInfos: [
{
type: AdjudicationReason.Overvote,
contestId: 'contest-id',
expected: 1,
optionIds: ['option-id', 'option-id-2'],
optionIndexes: [0, 1],
},
],
ignoredReasonInfos: [],
},
votes: {},
layout: {
pageSize: { width: 1, height: 1 },
metadata: {
...metadata,
pageNumber: 1,
},
contests: [],
},
};
const backInterpretation: InterpretedHmpbPage = {
...frontInterpretation,
metadata: {
...frontInterpretation.metadata,
pageNumber: 2,
},
};
await withApp(async ({ apiClient, workspace }) => {
jest
.spyOn(workspace.store, 'getNextAdjudicationSheet')
.mockReturnValueOnce({
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: frontInterpretation,
},
back: {
image: { url: '/url/back' },
interpretation: backInterpretation,
},
});

expect(await apiClient.nextSheet()).toEqual<
Awaited<ReturnType<typeof apiClient.nextSheet>>
>({
interpreted: {
id: 'mock-review-sheet',
front: {
image: { url: '/url/front' },
interpretation: frontInterpretation,
},
back: {
image: { url: '/url/back' },
interpretation: backInterpretation,
},
},
layouts: {
front: frontInterpretation.layout,
back: backInterpretation.layout,
},
definitions: {
front: { contestIds: expect.any(Array) },
back: { contestIds: expect.any(Array) },
},
});
});
});

test('continueScanning after invalid ballot', async () => {
const { electionDefinition } = electionFamousNames2021Fixtures;
const ballot = await generateBmdBallotFixture();
Expand Down
Loading

0 comments on commit 086f212

Please sign in to comment.