@@ -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 whole — metadata/ 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
209262describe ( 'MemberSchema' , ( ) => {
0 commit comments