Skip to content

Commit

Permalink
feat: load older corrections to build submission version
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Jun 27, 2022
1 parent 56456b5 commit 3a58c7b
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 91 deletions.
41 changes: 15 additions & 26 deletions src/server/routes/assessment/assessment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
import { Form } from '../../../form/form.js'
import { Submission } from '../../../form/submission.js'
import { formSchema } from '../../../schema/form.js'
import { Store } from '../../../storage/store.js'
import { portForTest } from '../../../test/portForTest.js'
import { tempJsonFileStore } from '../../../test/tempJsonFileStore.js'
import { ulid } from '../../../ulid.js'
import { HTTPStatusCode } from '../../response/HttpStatusCode.js'
import login from '../login.js'
Expand Down Expand Up @@ -53,39 +53,27 @@ const simpleForm: Form = {
},
],
}
const forms: Record<string, any> = {
[formId]: simpleForm,
}
const dummyFormStorage: Store<Form> = {
persist: async (id, form) => {
forms[id] = form
},
get: async (id) =>
forms[id] !== undefined ? { id, data: forms[id] } : undefined,
findAll: async () => [],
}

const submissions: Record<string, Static<typeof Submission>> = {}
const dummySubmissionStorage: Store<Static<typeof Submission>> = {
persist: async (id, form) => {
submissions[id] = form
},
get: async (id) =>
submissions[id] !== undefined ? { id, data: submissions[id] } : undefined,
findAll: async () => [],
}

const omnibus = new EventEmitter()

describe('Assessment API', () => {
let app: Express
let httpServer: Server
let r: SuperTest<Test>
const cleanups: (() => Promise<void>)[] = []

const adminEmail = `some-admin${ulid()}@example.com`
const getExpressCookie = getAuthCookie(1800, [adminEmail])

beforeAll(async () => {
const { cleanup: cleanupFormStorage, store: formStorage } =
await tempJsonFileStore<Form>()
cleanups.push(cleanupFormStorage)
await formStorage.persist(formId, simpleForm)
const { cleanup: cleanupSubmissionStorage, store: submissionStorage } =
await tempJsonFileStore<Static<typeof Submission>>()
cleanups.push(cleanupSubmissionStorage)

app = express()
app.use(cookieParser('cookie-secret'))
app.use(bodyParser.json({ strict: true }))
Expand All @@ -97,17 +85,17 @@ describe('Assessment API', () => {
assessmentSubmissionHandler({
omnibus,
endpoint,
formStorage: dummyFormStorage,
submissionStorage: dummySubmissionStorage,
formStorage,
submissionStorage: submissionStorage,
}),
)
app.post(
'/assessment/export',
cookieAuth,
assessmentsExportHandler({
endpoint,
formStorage: dummyFormStorage,
submissionStorage: dummySubmissionStorage,
formStorage: formStorage,
submissionStorage: submissionStorage,
}),
)

Expand All @@ -124,6 +112,7 @@ describe('Assessment API', () => {
})
afterAll(async () => {
httpServer.close()
await Promise.all(cleanups)
})
describe('POST /assessment', () => {
it('should store a valid submission', async () =>
Expand Down
2 changes: 1 addition & 1 deletion src/server/routes/assessment/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ export const assessmentSubmissionHandler = ({
}

const id = ulid()
omnibus.emit(events.assessment_created, id, validBody.value, form)
await submissionStorage.persist(id, validBody.value)
response
.status(HTTPStatusCode.Created)
.header('Location', new URL(`./assessment/${id}`, endpoint).toString())
.header('ETag', '1')
.end()
omnibus.emit(events.assessment_created, id, validBody.value, form)
}
}
19 changes: 12 additions & 7 deletions src/server/routes/correction/correct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ export const assessmentCorrectionHandler = ({
})
}

// TODO: load older corrections to build submission version
const submissionVersion = 1
// Load older corrections to build submission version
const submissionVersion =
(
await correctionStorage.findAll({
submission: validBody.value.submission,
})
).length + 1
if (validBody.value.submissionVersion !== submissionVersion) {
return respondWithProblem(request, response, {
title: `Expected correction for submission version ${submissionVersion}, got ${validBody.value.submissionVersion}.`,
Expand Down Expand Up @@ -121,17 +126,17 @@ export const assessmentCorrectionHandler = ({
}

const id = ulid()
await correctionStorage.persist(id, validBody.value)
response
.status(HTTPStatusCode.Created)
.header('Location', new URL(`./correction/${id}`, endpoint).toString())
.end()
omnibus.emit(
events.correction_created,
id,
validBody.value,
form,
submission,
)
await correctionStorage.persist(id, validBody.value)
response
.status(HTTPStatusCode.Created)
.header('Location', new URL(`./correction/${id}`, endpoint).toString())
.end()
}
}
96 changes: 61 additions & 35 deletions src/server/routes/correction/correction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { Correction } from '../../../form/correction.js'
import { Form } from '../../../form/form.js'
import { Submission } from '../../../form/submission.js'
import { formSchema } from '../../../schema/form.js'
import { Store } from '../../../storage/store.js'
import { portForTest } from '../../../test/portForTest.js'
import { tempJsonFileStore } from '../../../test/tempJsonFileStore.js'
import { ulid } from '../../../ulid.js'
import { HTTPStatusCode } from '../../response/HttpStatusCode.js'
import login from '../login.js'
Expand Down Expand Up @@ -53,19 +53,7 @@ const simpleForm: Form = {
},
],
}
const forms: Record<string, any> = {
[formId]: simpleForm,
}
const dummyFormStorage: Store<Form> = {
persist: async (id, form) => {
forms[id] = form
},
get: async (id) =>
forms[id] !== undefined ? { id, data: forms[id] } : undefined,
findAll: async () => [],
}

const submissions: Record<string, Static<typeof Submission>> = {}
const submissionId = ulid()
const submission: Static<typeof Submission> = {
form: new URL(`./form/${formId}`, endpoint).toString(),
Expand All @@ -75,37 +63,31 @@ const submission: Static<typeof Submission> = {
},
},
}
submissions[submissionId] = submission
const dummySubmissionStorage: Store<Static<typeof Submission>> = {
persist: async (id, form) => {
submissions[id] = form
},
get: async (id) =>
submissions[id] !== undefined ? { id, data: submissions[id] } : undefined,
findAll: async () => [],
}

const corrections: Record<string, Static<typeof Correction>> = {}
const dummyCorrectionStorage: Store<Static<typeof Correction>> = {
persist: async (id, form) => {
corrections[id] = form
},
get: async (id) =>
corrections[id] !== undefined ? { id, data: corrections[id] } : undefined,
findAll: async () => [],
}

const omnibus = new EventEmitter()

describe('Correction API', () => {
let app: Express
let httpServer: Server
let r: SuperTest<Test>
const cleanups: (() => Promise<void>)[] = []

const adminEmail = `some-admin${ulid()}@example.com`
const getExpressCookie = getAuthCookie(1800, [adminEmail])

beforeAll(async () => {
const { cleanup: cleanupFormStorage, store: formStorage } =
await tempJsonFileStore<Form>()
cleanups.push(cleanupFormStorage)
await formStorage.persist(formId, simpleForm)
const { cleanup: cleanupSubmissionStorage, store: submissionStorage } =
await tempJsonFileStore<Static<typeof Submission>>()
cleanups.push(cleanupSubmissionStorage)
await submissionStorage.persist(submissionId, submission)
const { cleanup: cleanupCorrectionStorage, store: correctionStorage } =
await tempJsonFileStore<Static<typeof Correction>>()
cleanups.push(cleanupCorrectionStorage)

app = express()
app.use(cookieParser('cookie-secret'))
app.use(bodyParser.json({ strict: true }))
Expand All @@ -118,9 +100,9 @@ describe('Correction API', () => {
assessmentCorrectionHandler({
omnibus,
endpoint,
formStorage: dummyFormStorage,
submissionStorage: dummySubmissionStorage,
correctionStorage: dummyCorrectionStorage,
formStorage,
submissionStorage,
correctionStorage,
}),
)
app.post(
Expand All @@ -136,6 +118,7 @@ describe('Correction API', () => {
})
afterAll(async () => {
httpServer.close()
await Promise.all(cleanups)
})

describe('POST /correction', () => {
Expand Down Expand Up @@ -182,6 +165,49 @@ describe('Correction API', () => {
`^http://127.0.0.1:${port}/correction/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$`,
),
))

it('should store another correction', async () =>
r
.post('/correction')
.set('Content-type', 'application/json; charset=utf-8')
.set('Cookie', [`${authCookieName}=${authCookie}`])
.set('If-Match', '2')
.send({
form: new URL(`./form/${formId}`, endpoint),
submission: new URL(`./assessment/${submissionId}`, endpoint),
response: {
section1: {
question1: 'Corrected answer, again',
},
},
})
.expect(HTTPStatusCode.Created)
.expect(
'Location',
new RegExp(
`^http://127.0.0.1:${port}/correction/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$`,
),
))

it.each([['1', '3', 'a']])(
'should not store a correction on etag mismatch (%s)',
async (etag) =>
r
.post('/correction')
.set('Content-type', 'application/json; charset=utf-8')
.set('Cookie', [`${authCookieName}=${authCookie}`])
.set('If-Match', etag)
.send({
form: new URL(`./form/${formId}`, endpoint),
submission: new URL(`./assessment/${submissionId}`, endpoint),
response: {
section1: {
question1: 'Corrected answer, again',
},
},
})
.expect(HTTPStatusCode.Conflict),
)
})
})
})
23 changes: 9 additions & 14 deletions src/server/routes/form/form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,14 @@ import request, { SuperTest, Test } from 'supertest'
import { URL } from 'url'
import { Form } from '../../../form/form.js'
import { formSchema } from '../../../schema/form.js'
import { Store } from '../../../storage/store.js'
import { portForTest } from '../../../test/portForTest.js'
import { tempJsonFileStore } from '../../../test/tempJsonFileStore.js'
import { HTTPStatusCode } from '../../response/HttpStatusCode.js'
import { formCreationHandler } from './create.js'
import { formGetHandler } from './get.js'

const port = portForTest(__filename)

const forms: Record<string, Form> = {}

const dummyStorage: Store<Form> = {
persist: async (id, form) => {
forms[id] = form
},
get: async (id) =>
forms[id] !== undefined ? { id, data: forms[id] } : undefined,
findAll: async () => [],
}

const endpoint = new URL(`http://127.0.0.1:${port}`)

const schema = formSchema({
Expand All @@ -34,16 +23,21 @@ describe('Form API', () => {
let app: Express
let httpServer: Server
let r: SuperTest<Test>
const cleanups: (() => Promise<void>)[] = []

beforeAll(async () => {
const { cleanup: cleanupFormStorage, store: formStorage } =
await tempJsonFileStore<Form>()
cleanups.push(cleanupFormStorage)

app = express()
app.use(bodyParser.json({ strict: true }))
app.get('/form/:id', formGetHandler({ storage: dummyStorage }))
app.get('/form/:id', formGetHandler({ storage: formStorage }))
app.post(
'/form',
formCreationHandler({
endpoint,
storage: dummyStorage,
storage: formStorage,
schema,
}),
)
Expand All @@ -55,6 +49,7 @@ describe('Form API', () => {
})
afterAll(async () => {
httpServer.close()
await Promise.all(cleanups)
})

const simpleForm = {
Expand Down
14 changes: 6 additions & 8 deletions src/storage/file.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { promises as fs } from 'fs'
import * as os from 'os'
import * as path from 'path'
import { tempJsonFileStore } from '../test/tempJsonFileStore.js'
import { ulid } from '../ulid.js'
import { jsonFileStore } from './file.js'
import { Store } from './store.js'

describe('File store', () => {
let store: Store<any>
let tmpDir: string
let id: string
let cleanup: () => Promise<void>
beforeAll(async () => {
tmpDir = await fs.mkdtemp(`${os.tmpdir()}${path.sep}`)
store = jsonFileStore<any>({ directory: tmpDir })
const { store: s, cleanup: c } = await tempJsonFileStore<any>()
cleanup = c
store = s
id = ulid()
})
afterAll(async () => {
await fs.rm(tmpDir, { recursive: true })
await cleanup()
})

describe('persist()', () => {
Expand Down
Loading

0 comments on commit 3a58c7b

Please sign in to comment.