|
2 | 2 | I created the boiler plate code, basic CI and deployment at the very beggining off the project (AP-3, AP-2, AP-8). |
3 | 3 | The idea is to have as soon as possible a "Hello world". In this context that would be a deployed sveltkit app that can read and write a value on Airtable. |
4 | 4 |
|
5 | | -# Testing from the very beginning |
| 5 | +# Testing Strategy |
6 | 6 |
|
7 | | -I created a test script (`scripts/test-airtable.ts`) to verify read and write access to Airtable before building any features. This script uses dotenv to load credentials from `.env.local` and tests creating and reading records from a dedicated test table. |
| 7 | +We use Vitest for automated testing with a co-location pattern: test files (`.spec.ts`) live next to the source files they test. This makes tests easy to find and encourages test coverage. |
8 | 8 |
|
9 | | -The project uses Vitest with Playwright for browser-based component testing, with Playwright browsers installed automatically via a postinstall hook. At the moment test is boilerplate, so we are not really testing any real functionality |
| 9 | +**Structure:** |
| 10 | +- `src/lib/**/*.spec.ts` - Unit tests for library code |
| 11 | +- `src/routes/**/*.spec.ts` - Component tests for pages |
| 12 | + |
| 13 | +**Manual integration scripts** live in `scripts/` and are used for verifying real API connectivity during development: |
| 14 | +- `scripts/check-airtable-connection.ts` - Verify Airtable read/write access |
| 15 | +- `scripts/check-cohort-apprentices.ts` - Test cohort lookup functionality |
| 16 | +- `scripts/fetch-schema.ts` - Fetch and document Airtable schema |
| 17 | + |
| 18 | +This separation keeps automated tests (fast, mocked, run in CI) distinct from manual integration checks (slow, real API, run during development). [P6 - 30%] [P7 - 30%] |
10 | 19 |
|
11 | 20 |
|
12 | 21 | # Importing Airtable schema |
@@ -70,5 +79,29 @@ During Sprint 02, I identified that certain tasks required permissions I did not |
70 | 79 |
|
71 | 80 | This approach improved visibility into the project status and helped me prioritise effectively, focusing on tasks within my control while maintaining a clear follow-up list for delegated items. [P1 - 50%] [P2 - 40%] [K2, K6] |
72 | 81 |
|
| 82 | +# Authentication |
| 83 | +We use jsonwebtoken library to authenticate learners and staff. This allows us to: |
| 84 | + * Generate the magic link token |
| 85 | + * Verify the token |
| 86 | + |
| 87 | +**API testing:** We use Postman to manually test the authentication endpoints during development. This allows us to verify request/response payloads and debug the auth flow before building the frontend. |
| 88 | + |
| 89 | +## Role-based Access Control |
| 90 | + |
| 91 | +The `findUserByEmail` function implements role-based authentication by checking two Airtable tables: |
| 92 | + |
| 93 | +1. **Staff table** - Returns `type: 'staff'` for admin access |
| 94 | +2. **Apprentices table** - Returns `type: 'student'` for learner access |
73 | 95 |
|
| 96 | +A technical challenge arose with the Staff table: the email is stored in a `singleCollaborator` field (an Airtable collaborator object with `{ id, email, name }`). Unlike regular text fields, collaborator fields cannot be filtered using Airtable's `filterByFormula`. The solution was to fetch all staff records and iterate through them in code, comparing emails case-insensitively: |
| 97 | + |
| 98 | +```typescript |
| 99 | +for (const record of staffRecords) { |
| 100 | + const collaborator = record.get(STAFF_FIELDS.COLLABORATOR); |
| 101 | + if (collaborator?.email?.toLowerCase() === email.toLowerCase()) { |
| 102 | + return { type: 'staff' }; |
| 103 | + } |
| 104 | +} |
| 105 | +``` |
74 | 106 |
|
| 107 | +This approach ensures staff members can authenticate regardless of how their email is capitalised in Airtable. [K2 - 40%] [S5 - 30%] [D2 - 20%] |
0 commit comments