@@ -11,6 +11,7 @@ import (
1111 "time"
1212
1313 "github.com/github/github-mcp-server/internal/githubv4mock"
14+ "github.com/github/github-mcp-server/internal/requeststate"
1415 "github.com/github/github-mcp-server/internal/toolsnaps"
1516 "github.com/github/github-mcp-server/pkg/inventory"
1617 "github.com/github/github-mcp-server/pkg/raw"
@@ -3037,6 +3038,96 @@ func Test_DeleteRepository(t *testing.T) {
30373038 assert .Contains (t , getTextResult (t , result ).Text , "owner/repo was deleted" )
30383039 })
30393040
3041+ t .Run ("seals and verifies the deletion target" , func (t * testing.T ) {
3042+ sealer , err := requeststate .New (base64 .StdEncoding .EncodeToString ([]byte ("0123456789abcdef0123456789abcdef" )))
3043+ require .NoError (t , err )
3044+ client := NewMockedHTTPClient (
3045+ WithRequestMatchHandler (
3046+ DeleteReposByOwnerByRepo ,
3047+ mockResponse (t , http .StatusNoContent , nil ),
3048+ ),
3049+ )
3050+ deps := BaseDeps {
3051+ Client : mustNewGHClient (t , client ),
3052+ StateSealer : sealer ,
3053+ }
3054+ handler := serverTool .Handler (deps )
3055+
3056+ firstRequest := createMCPRequest (map [string ]any {"owner" : "owner" , "repo" : "repo" })
3057+ firstResult , err := handler (ContextWithDeps (context .Background (), deps ), & firstRequest )
3058+ require .NoError (t , err )
3059+ require .NotEmpty (t , firstResult .RequestState )
3060+
3061+ retry := createMCPRequest (map [string ]any {"owner" : "owner" , "repo" : "repo" })
3062+ retry .Params .RequestState = firstResult .RequestState
3063+ retry .Params .InputResponses = mcp.InputResponseMap {
3064+ deleteRepositoryConfirmationID : & mcp.ElicitResult {
3065+ Action : "accept" ,
3066+ Content : map [string ]any {
3067+ deleteRepositoryConfirmationField : "owner/repo" ,
3068+ },
3069+ },
3070+ }
3071+ result , err := handler (ContextWithDeps (context .Background (), deps ), & retry )
3072+ require .NoError (t , err )
3073+ require .False (t , result .IsError )
3074+ assert .Contains (t , getTextResult (t , result ).Text , "owner/repo was deleted" )
3075+ })
3076+
3077+ t .Run ("refuses tampered deletion state" , func (t * testing.T ) {
3078+ sealer , err := requeststate .New (base64 .StdEncoding .EncodeToString ([]byte ("0123456789abcdef0123456789abcdef" )))
3079+ require .NoError (t , err )
3080+ deps := BaseDeps {
3081+ Client : mustNewGHClient (t , NewMockedHTTPClient ()),
3082+ StateSealer : sealer ,
3083+ }
3084+ handler := serverTool .Handler (deps )
3085+
3086+ request := createMCPRequest (map [string ]any {"owner" : "owner" , "repo" : "repo" })
3087+ request .Params .RequestState = "tampered"
3088+ request .Params .InputResponses = mcp.InputResponseMap {
3089+ deleteRepositoryConfirmationID : & mcp.ElicitResult {
3090+ Action : "accept" ,
3091+ Content : map [string ]any {
3092+ deleteRepositoryConfirmationField : "owner/repo" ,
3093+ },
3094+ },
3095+ }
3096+ result , err := handler (ContextWithDeps (context .Background (), deps ), & request )
3097+ require .NoError (t , err )
3098+ require .True (t , result .IsError )
3099+ assert .Contains (t , getErrorResult (t , result ).Text , "state was invalid" )
3100+ })
3101+
3102+ t .Run ("refuses a changed deletion target" , func (t * testing.T ) {
3103+ sealer , err := requeststate .New (base64 .StdEncoding .EncodeToString ([]byte ("0123456789abcdef0123456789abcdef" )))
3104+ require .NoError (t , err )
3105+ deps := BaseDeps {
3106+ Client : mustNewGHClient (t , NewMockedHTTPClient ()),
3107+ StateSealer : sealer ,
3108+ }
3109+ handler := serverTool .Handler (deps )
3110+
3111+ firstRequest := createMCPRequest (map [string ]any {"owner" : "owner" , "repo" : "repo" })
3112+ firstResult , err := handler (ContextWithDeps (context .Background (), deps ), & firstRequest )
3113+ require .NoError (t , err )
3114+
3115+ retry := createMCPRequest (map [string ]any {"owner" : "owner" , "repo" : "another" })
3116+ retry .Params .RequestState = firstResult .RequestState
3117+ retry .Params .InputResponses = mcp.InputResponseMap {
3118+ deleteRepositoryConfirmationID : & mcp.ElicitResult {
3119+ Action : "accept" ,
3120+ Content : map [string ]any {
3121+ deleteRepositoryConfirmationField : "owner/repo" ,
3122+ },
3123+ },
3124+ }
3125+ result , err := handler (ContextWithDeps (context .Background (), deps ), & retry )
3126+ require .NoError (t , err )
3127+ require .True (t , result .IsError )
3128+ assert .Contains (t , getErrorResult (t , result ).Text , "target changed" )
3129+ })
3130+
30403131 t .Run ("completes multi-round-trip elicitation before deleting" , func (t * testing.T ) {
30413132 httpClient := NewMockedHTTPClient (
30423133 WithRequestMatchHandler (
0 commit comments