What you will build.
Two fictional gates inspect ticket DEMO-TICKET-01 for event DEMO-EVENT-A. Ownership is current, but the admission service may already have redeemed the ticket. Work through the distinction between owning an entitlement and being admitted once.
Your working record
Create a private worksheet with columns for the source reference, observed state, responsible system, observation time, expected result and unresolved issue. Use the same fictional case throughout the steps; do not paste live personal or financial records into this exercise.
Run the example before changing it.
Inspect the baseline record below. Download and read the complete JavaScript file before running it. It uses only Node’s built-in test and assertion modules; there is no package installation, account connection or secret to configure.
{
"eventMatches": true,
"ownerVerified": true,
"alreadyRedeemed": false
}Save it as event-access-and-nft-ticketing.mjs. Open a terminal in that folder and run:
node --version
node --test event-access-and-nft-ticketing.mjsExpected result: four passing tests and zero failures. These tests verify the teaching classifier only. They do not verify a production integration, security boundary or financial outcome.
File or test runner will not start?
Check that Node.js 22 or later is installed, the filename ends in .mjs rather than .mjs.txt, and your terminal is in the download folder. If a managed browser blocks scripts, open “Read the full exercise” and save that text locally under the same filename. Do not disable organizational security controls.
Read the full exercise
// Hybrid-Chain offline teaching model: event-access-and-nft-ticketing
// Fictional fields/states; NOT an API client, authorization system or production implementation.
// No network calls, credentials, payments or file writes. Review before running.
// Requires Node.js 22+. Run: node --test event-access-and-nft-ticketing.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
export function inspect(record) {
if (!record.eventMatches) return 'WRONG_EVENT';
if (!record.ownerVerified) return 'OWNERSHIP_REVIEW_REQUIRED';
if (record.alreadyRedeemed) return 'ALREADY_ADMITTED';
return 'READY_FOR_ADMISSION_SERVICE_CHECK';
}
const baseline = {
"eventMatches": true,
"ownerVerified": true,
"alreadyRedeemed": false
};
const cases = [
{
"name": "eligible for separate atomic check",
"patch": {},
"expected": "READY_FOR_ADMISSION_SERVICE_CHECK"
},
{
"name": "another event",
"patch": {
"eventMatches": false
},
"expected": "WRONG_EVENT"
},
{
"name": "ownership unresolved",
"patch": {
"ownerVerified": false
},
"expected": "OWNERSHIP_REVIEW_REQUIRED"
},
{
"name": "already used ticket",
"patch": {
"alreadyRedeemed": true
},
"expected": "ALREADY_ADMITTED"
}
];
for (const example of cases) {
test(example.name, () => {
assert.equal(inspect({...baseline, ...example.patch}), example.expected);
});
}
Know what the tests demonstrate
- eligible for separate atomic check
READY_FOR_ADMISSION_SERVICE_CHECK- another event
WRONG_EVENT- ownership unresolved
OWNERSHIP_REVIEW_REQUIRED- already used ticket
ALREADY_ADMITTED
Now change one fixture at a time. If you change a case’s input without changing its expected result, the test should fail. Read the actual-versus-expected difference and use the walkthrough to decide whether your rule or your expectation needs correcting. Keep an unchanged copy for comparison.
Work through the case, step by step.
- 01
Bind the check to the event
Do this
- Write DEMO-EVENT-A and DEMO-TICKET-01 together in the admission worksheet.
- Record the authenticated gate identity as a separate implementation requirement.
- Set eventMatches=false in the fixture and confirm WRONG_EVENT even when ownership is valid.
Expected resultAn entitlement for another event cannot qualify merely because the attendee owns it.
If your result is different
If the QR code contains only a wallet address, add an event/ticket binding before designing admission.
- 02
Check current ownership and eligibility
Do this
- Run the baseline, then set ownerVerified=false and expect OWNERSHIP_REVIEW_REQUIRED.
- List the actual source of ownership and the organizer's transfer/refund cutoffs.
- Keep private identity data outside public asset metadata and admission evidence.
Expected resultThe worksheet separates current ownership from event-specific permission to use the ticket.
If your result is different
Do not use an old screenshot of ownership as current authority; check the actual supported source and freshness requirements.
- 03
Model a previously used ticket
Do this
- Set alreadyRedeemed=true and expect ALREADY_ADMITTED.
- Write the real service requirement: one durable atomic transition keyed by event and ticket.
- Specify a test that submits two simultaneous gate requests and permits at most one successful redemption.
Expected resultYou have a concurrency acceptance test rather than a misleading claim that a read-then-write check prevents double admission.
If your result is different
The local model tests classification only. Its boolean cannot protect two real gates from a race.
- 04
Define the offline and recovery path
Do this
- Add a manual case where the gate loses connectivity after submitting admission.
- Require reconciliation of the existing redemption before retrying; label offline observations provisional under the organizer's policy.
- Save the four tests and the concurrency specification for the admission-service implementation.
Expected resultThe gate design has an explicit uncertain-result path and does not silently turn offline scans into confirmed admission.
If your result is different
Do not waive event eligibility or duplicate checks just to make the demo proceed. Escalate through the organizer's defined exception process.
Review what you produced.
Save the worksheet, unmodified test output, one deliberately failing case and the explanation of its fix. A useful result includes an unresolved item with an owner; it is not just a screenshot of four green tests.
Map the model to actual systems.
The organizer owns event terms, ticket eligibility, gate authentication, atomic redemption, refunds, transfer cutoffs, and offline procedures.
For each worksheet field, name the owning system, exact current operation, permission and error behavior. Put “not available” next to any missing operation and assign a dependency owner. Do not substitute the teaching classifier for that missing integration.
Open the current API reference and inspect each operation’s request, response, scope and implementation status. Code examples on this page are local models only; they do not call those operations.
Take the next technical step.
Use your worksheet and failing-case notes in the next walkthrough. Keep the same reference trail so you can see which assumptions become actual observations—and which remain dependencies.
Trace purchase and ticket delivery