OMX Conformance Tests
Each test has a check you run and an observable pass condition, so this list doubles as a test plan. Run the group for each stage your product runs, and skip the others:
| Group | Run it if your product |
|---|---|
| A. Registration | Serves a registration document |
| B. Registration that names destinations | Also puts a publish array in that document |
| C. Publisher | Accepts results documents |
| D. Meet Operations | Reads registration documents and hands off results documents |
OMX-C107, in group A, applies to every stage. A test marked must is required for conformance, one marked should is a strong recommendation, and one marked may applies only if you build the optional feature it tests. A test’s id never changes, and an id the list skips is never reused.
Three tools do much of the work for you:
- The strict schemas, the registration check
registration-document-v1and the results checkresults-document-v1, fail a value this version does not define, a date-time without seconds, the wrongomx.kind, and a field your stage does not write. No schema fails a field the format does not define (OMX-R129), so a misspelt key passes them: check your field names against the field reference. omx-check.mjschecks what no schema can, and names the test each problem fails. It reads the document’somx.kind;--kind registrationor--kind resultschecks a document as that kind whatever it says.- Each test that needs a crafted document names a fixture. The
fixture index lists them all with the outcome a conforming product
produces (
accept,reject,refuse-version,refuse-kind,refuse-results,older,equal, oroperator), and the test page runs the Publisher tests against your endpoint.acceptis what a reader does; the checker, which judges a document as a writer would send it, may still report problems in anacceptfixture that a reader is meant to tolerate, such asnullvalues or a later version’s values.
Fixture order. Each fixture’s updated_at is later than every example’s and than the fixture
before it in the index, so the Publisher fixtures applied to one meet in the index’s order each
apply. older and equal fixtures are sent right after c30-first, to a meet holding nothing
later. A partial fixture is sent after the full document its test names.
You run every test yourself; none needs sign-off from 2Timer. If a test is ambiguous, that is a bug in the format: report it to [email protected].
A. Registration
Required of anyone serving a registration document, whether or not you want results back.
OMX-C1 — The document validates (must)
- Check: validate your document against
registration-document-v1with any draft-07 validator, with no add-ons or options, and runnode omx-check.mjson it. - Pass: zero errors from both, including for a meet with relays, scratches, and at least one unattached athlete. The lenient reader schema accepts values a later minor version may define, so passing it alone does not pass this test.
OMX-C107 — Every document says its kind (must)
- Check: fetch your document and read
omx.kind. If you also read documents, offer your product the fixturesc107-missing-kind.omx.jsonandc107-unknown-kind.omx.json. - Pass: your document says
"kind": "registration", and a results document you write says"results". Meet Operations refuses a document with no kind, or a kind it does not know, before creating anything, and names what it found. A Publisher answers both422witherrorsnaming/omx/kind, and answers a registration document422too: it reads results documents only. No product works out a document’s kind from its fields (OMX-R135). - Fixtures:
c107-missing-kind,c107-unknown-kind
OMX-C2 — Ids are stable across pulls (must)
- Check: fetch the document, change something unrelated in your database, fetch again. Diff the
idof every team, athlete, entry, event, and division. - Pass: no id changed. Regenerated ids break the results join and are the single most common implementation error.
OMX-C3 — Ids are unique within their list (must)
- Check: for each of
teams,athletes,entries,events,divisions,groups,waves, andpublish, count distinct ids against list length. Count distinct teamcodes against the number of teams. - Pass: equal.
omx-check.mjschecks this. - Fixture:
c3-duplicate-ids.omx.json
OMX-C4 — References resolve (must)
- Check: for every
team_id,athlete_id,event_id,division_id,group_id,wave_id, and relay leg, look up the id in its list. In a results document, addmulti_event_id, each entry’sroundin its event’srounds,group_places[].group_id, every split’smarker_idin its event’smarkers, and everyteam_scoresteam, scope, and counted entry. Apartialresults document’s team scores may name teams and entries it does not carry. - Pass: every one is found. The schema cannot check this, so a validator run alone does not prove
it;
omx-check.mjsdoes. - Fixture:
c4-unresolved-reference.omx.json
OMX-C108 — Entries can be joined (must)
- Check: fetch a document with individual entries and relays, including two relays from one team in one event.
- Pass: athlete + event names one entry: no athlete is entered twice in one event. Every relay
entry carries
relay.letter, and team + letter + event names one relay entry. A Publisher joins a result back to your entry through these, because no entry carries anexternal_ids(OMX-R136). A results document keeps the same rule within each round, and every relay in it carries its letter. - Fixture:
c108-entered-twice.omx.json
OMX-C5 — HTTPS only (must)
- Check: confirm the document URL and every
publish[].urlbeginshttps://. - Pass: all do. Meet Operations refuses plain HTTP.
OMX-C6 — The URL is a revocable capability (must)
- Check: revoke the link in your own UI, then fetch it again.
- Pass: the fetch answers
410. Then confirm the meet director can mint a new one without losing the meet.
OMX-C7 — The URL secret is strong (must)
- Check: read the code that mints the document URL secret.
- Pass: it draws at least 128 bits from a cryptographically secure generator (22 or more base62 characters, not counting any prefix), and your logs redact it.
OMX-C8 — Conditional requests work (should)
- Check: fetch, keep the
ETag, fetch again withIf-None-Match. - Pass:
304with no body. Then change one entry and repeat. - Pass:
200with a newETag. Meet Operations may fetch your link more than once; a304costs nothing.
OMX-C9 — Browsers can fetch the document (must)
- Check: from a web page on another origin, send
OPTIONSand then aGETwithIf-None-Matchto the document URL. - Pass: both succeed. Responses carry
Access-Control-Allow-Origin: *and exposeETag, the preflight allowsIf-None-Match, and nothing relies on cookies.
OMX-C100 — The document URL answers without a redirect (must)
- Check: from a web page on another origin, fetch the document URL exactly as you give it out: the form in your copy-link button, emails, and help pages.
- Pass: the URL itself answers
200; nothing answers301,302,307, or308. A browser cannot follow a cross-origin redirect that lacks CORS headers, and Meet Operations sees that as a URL it cannot reach.
OMX-C10 — A scratch is a status, not an omission (must)
- Check: scratch an athlete in your product and fetch the document.
- Pass: the entry is still present with
status: "scr". An entry that vanished instead tells Meet Operations nothing: whether it treats the athlete as scratched is its own guess.
OMX-C11 — Marks are in the unit you measured (must)
- Check: inspect seeds for a track event, a field event measured in inches, and a multi-event.
- Pass:
ms,mm, andptvalues are integers;inhas at most two decimal places; no imperial mark was converted to metric before sending; and each mark’s unit is one its event’s code ranks by:msfor a race,mmorinfor a field event,ptfor a multi-event.
OMX-C12 — Age or birth date, never both (must)
- Check: fetch a document for a meet with age groups or an age-spanned division.
- Pass: no athlete carries both
ageandbirth_date. One whose age your product computes for this meet carriesage(OMX-R128).
OMX-C13 — Registration writes only what Registration writes (must)
- Check: fetch your document and look for fields Meet Operations writes.
- Pass: no entry carries
round,heat,lane,result,net_result, a place,group_places,qualified,flags,wind_mps,reaction_time_ms,points,multi_points,splits,field_series, orofficial_note, and none carries abib: the bib is the athlete’s. No event carriesroundsormarkers, the meet carries nooriginortrack,omxcarries noupdateorupdated_at, and there are noteam_scores. Meet Operations that meets one of these in a registration document still reads the meet: it ignores the field and tells the timer. - Fixture:
c13-registration-writes-results.omx.json: Meet Operations reads it and reports the stray fields;omx-check.mjsreports them as errors.
OMX-C14 — Every event has a code for the meet’s sport (must)
- Check: fetch a document with every event your product offers.
- Pass: every event’s
codeis one the spec defines for the meet’ssport, spelled exactly:SPand3000Sare track codes, notrunorxccodes. A race’s code is its advertised distance, not what the course measured: the Riverbend “5K”, which measures 4.92 km, is5000. Each distance has one code: a half marathon isHMARand a marathonMAR, never21097or13.1MILE. A multi-event (DEC,HEP,PENT) may be for any gender. A registration document lists no component events. An event your product holds that has no code for the sport is not in the document.
OMX-C15 — Multi-event entries go on the multi-event (must)
- Check: enter one athlete in a decathlon and fetch the document.
- Pass: the athlete has one entry, on the
DECevent, with any seed inpt. No entry points at a component event, and the document lists no component events: Meet Operations builds them.
OMX-C88 — Only values this version defines (must)
- Check: fetch a document with every sport your product offers and every value you write in an open list.
- Pass: each is a value the format defines, spelled as the format spells it. A value of your own
fails, because a later version may define it to mean something else (OMX-R129). The strict
schema reports it, and
omx-check.mjsalso reports an undefined sport or event code. - Fixture:
c88-bare-values.omx.json
OMX-C103 — Registration writes only its statuses, and no origin (must)
- Check: fetch a document with an entered, a scratched, and a wait-listed athlete, and one you hold as unconfirmed or checked in.
- Pass: every entry’s
statusise,scr, orwait, or absent, which meanse(OMX-R186); an unconfirmed entry is left out or sent ase, since Entered means an entrant, confirmed or not; andmeetcarries noorigin. Checked In and every later status are set at the meet. There is no Pending:pis not a status in this version (OMX-R151), and the reader schema refuses it.
OMX-C106 — Your system id, and never your own id in external_ids (must)
- Check: fetch a document whose athletes, teams, events, or meet carry ids on other systems.
- Pass:
omx.generator.idis your system id, a name you picked, and it is the same in every document you write. No record lists that id in itsexternal_ids: your own id for a record is itsid(OMX-R130). Ids on other systems sit under theirs, such as an athlete’s Athletic.net id underanet. No entry carriesexternal_idsat all (OMX-R136).omx-check.mjschecks both, for every kind.
B. Registration that names destinations
Add these when your document carries a publish array. Skip the whole group if it does not;
Registration with no destinations is a complete implementation.
OMX-C18 — Destination ids are stable and unique (must)
- Check: fetch twice and diff
publish[].id; count distinct ids against array length. - Pass: unchanged and unique. Meet Operations reports each destination’s status against its id, and a timer who fetches your link again should see the same destinations, not new ones.
OMX-C19 — Every destination has a label (should)
- Pass:
labelis present and reads as a name a meet director would recognise. Without it the timer is asked to approve a bare URL.
OMX-C20 — Credentials are separate (must)
- Check: compare each
publish[].tokenagainst the document URL secret and against each other. - Pass: all distinct. Holding the read link must not grant the right to write results, and one compromised destination must not implicate another.
OMX-C21 — deliver and content were chosen, not defaulted by accident (must)
- Check: for each destination, state which
deliver(final,progressive, orlive) and whichcontent(results, andstart_listsor not) it asks for, and why. - Pass: you can answer. The default is
finalwith results only, so a destination that wanted the meet as it unfolds, or heat sheets, and said nothing will appear broken: it sees nothing until every round is official, and never a heat sheet.
C. Publisher
Required of anyone that accepts results documents.
OMX-C22 — Accepts a document (must)
- Check:
POSTa validfullresults document with the right bearer token. - Pass:
200or204.
OMX-C23 — Browsers can hand off (must)
- Check: from a web page on another origin, send
OPTIONSand then aPOSTwithAuthorization,Content-Type: application/json, andContent-Encoding: gzipto your endpoint. - Pass: both succeed. The preflight allows
Authorization,Content-Type,Content-Encoding, andContent-Digest; responses carryAccess-Control-Allow-Origin: *and exposeRetry-After.
OMX-C101 — The push URL answers without a redirect (must)
- Check: send
OPTIONSand aPOSTto theurlyour destination names, exactly as written, from a web page on another origin. - Pass: the URL itself answers both; neither is redirected. A browser never redirects a
preflight, and drops
Authorizationwhen a redirect leaves the origin.
OMX-C24 — Rejects a bad credential (must)
- Check:
POSTwith a wrong token, then with one you have retired: expired by your own rule, or revoked by the meet director. - Pass:
401, then410. You decide how long a token works, and keep it working long enough for corrections after the meet; once it stops, you answer410(OMX-R164). Anything else leaves the timer without a usable message.
OMX-C125 — Answers the probe correctly, if it answers (may)
- Check: send
GETto your destinationurlwith the right bearer token, then with a wrong one, from a web page on another origin. - Pass: either you do not answer probes (
404or405), or the right token gets200with{ "accepts": [...], "generator": { "id", "name", "version"? } }, whereacceptslists the versions you read andgeneratornames you as your documents do, or503withRetry-Afterwhile you are not ready; and the wrong token gets401. The preflight allowsGET, the response carriesAccess-Control-Allow-Origin: *, and nothing you hold changes.
OMX-C25 — Tokens you issue are strong (must)
- Check: read the code that mints destination tokens.
- Pass: at least 128 bits from a cryptographically secure generator, compared in constant time, and redacted from logs.
OMX-C26 — Refuses a version it does not implement (must)
- Check:
POSTa document withomx.version: "9.0". - Pass:
422, with a message naming the version offered and the versions you accept, not a schema error: read the version before validating. Nothing you hold changes. - Fixture:
c26-unknown-version.omx.json
OMX-C99 — Says which versions it reads (should)
- Check:
POSTa document withomx.version: "9.0". - Pass: the
422body carriesaccepts, listing every version you read, such as["1.0"], so Meet Operations knows what you read. If your destination is named in apublishitem or a destination card,acceptsthere lists the same versions. - Fixture:
c26-unknown-version.omx.json
OMX-C27 — Explains an invalid document (should)
- Check:
POSTa document with an entry whoseresult.valueis not a number. - Pass:
400or422, witherrorsnaming the path, so the timer can pass it on. Nothing you hold changes. - Fixture:
c27-invalid-mark.omx.json
OMX-C28 — A repeat handoff is a no-op (must)
- Check: send the same document twice. Snapshot your stored state after each.
- Pass: identical. No duplicated entries, no doubled counts.
OMX-C29 — A full document supersedes everything before it (must)
- Check: send a
fulldocument, then a correctedfullone with a laterupdated_at, the sameomx.generator.id, and the samemeet.id. - Pass: what you hold matches the second document exactly, with nothing left over from the
first: a round, entry, event, team, or athlete the second lacks is gone. You key a meet on
omx.generator.idplusmeet.id, whichever credential it arrives on; a document with the samemeet.idunder anothergenerator.idis another product’s meet (OMX-C91).
OMX-C30 — An older document is refused (must)
- Check: send a document with
omx.updated_atof2026-10-03T14:41:52.418-04:00, then one with2026-10-03T14:40:00.000-04:00. Then send2026-10-03T18:41:52.418Z, the first instant written with a different offset. - Pass: the second answers
409and what you hold still matches the first. The third is treated as equal:200, no change. Comparing the strings as text fails this test. The rule is the same whatever each document’supdate. - Fixtures:
c30-first.omx.json,c30-older.omx.json, andc30-same-instant.omx.json, sent in that order
OMX-C31 — A missed handoff heals itself (must)
- Check: drop a handoff entirely, then accept the next one.
- Pass: you are fully correct. Nothing has to detect the gap or replay a log: a
partialdocument carries every round changed since the last document you accepted, and afullone carries the whole meet. If you lost what you accepted, you answer412and the next document isfull(OMX-C121).
OMX-C32 — Creates a meet it has never seen (must)
- Check: with no prior knowledge and no Registration anywhere, accept a
fulldocument. - Pass: you can create the meet (name, sport, date, time zone when given, venue, schedule, events, teams, athletes, entries) without asking anyone for anything.
OMX-C33 — Reads meet.origin (must)
- Check: accept a meet whose
origin.idis the document’s ownomx.generator.id: the timer built it. - Pass: you do not wait for, warn about, or error on missing Registration ids. Such a meet was built without Registration and never will have one.
OMX-C34 — Joins through athletes and events (must)
- Check: accept a document whose athletes, teams, and events carry your id in
external_ids, under your system id, or an id on another system you know, such asanet. - Pass: each athlete, team, and event is attached to your record by that id, never by name. An
entry carries no
external_ids: you find it through its athlete and its event, a relay through its team, letter, and event, and a multi-event component through its athlete and the component event, whosemulti_event_idleads to the multi-event.
OMX-C35 — Keeps entries with no Registration id (must)
- Check: accept a document built from a registration document (its
origin.idis Registration’s) containing a walk-up athlete the timer created, with an entry. - Pass: the entry appears, with its athlete’s own name, team, and bib. Dropping it is a failure: a dropped entry means incomplete results and a wrong team score, published as if correct.
OMX-C36 — Survives an athlete acquiring a Registration id (must)
- Check: accept an athlete with no Registration id, then accept a later document where that
same athlete
idcarries one inexternal_ids. - Pass: one athlete, updated. Two means your storage is keyed on the external id and will duplicate every walk-up the timer later merges against a roster.
OMX-C37 — Compares on result.value, not display (must)
- Check: ingest two results of 10.605 and 10.614, both displaying as “10.61”.
- Pass: you can tell them apart and order them correctly. A tie exists only where two entries in a round share a place, so comparing display strings invents ties that are not there.
OMX-C38 — Handles a non-placer that carries a result (must)
- Check: ingest an entry with
status: "dq"and a populatedresult. - Pass: the time or mark is retained and displayed, and no place is assigned. Whether an entry
places is decided by
statusalone, never by whetherresultis populated.
OMX-C40 — Invents no status codes (must)
- Check: ingest an entry with
status: "nr"in a race and one in a shot put, and adnfon a decathlon’s own entry. - Pass: the
nrentries are stored as No Result with no value, and rendered asNTandNM. The decathlete is shown as having abandoned, with no place in the standings. If you wroteNT,NH, orNMinto a status field, or renderedNRon a results sheet, that is a failure:NT,NM, andNHare hownrprints, not statuses.
OMX-C41 — Never recomputes (must)
- Check: accept a result set whose places or team scores you would compute differently, such as a tie or an XC score with displacers.
- Pass: you display the published values unchanged. Two entries in one round with the same place are tied; you print them as tied without a separate flag.
OMX-C42 — Ignores what it does not know (must)
- Check: accept a document containing an unknown optional field, a camelCase key, a
$schemakey, and a field prefixed with another product’s name (examplereg:season_id). - Pass: accepted. Never fail an ingest over an unrecognised field.
- Fixture:
c42-unknown-fields.omx.json
OMX-C43 — Treats null as absent (must)
- Check: accept a document where an entry has
"lane": nulland a finisher has"result": null. - Pass: accepted. The entry has no lane, and the finisher is a finisher with no time.
- Fixture:
c43-nulls.omx.json
OMX-C44 — Publishes a multi-event at both levels (should)
- Check: accept a results document with a scored heptathlon.
- Pass: you can show the standings from the multi-event entries, each component’s results from
its own event, and one athlete’s whole competition by grouping their component entries: the same
athlete, in the events whose
multi_event_idnames the heptathlon. A component entry’smulti_pointsis what it earned; itspoints, if any, are team points.
OMX-C45 — Keeps personal data private (must)
- Check: accept a document where an athlete has a
birth_date, anemail, aphone, andnotes, as a non-conforming writer might send. - Pass: nothing you publish shows any birth date, email, phone number, or note. A conforming results document carries none of them: it lists only the athletes its entries name, with the age Meet Operations placed them by.
OMX-C46 — Rate limiting says when to come back (should)
- Check: if you rate-limit, exceed the limit.
- Pass:
429with aRetry-Afterheader.
OMX-C82 — Accepts a large meet (must)
- Check:
POSTa valid document that is 64 MB uncompressed, once as is and once gzipped. The test page builds one. - Pass:
200or204both times. A413for a document below 64 MB uncompressed is a failure (OMX-R182).
OMX-C109 — Accepts a gzip body (must)
- Check:
POSTa valid document withContent-Encoding: gzipand the gzipped document as the body, then the same document uncompressed. - Pass: both are accepted and give the same stored state. Meet Operations may compress any handoff, or not (OMX-R140).
OMX-C83 — Gives the timer a destination card (should)
- Check: set up a destination in your product the way a meet director would.
- Pass: you show a destination card with a copy button, or offer it as a file, and it validates
against
destination-card-v1, with thedeliverandcontentyou want. Without one, a timer has to copy a URL and a token by hand.
OMX-C84 — Takes the event’s result from the final (must)
- Check: accept a document in which an event’s prelims are official with places and its final,
marked
final: true, isupcomingwith no entries. - Pass: you show no result for the event yet, and you do not present the prelim places as the event’s places. When the final is official, its places are the event’s. You never decide the final from a round’s number or label.
- Fixture:
c84-final-not-run.omx.json
OMX-C110 — Shows a mark in a unit it does not know (must)
- Check: accept a document with a result whose
unitis one this version does not define, with adisplay. - Pass: accepted. The result is shown by its
display, and is never compared, sorted, or converted. The places that came with it are shown as given (OMX-R141). - Fixture:
c110-unknown-unit.omx.json
OMX-C90 — Reads values from a later version safely (must)
- Check: accept a document carrying a round with
status: "provisional"andtiming: "laser". - Pass: accepted. The round is shown as not official and the timing as unknown. An unknown value in an open list never fails an import, and never makes a result look official.
- Fixture:
c90-later-values.omx.json
OMX-C91 — Keeps two Meet Operations products apart (must, if you are also Registration)
- Check: accept the cross country results example, then a document for the same registration
meet (the same Registration id in
meet.external_ids) under a differentomx.generator.idandmeet.id, from another Meet Operations product. - Pass: you hold both, and neither replaced the other; your operator is told the meet has a second Meet Operations product. Each is then replaced only by its own later documents.
- Fixture:
c91-second-timing-system.omx.json, sent afterxc-invitational-returned.json
OMX-C92 — Prints team scorers from the standing (should)
- Check: accept a cross country results document whose standings list their
entries. - Pass: a team’s line shows its scorers and its displacers as the standing lists them, each with
its entry’s own
points: “45 (3-7-9-12-14) [18-22]”, with no recomputation. A displacer’spointsis its scoring position.
OMX-C111 — Applies a partial document (must)
- Check: send
c30-first.omx.json, afulldocument, thenc111-partial.omx.json, a laterpartialone carrying one event’s round. - Pass: that round is replaced whole: its status, its heats, and every entry in it, an entry it
no longer lists gone. The team score blocks it carries are replaced by their
id. The meet, divisions, groups, waves, events, teams, and athletes it carries are added or updated byid(OMX-R180). Every other round, entry, event, team, and athlete is exactly asc30-firstleft it: apartialdocument deletes nothing outside its rounds.
OMX-C112 — Refuses an unknown update (must)
- Check:
POSTa results document whoseomx.updateis a value this version does not define. - Pass:
422witherrorsnaming/omx/update, and nothing you hold changes. Reading it asfullwould delete every round it does not carry. - Fixture:
c112-unknown-update.omx.json
OMX-C113 — Shows a cancelled round (must)
- Check: accept a document in which one event’s round is
cancelled, with no entries. - Pass: the event is shown as cancelled, not as “results to come”, and no result is shown for it.
- Fixture:
c113-cancelled-round.omx.json
OMX-C114 — Shows athletes still competing (should)
- Check: send
road-5k-returned.json, afulldocument, then accept a laterpartialdocument whose round isstarted, with finishers so far carrying theiroverall_placeand runners still on the course ass, some with splits. - Pass: the finishers are shown with their places, marked as able to move while the round
runs; the
srunners are shown as still competing, with any splits and no place; and the round is not shown as official. - Fixture:
c114-race-in-progress.omx.json
OMX-C120 — Shows an event whose code it does not know (must)
- Check: accept a results document with an event whose code this version does not define, such
as a later minor version’s
600Y, with placed entries, annrentry, and a team score block. - Pass: the event is shown by its
name, with places as given, marks bydisplay, thenrentry printed asNR, and its team scores as sent. Nothing in it is dropped or recomputed (OMX-R179). - Fixture:
c120-unknown-code.omx.json
OMX-C121 — Asks for the whole meet (must)
- Check: with nothing held for a meet, accept a
partialdocument for it; then accept afullone; then apartialagain. - Pass: the first
partialgets412, and nothing is stored from it; thefullgets200or204; the secondpartialis applied. A Publisher that restores a backup or refused afullwith413answers412to everypartialuntil afullarrives (OMX-R180). - Fixture:
c111-partial.omx.jsonandc30-first.omx.json
D. Meet Operations
Required of timing or meet-management software that reads registration documents and hands off results documents.
OMX-C50 — Builds a meet from a pasted URL (must)
- Check: paste a registration document URL into an empty install.
- Pass: venue, schedule, divisions, events, teams, athletes, and entries exist with no wizard and nothing typed twice. Fetching the same link again later returns the same kind of complete document; how you merge it is your own decision.
OMX-C105 — Leaves out an event whose code it does not know (must)
- Check: read a registration document with an event of code
SWIM, which this version does not define. - Pass: the rest of the meet is read, and that event and its entries are not: the timer is told which event was left out and why. This is Meet Operations’ rule; a Publisher shows such an event (OMX-C120). The event is never turned into one your product knows from its name. An event code is an open list, so a later minor version’s event never fails a whole meet (OMX-R132). A writer’s checker still reports the code, because a writer sends only codes this version defines.
- Fixture:
c105-undefined-code.omx.json
OMX-C104 — Keeps the ids other systems gave a record (must)
- Check: read a registration document whose meet carries
external_idswithmilesplitandanet, and whose athletes carryanetids and an id under a system your product does not know, such as"exampleresults": "77812". Then hand the meet off. - Pass: the results document carries every one of those ids, unchanged, on the same records, beside Registration’s own id under Registration’s system id, whether or not your product knows the system (OMX-R142). A Publisher keyed on any of them can match its records. The timer may edit or remove one.
OMX-C102 — Refuses a results document (must)
- Check: offer a document whose
omx.kindis"results", with results and without. Then offer a registration document. - Pass: both results documents are refused before anything is created or changed, and the timer
is told that a meet is moved between computers with your product’s own backup. The registration
document reads as usual. Meet Operations takes only Registration’s handoff; results only ever go
forward, to a Publisher. The kind is read from
omx.kind, never worked out from the document’s fields (OMX-R135). - Fixtures:
c102-results-document.omx.json, refused; the cross country registration example reads as usual
OMX-C51 — Reads a document from a file (should)
- Check: open a document saved as
meet.omx.json, and the same document saved asmeet.json. - Pass: both build the same meet as fetching it from a URL would.
OMX-C52 — Refuses plain HTTP (must)
- Pass: an
http://document or destination URL is rejected with a clear message.
OMX-C53 — Refuses an unknown major, reads a later minor (must)
- Check: offer a document with
omx.version: "9.0", one carrying a draft number such as"0.9", and one carrying a later minor of the major you implement, such as"1.7". - Pass: the first two refused, naming the version offered and the versions you implement. Importing what you half-understand is worse than failing. The later minor is read: a minor only adds, so you ignore what you do not know (OMX-R175).
- Fixtures:
c53-unknown-draft.omx.json,c53-later-minor.omx.json
OMX-C123 — One malformed value never fails a whole meet (must)
- Check: read a registration document whose athlete carries
country: "JPN", whose team code is 20 characters, and whose destination’stokenis 12 characters. - Pass: the meet is read, and the timer is warned about each value. The writer’s own checks still fail the document: the strict schema keeps every format (OMX-R181).
- Fixture:
c123-malformed-values.omx.json
OMX-C55 — Keeps the meet when its link is revoked (must)
- Check: set up from a document URL, then have Registration revoke it and fetch the link again.
- Pass: the
410is shown to the timer as a link that no longer works, and the meet, its results, and its destinations are all still there.
OMX-C61 — A multi-event is built, never guessed (must)
- Check: read a document with a
HEPevent with genderMin atf-outdoormeet, which the spec’s default table does not list. - Pass: you build its component events by your own rules for that meet, or, if you have none, you report the event to the timer and leave it out. You never guess its events from its name. The default table is where you start, not a limit on what Registration may send (OMX-R139).
- Fixture:
c61-multi-off-table.omx.json
OMX-C63 — Exhibition athletes never score (must)
- Check: score an event in which an
exhibition: trueathlete finishes between two scorers. - Pass: the exhibition athlete holds a place and any award, scores nothing, and the next scorer is not pushed down a scoring slot.
OMX-C124 — Writes team scores (must)
- Check: score a cross country race with one complete team and one too small to score, and a track meet with an event’s score and the meet’s total; hand off twice.
- Pass: every
team_scoresblock has anid, unique in the document and the same in both handoffs. A sprinter’spointsis one value, which the event’s block and the meet’s total both use. The incomplete team is listed withcomplete: falseand noplace,points, orentries; its runners carry nopoints, and the other team’s scoring positions skip them (OMX-R178).omx-check.mjschecks the incomplete team.
OMX-C64 — Every handoff stands on its own (must)
- Check: capture what you send to a destination at three points in a meet.
- Pass: each
fulldocument is a whole meet document. Eachpartialdocument carries each of its rounds whole, with every athlete and team its entries name, so it makes sense applied to the last document the Publisher accepted, whichever that was. If any of them needs a document the Publisher may have missed, that is a failure.
OMX-C65 — A handoff carries no secrets or private data (must)
- Check: set up from a document naming two destinations, with an athlete carrying a birth date, an email, a phone, notes, and a chip, a meet and an event carrying notes, and an extra field of Registration’s own on an athlete. Hand off.
- Pass: no handoff contains a
publisharray, a token, any athleteemail,phone,notes,birth_date, orchip, the meet’s or an event’snotes, or Registration’s extra field. The athlete carries the age you placed them by, and the document lists only the athletes and teams its entries name: a scratched athlete with no other entry is not in it.
OMX-C66 — meet.origin is honest (must)
- Check: hand off a meet built from a registration document, and one the timer built from nothing.
- Pass:
omx.generator.idis your system id. The first meet’soriginis Registration’s{ id, name }and the second’s is your own. The first carries your own ids inidand Registration’s id unchanged inexternal_ids, under Registration’s system id, on the meet and every event, team, and athlete that came from Registration, and on nothing else (OMX-R134, OMX-R136): no entry carriesexternal_ids. Neither lists your own system id inexternal_ids(OMX-C106).
OMX-C67 — Publishes stored values (must)
- Check: hand off a race whose event displays hundredths, with two runners whose stored times differ only in thousandths.
- Pass:
result.valueis the stored thousandths value,displayis the hundredths string, and the two hold different places. A tie is two entries with the same place, decided at stored value; there is notiedflag and noprecision.
OMX-C68 — Does not re-encode statuses (must)
- Check: hand off a DQ that has a recorded time, a DNS, a thrower with three fouls, a finisher placed by the finish order with no time, and a decathlete who abandoned after seven events.
- Pass:
dqwithresultpopulated and no place;dnsandnrwith noresultand no place; the finisher with itsoverall_place, no status, and noresultkey; every entry with exactly one ofstatusandoverall_place(OMX-R177); the decathlete’s own entrydnfwith no place, and their seven component entries standing.dnfappears only in a race or on a multi-event’s own entry, andfsonly in a timed track event. No invented status codes anywhere in the document.
OMX-C69 — Omits the unknown, never writes null (must)
- Check: hand off a meet with an unlaned race, a finisher placed with no time, and a field event.
- Pass: no key in the document has the value
null. The unlaned entries have nolanekey, and the finisher carries itsoverall_place, nostatus, and noresultkey (OMX-R177).
OMX-C70 — Gun and net times stay separate (must)
- Check: hand off a road race with a start mat, placed overall by gun time and in age groups by net time.
- Pass: every finisher’s
resultis the gun time andnet_resultthe net time, and the round carriesplace_bywithoverall: "gun"andgroups: "net". Gender places follow the same clock as overall places.
OMX-C71 — Carries what it knows about how the result came to be (should)
- Check: hand off a prelims-and-finals track event, a hand-timed race, a horizontal jump, and a high jump.
- Pass: each prelim entry that advanced carries
qualifiedasQ,q,qD,qR, orqJ, the letter its result prints; every round carriestiming; each race heat’s wind is on the heat aswind_mps, and a field entry’swind_mpsis the wind of the mark that counted; each horizontal jump attempt infield_seriescarriesoutcomefromo,x,-,r, itsmarkwhen valid, and its ownwind_mpswhere one was read; each high jump attempt carries the bar height asmarkwhether it was cleared or missed. A product that does not record one of these omits it rather than guessing.
OMX-C72 — One entry per round (must)
- Check: hand off a track event with a prelim and a final.
- Pass: an athlete who ran both appears as two entries with two ids,
round: 1andround: 2, and neither carriesexternal_ids: a Publisher finds both through the athlete and the event. The prelim entry carriesqualified; the final carries the place. No entry is ever overwritten by a later round.
OMX-C73 — Multi-events come back at both levels (must)
- Check: read a document that lists a heptathlon with no component events, score it, and hand
it off in a
fulldocument. - Pass: the document lists every component event actually contested, each with
multi_event_id, built by your rules (the spec’s table is the default). Each athlete’s multi-event entry carries the points total asresultinptandoverall_place; each of their component entries carriesresultandmulti_points. The component events, which you built, carry no Registration id.
OMX-C74 — Every handoff is ordered (must)
- Check: hand off a meet twice with no change in between. Change one result and hand off. Delete one entry and hand off. Set the computer’s clock back an hour, change a result, and hand off.
- Pass: every document carries
omx.updated_atwith milliseconds. The first two are equal and every later one is strictly later than the one before, including after the clock change.
OMX-C75 — Offers a cadence, preselecting the preference (must)
- Check: add destinations whose
deliverisfinal,progressive, andlive, and one whosecontentincludesstart_lists. - Pass: the timer is offered when handoffs happen, with each destination’s
deliverandcontentpreselected and overridable. Afinaldestination receivesfulldocuments only. The format sets no cadence beyond that; you do.
OMX-C76 — Destinations are independent (must)
- Check: make one destination return
401for a whole meet. - Pass: the other keeps receiving documents, and the timer is shown per-destination status naming the failing one.
OMX-C77 — A timer can add a destination (must)
- Check: with no incoming document at all, add a destination by hand.
- Pass: it receives handoffs. Without this, Publisher-only products are unreachable.
OMX-C79 — Retries only what can succeed (must)
- Check: point a destination at a test endpoint that answers
503, then429withRetry-After: 30, then400,401,404,409,410,413, and422in turn. While a retry is pending, change a result. - Pass: the
503is retried with backoff and the429no sooner than 30 seconds. The others are not retried and are shown to the timer against that destination, with anyerrorsthe body carried. The retry that finally goes out is built when it is sent, so it carries the changed result (OMX-C116).
OMX-C85 — Accepts a destination card (must)
- Check: paste a destination card into a meet, then open the same card as a file.
- Pass: each adds the destination as one the timer added by hand, approved by the act of adding
it, with the card’s
deliverandcontentpreselected, and the meet’s handoffs reach it. Pasting the same card again changes that destination rather than adding a second. A card whoseurlishttp://is refused. - Fixture:
c85-destination-card.omx-destination.json
OMX-C86 — Marks the final (must)
- Check: hand off a prelims-and-finals event before the final runs, and again after it.
- Pass: the final round carries
final: trueboth times,upcomingbefore it runs, and no other round does; a single-round event’s round carries it too. Before the final is run, the prelim entries’overall_placeis their place across the prelims.
OMX-C87 — Every handoff passes the strict schema (must)
- Check: capture a
fulland apartialhandoff and validate each againstresults-document-v1, then runnode omx-check.mjson it. Note each request’sContent-Encoding. - Pass: zero errors from both. Each body is the document itself, sent uncompressed or with
Content-Encoding: gzip, and nothing else.
OMX-C94 — Adaptive events stay separate (must)
- Check: read a document with a women’s shot put and a women’s shot put carrying
adaptive: { "mode": "seated" }. - Pass: two events. If your product cannot hold an adaptive event, the operator is told and the seated event is not imported; it is never merged into the standard shot put.
OMX-C96 — Splits say where they were taken (must)
- Check: hand off a road race with two split mats, and a relay with leg splits.
- Pass: the race’s event lists its
markers, and every split names amarker_idfrom that list; each relay leg split names itsleg. Every split carries one number,time_ms, elapsed from the same start as the entry’sresult: a relay leg’s split is the running clock when that leg finished. A runner who missed a mat has no split there, and their other splits still name their markers.
OMX-C97 — Writes gender as stated (must)
- Check: hand off a road race open to everyone, a mixed relay, an athlete who did not state a gender, a non-binary athlete Registration entered in a boys’ race, and a co-ed school team scored separately for boys and girls.
- Pass: the race and the relay are both
C. The athlete with no gender carries nogenderand nogender_place, and nothing guessed one. The non-binary athlete keeps the entry Registration gave them and is placed within their own gender. The boys’ and girls’ scores are separateteam_scoresblocks, each naming itsscope.gender; standings that count every gender together nameC, and no block leavesscope.genderout.
OMX-C115 — A partial document carries whole rounds (must)
- Check: while a meet runs, capture a
partialhandoff that carries one event’s round, and one that carries a decathlon component’s round. - Pass: each round is whole: its status, timing, heats, and every entry in it. Each carries the
round’s event with only that round, every athlete, team, division, group, and wave its entries
name, the event-scope team scores of its events, and every division- and meet-scope team score
whole, each block with the same
idit had in the last document. The component’s document also carries the decathlon and the decathlon’s round, because the standing changes with every component. Nopartialdocument is used to remove anything.
OMX-C116 — One send at a time, each built when it is sent (must)
- Check: for one destination, change three rounds in quick succession while the endpoint is slow, make the endpoint fail one send, then change a fourth round. Separately, hand off from a second copy of the meet (a laptop swapped mid-meet), and remove an athlete.
- Pass: at most one document is in flight to a destination at a time. Every send, a retry
included, is built at the moment it goes out and carries every round changed since the
Publisher last accepted a document, so the failed send’s rounds arrive in the next. The first
document any copy of the meet sends to a destination is
full, and so is every document after anything is removed: an event, round, team, athlete, division, group, wave, or team score block.
OMX-C122 — Sends the whole meet when asked (must)
- Check: have a
progressivedestination answer the nextpartialwith412, then answer200. Separately, have it answer a status the response table does not list, such as418, and then599. - Pass: after the
412the next document, sent at once, isfull, and later ones arepartialagain. The418is not retried and the timer is told, as for400; the599is retried with backoff, as for5xx(OMX-R180).
OMX-C117 — Entry statuses fit their round (must)
- Check: hand off a meet with an
upcominground, astartedone, a finished one (unofficial,under_review, orofficial), and acancelledone. - Pass: an
upcominground holds onlyeentries, and only for a destination that asked for start lists. Astartedround holdse,s, placed entries (their places so far), anddnf,dns,dq,fs,nr. A finished round holds placed entries anddnf,dns,dq,fs,nronly. No entry carries both a status and anoverall_place, and none carries neither. Acancelledround holds no entries. No results entry isci,scr, orwait: a scratched or wait-listed athlete has no results entry.omx-check.mjschecks this.
OMX-C118 — Heat sheets only for a destination that asks (must)
- Check: add two destinations, one whose
contentincludesstart_listsand one without, then seed a round that has not run. - Pass: the first receives the round with an
eentry for each athlete, carryingheat,lane,seed, andwave_idwhere it has them. The second receives the round,upcoming, with no entries.
OMX-C119 — Writes a cancelled round (must)
- Check: cancel an event after its heats are drawn, and hand off.
- Pass: the event’s round is
cancelled, with no entries, and the event stays listed: in the nextfulldocument and in apartialone carrying that round.
These tests are dedicated to the public domain under CC0 1.0: copy them into your own test suite.
