Skip to content

Commit 485138e

Browse files
committed
test(spec): take down the scope fence the logo card pinned, and say why
Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
1 parent c57a951 commit 485138e

1 file changed

Lines changed: 69 additions & 16 deletions

File tree

packages/spec/src/identity/organization.test.ts

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -177,33 +177,86 @@ describe('[#18509] OrganizationSchema.logo accept set', () => {
177177
});
178178

179179
/**
180-
* ⛔ Scope fence, deliberately pinned as CURRENT behaviour rather than fixed:
181-
* the same measurement found `metadata` served present-and-null and
182-
* `/auth/organization/create` omitting the required `updatedAt`. Those are
183-
* separate defects, filed separately — #18509 asked about `logo`. This pin
184-
* exists so that the fence is visible and so that a later fix for either one
185-
* has to come here and say so.
180+
* ⭐ [#18728] The scope fence this block used to pin is DOWN, and #18509's own
181+
* pin asked whoever took it down to come here and say so. Saying so:
182+
*
183+
* The fence pinned two refusals as current behaviour — `metadata` served
184+
* present-and-null, and `updatedAt` absent while declared required — and
185+
* maintainer ruling C (batch #158 item 4) closed each at a different end:
186+
*
187+
* - `updatedAt` is now `.optional()` on this schema, which is the ruling's
188+
* own fallback A: the wire is better-auth's serializer and its documented
189+
* organization model declares no such field, so the schema aligns to the
190+
* documented wire rather than the producer inventing a value.
191+
* - `metadata` was fixed at the PRODUCER, not here. plugin-auth's data
192+
* adapter decodes `sys_organization.metadata` from its stored JSON text on
193+
* its read verbs and OMITS the key when the column is unset, so the served
194+
* body now carries an object or nothing — never `null`.
195+
*
196+
* So a served body parses whole, and the `null` this schema still refuses is
197+
* a shape nothing sends any more. Both halves are pinned below, because
198+
* "accepts the served body" and "stopped checking" are otherwise the same
199+
* green.
186200
*/
187-
it('does NOT (yet) accept a served body wholemetadata/updatedAt are separate cards', () => {
201+
it('[#18728] accepts a served read-route body WHOLE — updatedAt absent, metadata decoded', () => {
188202
const served = {
189203
id: 'org_123',
190204
name: 'Acme Corporation',
191205
slug: 'acme-corp',
192206
logo: null,
193-
metadata: null,
207+
metadata: { plan: 'pro' },
194208
createdAt: '2026-01-01T00:00:00.000Z',
195-
// `updatedAt` absent, exactly as `/auth/organization/create` serves it
209+
// `updatedAt` absent, exactly as every route of this family serves it
196210
};
197211
const result = OrganizationSchema.safeParse(served);
198-
expect(result.success).toBe(false);
199-
if (!result.success) {
200-
// `logo` is gone from this list — that is this card's contribution.
201-
expect(result.error.issues.map((i) => i.path.join('.')).sort()).toEqual([
202-
'metadata',
203-
'updatedAt',
204-
]);
212+
expect(result.error?.issues.map((i) => i.path.join('.')) ?? []).toEqual([]);
213+
expect(result.success).toBe(true);
214+
});
215+
216+
it('[#18728] accepts the same body with metadata OMITTED — an unset column', () => {
217+
const { metadata: _unset, ...withoutMetadata } = {
218+
id: 'org_123',
219+
name: 'Acme Corporation',
220+
slug: 'acme-corp',
221+
logo: null,
222+
metadata: { plan: 'pro' },
223+
createdAt: '2026-01-01T00:00:00.000Z',
224+
};
225+
const result = OrganizationSchema.safeParse(withoutMetadata);
226+
expect(result.success).toBe(true);
227+
});
228+
229+
it('⭐ [#18728] still REFUSES metadata as null or as the stored JSON text', () => {
230+
// The producer omits an unset column and decodes a set one, so neither of
231+
// these is a shape any route sends. They must stay refused: if either ever
232+
// parses, the producer has regressed or this schema has been loosened to
233+
// hide the regression.
234+
for (const wrong of [null, '{"plan":"pro"}']) {
235+
const result = OrganizationSchema.safeParse({
236+
id: 'org_123',
237+
name: 'Acme Corporation',
238+
slug: 'acme-corp',
239+
logo: null,
240+
metadata: wrong,
241+
createdAt: '2026-01-01T00:00:00.000Z',
242+
});
243+
expect(result.success).toBe(false);
244+
expect(result.error?.issues.map((i) => i.path.join('.'))).toEqual(['metadata']);
205245
}
206246
});
247+
248+
it('⭐ [#18728] `.optional()` widened updatedAt by ABSENCE only — a present value is still a datetime', () => {
249+
const result = OrganizationSchema.safeParse({
250+
id: 'org_123',
251+
name: 'Acme Corporation',
252+
slug: 'acme-corp',
253+
logo: null,
254+
createdAt: '2026-01-01T00:00:00.000Z',
255+
updatedAt: 'whenever',
256+
});
257+
expect(result.success).toBe(false);
258+
expect(result.error?.issues.map((i) => i.path.join('.'))).toEqual(['updatedAt']);
259+
});
207260
});
208261

209262
describe('MemberSchema', () => {

0 commit comments

Comments
 (0)