@@ -451,4 +451,123 @@ public function testSerializeCustomnameMaxLength(): void
451451 $ result = InspectLink::serialize (new ItemPreviewData (customname: str_repeat ('A ' , 100 )));
452452 $ this ->assertStringStartsWith ('00 ' , $ result );
453453 }
454+
455+ // -----------------------------------------------------------------------
456+ // New test vectors from csfloat/cs-inspect-serializer gen.test.ts
457+ // -----------------------------------------------------------------------
458+
459+ /** CSFloat vector A — no stickers, paintwear ≈ 0.6337 */
460+ private const CSFLOAT_A = '00180720DA03280638FBEE88F90340B2026BC03C96 ' ;
461+
462+ /** CSFloat vector B — 4 stickers id=76 each, paintwear ≈ 0.99 */
463+ private const CSFLOAT_B = '00180720C80A280638A4E1F5FB03409A0562040800104C62040801104C62040802104C62040803104C6D4F5E30 ' ;
464+
465+ /** CSFloat vector C — keychain item (defindex=1355), highlight_reel=345 */
466+ private const CSFLOAT_C = 'A2B2A2BA69A882A28AA192AECAA2D2B700A3A5AAA2B286FA7BA0D684BE72 ' ;
467+
468+ public function testCsfloatA_Defindex (): void
469+ {
470+ $ this ->assertSame (7 , InspectLink::deserialize (self ::CSFLOAT_A )->defindex );
471+ }
472+
473+ public function testCsfloatA_Paintindex (): void
474+ {
475+ $ this ->assertSame (474 , InspectLink::deserialize (self ::CSFLOAT_A )->paintindex );
476+ }
477+
478+ public function testCsfloatA_Paintseed (): void
479+ {
480+ $ this ->assertSame (306 , InspectLink::deserialize (self ::CSFLOAT_A )->paintseed );
481+ }
482+
483+ public function testCsfloatA_Rarity (): void
484+ {
485+ $ this ->assertSame (6 , InspectLink::deserialize (self ::CSFLOAT_A )->rarity );
486+ }
487+
488+ public function testCsfloatA_Paintwear (): void
489+ {
490+ $ item = InspectLink::deserialize (self ::CSFLOAT_A );
491+ $ this ->assertNotNull ($ item ->paintwear );
492+ $ this ->assertEqualsWithDelta (0.6337 , $ item ->paintwear , 0.001 );
493+ }
494+
495+ public function testCsfloatB_StickerCount (): void
496+ {
497+ $ this ->assertCount (4 , InspectLink::deserialize (self ::CSFLOAT_B )->stickers );
498+ }
499+
500+ public function testCsfloatB_StickerIds (): void
501+ {
502+ $ stickers = InspectLink::deserialize (self ::CSFLOAT_B )->stickers ;
503+ foreach ($ stickers as $ s ) {
504+ $ this ->assertSame (76 , $ s ->stickerId );
505+ }
506+ }
507+
508+ public function testCsfloatB_Paintindex (): void
509+ {
510+ $ this ->assertSame (1352 , InspectLink::deserialize (self ::CSFLOAT_B )->paintindex );
511+ }
512+
513+ public function testCsfloatB_Paintwear (): void
514+ {
515+ $ item = InspectLink::deserialize (self ::CSFLOAT_B );
516+ $ this ->assertNotNull ($ item ->paintwear );
517+ $ this ->assertEqualsWithDelta (0.99 , $ item ->paintwear , 0.001 );
518+ }
519+
520+ public function testCsfloatC_Defindex (): void
521+ {
522+ $ this ->assertSame (1355 , InspectLink::deserialize (self ::CSFLOAT_C )->defindex );
523+ }
524+
525+ public function testCsfloatC_Quality (): void
526+ {
527+ $ this ->assertSame (12 , InspectLink::deserialize (self ::CSFLOAT_C )->quality );
528+ }
529+
530+ public function testCsfloatC_KeychainHighlightReel (): void
531+ {
532+ $ keychains = InspectLink::deserialize (self ::CSFLOAT_C )->keychains ;
533+ $ this ->assertCount (1 , $ keychains );
534+ $ this ->assertSame (345 , $ keychains [0 ]->highlightReel );
535+ }
536+
537+ public function testCsfloatC_NoPaintwear (): void
538+ {
539+ // keychain items have no wear — field 7 not present in proto
540+ $ this ->assertNull (InspectLink::deserialize (self ::CSFLOAT_C )->paintwear );
541+ }
542+
543+ // -----------------------------------------------------------------------
544+ // Roundtrip: highlight_reel and nullable paintwear
545+ // -----------------------------------------------------------------------
546+
547+ public function testRoundtrip_HighlightReel (): void
548+ {
549+ $ data = new ItemPreviewData (
550+ defindex: 7 ,
551+ keychains: [new Sticker (slot: 0 , stickerId: 36 , highlightReel: 345 )],
552+ );
553+ $ result = InspectLink::deserialize (InspectLink::serialize ($ data ));
554+ $ this ->assertCount (1 , $ result ->keychains );
555+ $ this ->assertSame (345 , $ result ->keychains [0 ]->highlightReel );
556+ }
557+
558+ public function testRoundtrip_NullPaintwear (): void
559+ {
560+ $ data = new ItemPreviewData (defindex: 7 , paintwear: null );
561+ $ result = InspectLink::deserialize (InspectLink::serialize ($ data ));
562+ $ this ->assertNull ($ result ->paintwear );
563+ }
564+
565+ public function testSerialize_NullPaintwearProducesFewerBytes (): void
566+ {
567+ // An item with null paintwear omits field 7 entirely; a non-zero float value writes it.
568+ // (0.0 also omits field 7 due to proto3 default-value skipping, so compare against 0.5.)
569+ $ withNull = InspectLink::serialize (new ItemPreviewData (defindex: 7 , paintwear: null ));
570+ $ withFloat = InspectLink::serialize (new ItemPreviewData (defindex: 7 , paintwear: 0.5 ));
571+ $ this ->assertLessThan (strlen ($ withFloat ), strlen ($ withNull ));
572+ }
454573}
0 commit comments