@@ -2504,5 +2504,198 @@ public async Task SerializeDocumentWithSelfPropertyAsV30WritesAsExtension()
25042504 // Assert
25052505 Assert . Equal ( expected . MakeLineBreaksEnvironmentNeutral ( ) , actual . MakeLineBreaksEnvironmentNeutral ( ) ) ;
25062506 }
2507+
2508+ [ Fact ]
2509+ public void GetOperationById_ReturnsMatchingOperation ( )
2510+ {
2511+ var operation = new OpenApiOperation { OperationId = "getUser" } ;
2512+ var doc = new OpenApiDocument
2513+ {
2514+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2515+ Paths = new OpenApiPaths
2516+ {
2517+ [ "/users/{id}" ] = new OpenApiPathItem
2518+ {
2519+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2520+ {
2521+ [ HttpMethod . Get ] = operation
2522+ }
2523+ }
2524+ }
2525+ } ;
2526+
2527+ var result = doc . GetOperationById ( "getUser" ) ;
2528+
2529+ Assert . Same ( operation , result ) ;
2530+ }
2531+
2532+ [ Fact ]
2533+ public void GetOperationById_ReturnsNullWhenNotFound ( )
2534+ {
2535+ var doc = new OpenApiDocument
2536+ {
2537+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2538+ Paths = new OpenApiPaths
2539+ {
2540+ [ "/users" ] = new OpenApiPathItem
2541+ {
2542+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2543+ {
2544+ [ HttpMethod . Get ] = new OpenApiOperation { OperationId = "listUsers" }
2545+ }
2546+ }
2547+ }
2548+ } ;
2549+
2550+ var result = doc . GetOperationById ( "nonExistentId" ) ;
2551+
2552+ Assert . Null ( result ) ;
2553+ }
2554+
2555+ [ Fact ]
2556+ public void GetOperationById_SearchesWebhooks ( )
2557+ {
2558+ var webhookOperation = new OpenApiOperation { OperationId = "onUserCreated" } ;
2559+ var doc = new OpenApiDocument
2560+ {
2561+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2562+ Paths = [ ] ,
2563+ Webhooks = new Dictionary < string , IOpenApiPathItem >
2564+ {
2565+ [ "userCreated" ] = new OpenApiPathItem
2566+ {
2567+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2568+ {
2569+ [ HttpMethod . Post ] = webhookOperation
2570+ }
2571+ }
2572+ }
2573+ } ;
2574+
2575+ var result = doc . GetOperationById ( "onUserCreated" ) ;
2576+
2577+ Assert . Same ( webhookOperation , result ) ;
2578+ }
2579+
2580+ [ Fact ]
2581+ public void GetOperationById_IsCaseSensitive ( )
2582+ {
2583+ var doc = new OpenApiDocument
2584+ {
2585+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2586+ Paths = new OpenApiPaths
2587+ {
2588+ [ "/users" ] = new OpenApiPathItem
2589+ {
2590+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2591+ {
2592+ [ HttpMethod . Get ] = new OpenApiOperation { OperationId = "getUser" }
2593+ }
2594+ }
2595+ }
2596+ } ;
2597+
2598+ Assert . NotNull ( doc . GetOperationById ( "getUser" ) ) ;
2599+ Assert . Null ( doc . GetOperationById ( "GetUser" ) ) ;
2600+ Assert . Null ( doc . GetOperationById ( "GETUSER" ) ) ;
2601+ }
2602+
2603+ [ Fact ]
2604+ public void GetOperationById_ResolvesOperationThroughPathItemReference ( )
2605+ {
2606+ const string yaml = """
2607+ openapi: '3.1.0'
2608+ info:
2609+ title: Test
2610+ version: 1.0.0
2611+ paths:
2612+ /users:
2613+ $ref: '#/components/pathItems/userPathItem'
2614+ components:
2615+ pathItems:
2616+ userPathItem:
2617+ get:
2618+ operationId: listUsers
2619+ responses:
2620+ '200':
2621+ description: OK
2622+ """ ;
2623+
2624+ var doc = OpenApiDocument . Parse ( yaml , OpenApiConstants . Yaml , SettingsFixture . ReaderSettings ) . Document ;
2625+ doc . Workspace . RegisterComponents ( doc ) ;
2626+
2627+ var result = doc . GetOperationById ( "listUsers" ) ;
2628+
2629+ Assert . NotNull ( result ) ;
2630+ Assert . Equal ( "listUsers" , result . OperationId ) ;
2631+ }
2632+
2633+ [ Fact ]
2634+ public void GetOperationById_DuplicateIdReturnsFirstMatch ( )
2635+ {
2636+ // operationId must be unique per spec, but if not, Paths takes priority over Webhooks
2637+ var pathsOperation = new OpenApiOperation { OperationId = "duplicateId" } ;
2638+ var webhooksOperation = new OpenApiOperation { OperationId = "duplicateId" } ;
2639+ var doc = new OpenApiDocument
2640+ {
2641+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2642+ Paths = new OpenApiPaths
2643+ {
2644+ [ "/users" ] = new OpenApiPathItem
2645+ {
2646+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2647+ {
2648+ [ HttpMethod . Get ] = pathsOperation
2649+ }
2650+ }
2651+ } ,
2652+ Webhooks = new Dictionary < string , IOpenApiPathItem >
2653+ {
2654+ [ "userEvent" ] = new OpenApiPathItem
2655+ {
2656+ Operations = new Dictionary < HttpMethod , OpenApiOperation >
2657+ {
2658+ [ HttpMethod . Post ] = webhooksOperation
2659+ }
2660+ }
2661+ }
2662+ } ;
2663+
2664+ var result = doc . GetOperationById ( "duplicateId" ) ;
2665+
2666+ Assert . Same ( pathsOperation , result ) ;
2667+ }
2668+
2669+ [ Fact ]
2670+ public void GetOperationById_UnresolvedPathItemReferenceIsSkipped ( )
2671+ {
2672+ // An unresolved $ref has Target = null, so Operations = null — should be skipped gracefully
2673+ var unresolvedRef = new OpenApiPathItemReference ( "nonExistentPathItem" , null ) ;
2674+ var doc = new OpenApiDocument
2675+ {
2676+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2677+ Paths = new OpenApiPaths
2678+ {
2679+ [ "/users" ] = unresolvedRef
2680+ }
2681+ } ;
2682+
2683+ var result = doc . GetOperationById ( "anyId" ) ;
2684+
2685+ Assert . Null ( result ) ;
2686+ }
2687+
2688+ [ Fact ]
2689+ public void GetOperationById_ThrowsOnNullOrEmptyId ( )
2690+ {
2691+ var doc = new OpenApiDocument
2692+ {
2693+ Info = new OpenApiInfo { Title = "Test" , Version = "1.0" } ,
2694+ Paths = [ ]
2695+ } ;
2696+
2697+ Assert . Throws < ArgumentNullException > ( ( ) => doc . GetOperationById ( null ! ) ) ;
2698+ Assert . Throws < ArgumentNullException > ( ( ) => doc . GetOperationById ( string . Empty ) ) ;
2699+ }
25072700 }
25082701}
0 commit comments