@@ -329,4 +329,274 @@ private static bool IsTagForName(TagNavigationItem tagItem, string expectedTagNa
329329 // Access the ApiTag model through the Index property
330330 return tagItem . Index . Model is ApiTag tag && tag . Name == expectedTagName ;
331331 }
332+
333+ [ Fact ]
334+ public async Task Tags_WithMixedDisplayNames_SortedAlphabeticallyByDisplayName ( )
335+ {
336+ // Arrange - spec with mixed x-displayName and canonical names
337+ var openApiJson = /*lang=json*/ """
338+ {
339+ "openapi": "3.0.3",
340+ "info": {
341+ "title": "Test API",
342+ "version": "1.0.0"
343+ },
344+ "paths": {
345+ "/zebra": {
346+ "get": {
347+ "operationId": "zebra-op",
348+ "tags": ["zebra"],
349+ "responses": {"200": {"description": "Success"}}
350+ }
351+ },
352+ "/apple": {
353+ "get": {
354+ "operationId": "apple-op",
355+ "tags": ["apple"],
356+ "responses": {"200": {"description": "Success"}}
357+ }
358+ },
359+ "/charlie": {
360+ "get": {
361+ "operationId": "charlie-op",
362+ "tags": ["charlie"],
363+ "responses": {"200": {"description": "Success"}}
364+ }
365+ }
366+ },
367+ "tags": [
368+ {
369+ "name": "zebra",
370+ "x-displayName": "Animal Zoo"
371+ },
372+ {
373+ "name": "apple",
374+ "x-displayName": "Fruit Store"
375+ },
376+ {
377+ "name": "charlie"
378+ }
379+ ]
380+ }
381+ """ ;
382+
383+ var ( generator , openApiDocument ) = await CreateGeneratorWithSpec ( openApiJson ) ;
384+
385+ // Act
386+ var navigation = generator . CreateNavigation ( "test" , openApiDocument ) ;
387+
388+ // Assert - should be sorted alphabetically by display name: "Animal Zoo", "charlie", "Fruit Store"
389+ navigation . NavigationItems . Should ( ) . HaveCount ( 3 ) ;
390+
391+ var tagItems = navigation . NavigationItems . OfType < TagNavigationItem > ( ) . ToList ( ) ;
392+ tagItems . Should ( ) . HaveCount ( 3 ) ;
393+
394+ // Verify sorted order
395+ tagItems [ 0 ] . NavigationTitle . Should ( ) . Be ( "Animal Zoo" , "First tag should be 'Animal Zoo' (zebra with x-displayName)" ) ;
396+ tagItems [ 1 ] . NavigationTitle . Should ( ) . Be ( "charlie" , "Second tag should be 'charlie' (canonical name, no x-displayName)" ) ;
397+ tagItems [ 2 ] . NavigationTitle . Should ( ) . Be ( "Fruit Store" , "Third tag should be 'Fruit Store' (apple with x-displayName)" ) ;
398+ }
399+
400+ [ Fact ]
401+ public async Task Tags_CaseInsensitiveSorting_WorksCorrectly ( )
402+ {
403+ // Arrange - spec with case variations
404+ var openApiJson = /*lang=json*/ """
405+ {
406+ "openapi": "3.0.3",
407+ "info": {
408+ "title": "Test API",
409+ "version": "1.0.0"
410+ },
411+ "paths": {
412+ "/bravo": {
413+ "get": {
414+ "operationId": "bravo-op",
415+ "tags": ["bravo"],
416+ "responses": {"200": {"description": "Success"}}
417+ }
418+ },
419+ "/alpha": {
420+ "get": {
421+ "operationId": "alpha-op",
422+ "tags": ["alpha"],
423+ "responses": {"200": {"description": "Success"}}
424+ }
425+ }
426+ },
427+ "tags": [
428+ {
429+ "name": "bravo",
430+ "x-displayName": "beta Service"
431+ },
432+ {
433+ "name": "alpha",
434+ "x-displayName": "Alpha Service"
435+ }
436+ ]
437+ }
438+ """ ;
439+
440+ var ( generator , openApiDocument ) = await CreateGeneratorWithSpec ( openApiJson ) ;
441+
442+ // Act
443+ var navigation = generator . CreateNavigation ( "test" , openApiDocument ) ;
444+
445+ // Assert - should be sorted case-insensitively: "Alpha Service", "beta Service"
446+ var tagItems = navigation . NavigationItems . OfType < TagNavigationItem > ( ) . ToList ( ) ;
447+ tagItems . Should ( ) . HaveCount ( 2 ) ;
448+
449+ tagItems [ 0 ] . NavigationTitle . Should ( ) . Be ( "Alpha Service" , "Should sort case-insensitively" ) ;
450+ tagItems [ 1 ] . NavigationTitle . Should ( ) . Be ( "beta Service" , "Should sort case-insensitively" ) ;
451+ }
452+
453+ [ Fact ]
454+ public async Task Tags_OnlyCanonicalNames_SortedAlphabetically ( )
455+ {
456+ // Arrange - spec with no x-displayName values
457+ var openApiJson = /*lang=json*/ """
458+ {
459+ "openapi": "3.0.3",
460+ "info": {
461+ "title": "Test API",
462+ "version": "1.0.0"
463+ },
464+ "paths": {
465+ "/zebra": {
466+ "get": {
467+ "operationId": "zebra-op",
468+ "tags": ["zebra"],
469+ "responses": {"200": {"description": "Success"}}
470+ }
471+ },
472+ "/alpha": {
473+ "get": {
474+ "operationId": "alpha-op",
475+ "tags": ["alpha"],
476+ "responses": {"200": {"description": "Success"}}
477+ }
478+ },
479+ "/mike": {
480+ "get": {
481+ "operationId": "mike-op",
482+ "tags": ["mike"],
483+ "responses": {"200": {"description": "Success"}}
484+ }
485+ }
486+ },
487+ "tags": [
488+ {
489+ "name": "zebra",
490+ "description": "Zebra operations"
491+ },
492+ {
493+ "name": "alpha",
494+ "description": "Alpha operations"
495+ },
496+ {
497+ "name": "mike",
498+ "description": "Mike operations"
499+ }
500+ ]
501+ }
502+ """ ;
503+
504+ var ( generator , openApiDocument ) = await CreateGeneratorWithSpec ( openApiJson ) ;
505+
506+ // Act
507+ var navigation = generator . CreateNavigation ( "test" , openApiDocument ) ;
508+
509+ // Assert - should be sorted alphabetically by canonical name: "alpha", "mike", "zebra"
510+ var tagItems = navigation . NavigationItems . OfType < TagNavigationItem > ( ) . ToList ( ) ;
511+ tagItems . Should ( ) . HaveCount ( 3 ) ;
512+
513+ tagItems [ 0 ] . NavigationTitle . Should ( ) . Be ( "alpha" , "Should sort by canonical name" ) ;
514+ tagItems [ 1 ] . NavigationTitle . Should ( ) . Be ( "mike" , "Should sort by canonical name" ) ;
515+ tagItems [ 2 ] . NavigationTitle . Should ( ) . Be ( "zebra" , "Should sort by canonical name" ) ;
516+ }
517+
518+ [ Fact ]
519+ public async Task Tags_WithinClassification_SortedCorrectly ( )
520+ {
521+ // Arrange - Elasticsearch spec that creates MULTIPLE classifications so we get classification groups
522+ var openApiJson = /*lang=json,strict*/ """
523+ {
524+ "openapi": "3.0.3",
525+ "info": {
526+ "title": "Elasticsearch Request & Response Specification",
527+ "version": "1.0.0"
528+ },
529+ "paths": {
530+ "/search": {
531+ "get": {
532+ "operationId": "search-op",
533+ "tags": ["search"],
534+ "responses": {"200": {"description": "Success"}}
535+ }
536+ },
537+ "/indices": {
538+ "get": {
539+ "operationId": "indices-op",
540+ "tags": ["indices"],
541+ "responses": {"200": {"description": "Success"}}
542+ }
543+ },
544+ "/watcher": {
545+ "get": {
546+ "operationId": "watcher-op",
547+ "tags": ["watcher"],
548+ "responses": {"200": {"description": "Success"}}
549+ }
550+ },
551+ "/tasks": {
552+ "get": {
553+ "operationId": "tasks-op",
554+ "tags": ["tasks"],
555+ "responses": {"200": {"description": "Success"}}
556+ }
557+ }
558+ },
559+ "tags": [
560+ {
561+ "name": "search",
562+ "x-displayName": "Search API"
563+ },
564+ {
565+ "name": "indices",
566+ "x-displayName": "Indices Management"
567+ },
568+ {
569+ "name": "watcher",
570+ "x-displayName": "Watcher API"
571+ },
572+ {
573+ "name": "tasks",
574+ "x-displayName": "Task management"
575+ }
576+ ]
577+ }
578+ """ ;
579+
580+ var ( generator , openApiDocument ) = await CreateGeneratorWithSpec ( openApiJson ) ;
581+
582+ // Act
583+ var navigation = generator . CreateNavigation ( "elasticsearch" , openApiDocument ) ;
584+
585+ // Assert - these should create multiple classifications:
586+ // "search" -> "common", "indices" -> "management", "watcher"/"tasks" -> "info"
587+ // We should get classification groups since there are multiple classifications
588+ var classificationItems = navigation . NavigationItems . OfType < ClassificationNavigationItem > ( ) . ToList ( ) ;
589+ classificationItems . Should ( ) . HaveCountGreaterThan ( 1 , "Should have multiple classification groups" ) ;
590+
591+ // Find the "info" classification which should contain watcher and tasks
592+ var infoClassification = classificationItems . FirstOrDefault ( c => c . NavigationTitle == "info" ) ;
593+ infoClassification . Should ( ) . NotBeNull ( "Should have 'info' classification for watcher and tasks" ) ;
594+
595+ var tagItems = infoClassification . NavigationItems . OfType < TagNavigationItem > ( ) . ToList ( ) ;
596+ tagItems . Should ( ) . HaveCount ( 2 , "Info classification should have watcher and tasks" ) ;
597+
598+ // Expected sort order within info classification: "Task management", "Watcher API"
599+ tagItems [ 0 ] . NavigationTitle . Should ( ) . Be ( "Task management" , "Should sort by displayName" ) ;
600+ tagItems [ 1 ] . NavigationTitle . Should ( ) . Be ( "Watcher API" , "Should sort by displayName" ) ;
601+ }
332602}
0 commit comments