|
39 | 39 | * into the request, so a later "helpful" translation layer could add or rename |
40 | 40 | * members without changing a type and without changing a status. ④ therefore |
41 | 41 | * holds the request bytes to FULL-STRING equality — never `toContain`, which a |
42 | | - * body carrying extra members would satisfy — and it is the guard that the |
43 | | - * default is applied by SUBSTITUTION, not by clobbering: a spread written the |
44 | | - * other way round (`{ role: 'member', ...req }`) passes ① and ③ and fails ② |
45 | | - * and ④. |
| 42 | + * body carrying extra members would satisfy. |
| 43 | + * |
| 44 | + * It is also the guard that the default is applied by SUBSTITUTION rather than |
| 45 | + * by ordering. The tempting other spelling, `{ role: 'member', ...req }`, agrees |
| 46 | + * with the shipped one on every status cases ① – ③ can observe, and differs in |
| 47 | + * exactly two places ④ can: it re-orders the body, and — because a spread |
| 48 | + * copies an explicitly-`undefined` member over the default while `??` does not |
| 49 | + * — it puts a caller's `role: undefined` back on the wire as no `role` at all, |
| 50 | + * restoring the 400 for the caller who wrote `invite({ email, role: maybe })`. |
| 51 | + * ④c is that case. |
46 | 52 | * |
47 | 53 | * ## ⑤ pins the shape of the fix, not just its effect |
48 | 54 | * |
@@ -269,6 +275,23 @@ describe('#16582 the bytes organizations.invite puts on the wire', () => { |
269 | 275 | JSON.stringify({ email: 'probe@example.com', role: 'admin', organizationId: 'org_probe' }), |
270 | 276 | ); |
271 | 277 | }); |
| 278 | + |
| 279 | + it('④c an explicitly-undefined role is the same call as omitting it', async () => { |
| 280 | + const { client, fetchMock } = capturing(); |
| 281 | + |
| 282 | + // What `invite({ email, role: maybeRole })` compiles to when the variable |
| 283 | + // is empty — indistinguishable from omission to the caller, and it must be |
| 284 | + // indistinguishable on the wire too. |
| 285 | + await client.organizations.invite({ |
| 286 | + email: 'probe@example.com', |
| 287 | + role: undefined, |
| 288 | + organizationId: 'org_probe', |
| 289 | + }); |
| 290 | + |
| 291 | + expect(soleBody(fetchMock)).toBe( |
| 292 | + JSON.stringify({ email: 'probe@example.com', role: 'member', organizationId: 'org_probe' }), |
| 293 | + ); |
| 294 | + }); |
272 | 295 | }); |
273 | 296 |
|
274 | 297 | // ───────────────────────────────────────────────────────────────────────── |
|
0 commit comments