Files
vlt/projects/hscc/mcq-test-results-js
2026-09-18 23:22:55 +10:00
..
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00
2026-09-18 23:22:55 +10:00

mcq-test-results-js

I had originally thought to write this service using Rust and Postgres as the data store, but after some consideration I decided to use JavaScript (not TypeScript) to iterate quickly, spend less time fighting with the borrow checker or tweaking moduleResolution in a tsconfig.json and focus on aspects I considered to be important:

  • clean modular code
  • dependency injection
  • easily extensible integration tests
    • See src/api.test.js

Plus, it's just a prototype right? :P

I chose SQLite to simplify the deployment and running of the service, as it can be tricky ensuring that a separate DB service is migrated and running before the service starts trying to connect. Additionally it allows the integration tests to use the database layer without having to run multiple services inside a compose network. Finally I wanted to use SQL features like transactions and check constraints to meet requirements of atomicity due to unpaid AWS bills.

Regarding the XML parsing, I chose to use a library with a simple API and be quite generous in what I accepted in requests instead of using a schema and instead let the application and database layers enforce missing or invalid fields.

Using the Service

Starting the Service

docker compose up

localhost:4567 is forwarded to the listening service inside docker.

Tests

Running tests requires NPM to be installed, if you have it you can simply run

npm i
npm t

This will install the dependencies and run the tests.

Note that the database on your host machine will not be shared with the instance running inside docker.

Deployment

You can find the built service image with

docker images | grep mcq-test-results

This image can then be tagged and pushed to the appropriate container registry to be run in ECS, or EKS, or whatever container service it is we use here at Markr...

IMPORTANT

In order for the service's data to be persisted the directory /opt/mcq-test-results/db must be mounted as a high write speed persistent block store.

Next Steps

Migrate to PostgreSQL

Postgres has excellent built in aggregate functions such as percentile_cont that will likely outperform the application level calculations being done by the simple-statistics library used here.

Logging

The application has very little in the way of logging. Suggest a JSON logging library such as Bunyan to improve debugging and traceability of the application.

Assumptions

Student Uniqueness

The sample data didn't seem to make a great deal of sense; perhaps our friend at the school board butchered it redacting student PII.

It contained entries where for example:

  • the same student-number was supplied by students with different names
    • John John & Jean Stephanie both supplied 2398
  • students with the same name supplied different student-numbers
    • there are 3 Penelope Graces (!) all supplying different student numbers

Here I assumed that the students and teacher are smart enough to know their names and student numbers and consider them to be unique individuals based on the combination of those attributes. ...then again maybe they're just messing with the teacher...

On Selecting the highest score

When inserting entries into the database, a row is only inserted or updated if the marksObtained are greater than the value of an existing row. I had originally thought to standardise the marksAvailable based on the highest value seen in any entry for a given testId but I didn't know definitively that a test should all have the same marksAvailable. The test data didn't seem to indicate that the machines could submit any entries to the contrary either.

Size Limits

20MB is assumed to be the maximum test submission size