@@ -213,7 +213,13 @@ public void testAttrsInAlphabeticalOrder() {
213213 }
214214
215215 // -----------------------------------------------------------------------
216- // Test 7 — extends emitted when super data is set
216+ // Test 7 — extends emitted when super data is set (no authored ref → FQN)
217+ //
218+ // Cross-port contract (matches TS / C# / Python): the serializer echoes the
219+ // AUTHORED `extends` string verbatim. A programmatically-built tree that sets
220+ // super via setSuperData() (never the parser) carries NO authored ref, so the
221+ // serializer falls back to the resolved super FQN. The serializer must never
222+ // recompute a short-vs-FQN form — that was the latent divergence (#37).
217223 // -----------------------------------------------------------------------
218224 @ Test
219225 public void testExtendsEmittedWhenSuperDataSet () {
@@ -229,7 +235,46 @@ public void testExtendsEmittedWhenSuperDataSet() {
229235
230236 String json = CanonicalJsonSerializer .canonicalSerialize (root );
231237
232- assertTrue ("expected extends key with short name, got: " + json , json .contains ("\" extends\" : \" BaseEntity\" " ));
238+ // No authored ref on this programmatic tree → fall back to resolved FQN.
239+ assertTrue ("expected extends key with resolved FQN, got: " + json ,
240+ json .contains ("\" extends\" : \" test::pkg::BaseEntity\" " ));
241+ }
242+
243+ // -----------------------------------------------------------------------
244+ // Test 7b — extends echoes the AUTHORED ref VERBATIM (cross-port contract)
245+ //
246+ // When the authored `extends` string is preserved (as the canonical parser
247+ // does via setAuthoredSuperRef), the serializer re-emits it byte-for-byte —
248+ // regardless of whether the super lives in the same or a different package.
249+ // This is the contract gated by the flattened-kitchen-sink fixture's
250+ // same-package-FQN extends; here we assert it directly on a synthetic tree.
251+ // -----------------------------------------------------------------------
252+ @ Test
253+ public void testExtendsEchoesAuthoredRefVerbatim () {
254+ MetaRoot root = new MetaRoot ("test::pkg" );
255+
256+ ValueMetaObject base = ValueMetaObject .create ("test::pkg::Base" );
257+ base .addMetaAttr (BooleanAttribute .create (MetaData .ATTR_IS_ABSTRACT , true ));
258+ root .addChild (base );
259+
260+ // Same package as the super, but authored as a FULL FQN — must echo FQN.
261+ ValueMetaObject derivedFqn = ValueMetaObject .create ("test::pkg::DerivedFqn" );
262+ derivedFqn .setSuperData (base );
263+ derivedFqn .setAuthoredSuperRef ("test::pkg::Base" );
264+ root .addChild (derivedFqn );
265+
266+ // Same package, authored as a SHORT name — must echo the short name.
267+ ValueMetaObject derivedShort = ValueMetaObject .create ("test::pkg::DerivedShort" );
268+ derivedShort .setSuperData (base );
269+ derivedShort .setAuthoredSuperRef ("Base" );
270+ root .addChild (derivedShort );
271+
272+ String json = CanonicalJsonSerializer .canonicalSerialize (root );
273+
274+ assertTrue ("authored FQN extends must echo verbatim, got: " + json ,
275+ json .contains ("\" extends\" : \" test::pkg::Base\" " ));
276+ assertTrue ("authored short extends must echo verbatim, got: " + json ,
277+ json .contains ("\" extends\" : \" Base\" " ));
233278 }
234279
235280 // -----------------------------------------------------------------------
@@ -330,13 +375,16 @@ public void testExtendsEmittedAsFQNWhenSuperInDifferentPackage() {
330375 }
331376
332377 // -----------------------------------------------------------------------
333- // Test 12 — extends emitted as short name when super is in the same package
378+ // Test 12 — no authored ref → serializer falls back to the resolved FQN
334379 //
335- // Both Base and Derived are in "test::pkg". The existing Test 7 covers
336- // same-package extends; this test makes the intent explicit.
380+ // Both Base and Derived are in "test::pkg", super set programmatically with
381+ // NO authored ref. Under the echo-verbatim contract (#37) the serializer no
382+ // longer recomputes a short name for same-package supers — it emits the
383+ // resolved FQN fallback. The short-vs-FQN choice is the AUTHOR's (echoed
384+ // verbatim; see Test 7b), not the serializer's to recompute.
337385 // -----------------------------------------------------------------------
338386 @ Test
339- public void testExtendsEmittedAsShortNameWhenSamePackage () {
387+ public void testExtendsFallsBackToFqnWhenNoAuthoredRef () {
340388 MetaRoot root = new MetaRoot ("test::pkg" );
341389
342390 ValueMetaObject base = ValueMetaObject .create ("test::pkg::Base" );
@@ -349,11 +397,8 @@ public void testExtendsEmittedAsShortNameWhenSamePackage() {
349397
350398 String json = CanonicalJsonSerializer .canonicalSerialize (root );
351399
352- // Same package — short name is unambiguous and preferred.
353- assertTrue ("extends must be the short name when super is in the same package, got: " + json ,
354- json .contains ("\" extends\" : \" Base\" " ));
355- // Must NOT use the FQN — that would be unnecessarily verbose.
356- assertFalse ("extends must NOT be the FQN when super is in the same package" ,
400+ // No authored ref → resolved FQN fallback (no short-name recompute).
401+ assertTrue ("extends must be the resolved FQN when no ref was authored, got: " + json ,
357402 json .contains ("\" extends\" : \" test::pkg::Base\" " ));
358403 }
359404
0 commit comments