diff --git a/src/notifications/go.mod b/src/notifications/go.mod index 9717ef8b..8e49f2f3 100644 --- a/src/notifications/go.mod +++ b/src/notifications/go.mod @@ -6,12 +6,12 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/PuerkitoBio/goquery v1.12.0 github.com/chrj/smtpd v1.0.0 - github.com/cloudfoundry-community/go-uaa v0.3.6 + github.com/cloudfoundry-community/go-uaa v0.4.1 github.com/go-sql-driver/mysql v1.10.0 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/gorilla/mux v1.8.1 - github.com/onsi/ginkgo/v2 v2.29.0 - github.com/onsi/gomega v1.41.0 + github.com/onsi/ginkgo/v2 v2.32.0 + github.com/onsi/gomega v1.42.1 github.com/pivotal-cf-experimental/rainmaker v0.0.0-20160401052143-d533d01b7c52 github.com/pivotal-cf/uaa-sso-golang v0.0.0-20141119184546-0b91e8ad4bb6 github.com/pivotal-golang/conceal v0.0.0-20141120010127-31656578115c @@ -42,12 +42,12 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/ziutek/mymysql v1.5.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.35.0 // indirect - golang.org/x/net v0.55.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.45.0 // indirect - golang.org/x/text v0.37.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.38.0 // indirect + golang.org/x/tools v0.45.0 // indirect gopkg.in/alexcesaro/quotedprintable.v1 v1.0.0-20141111223934-dacd4576c5aa // indirect ) diff --git a/src/notifications/go.sum b/src/notifications/go.sum index d3641b07..576373b2 100644 --- a/src/notifications/go.sum +++ b/src/notifications/go.sum @@ -12,8 +12,8 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/chrj/smtpd v1.0.0 h1:jChSfLzAhrSxTky0EZ6oW+UKiqdnepdknWYRkPeA+ZE= github.com/chrj/smtpd v1.0.0/go.mod h1:zEP61gNDlWp/jdUqBcq/ykIbgOERyRvwfMsRLl3h9gM= -github.com/cloudfoundry-community/go-uaa v0.3.6 h1:G4yWO3Axi5eTOvqfWBBSWiA1Mkonlk/q2DxGXIYtbGE= -github.com/cloudfoundry-community/go-uaa v0.3.6/go.mod h1:EbMPp8sKxKmz0+WKa6qPfdshI8NZYoD4FJAAIbQ9Vl4= +github.com/cloudfoundry-community/go-uaa v0.4.1 h1:Lp3CM1MaHLQ1i5eQo4o4pusIOFxo4PCmCp1V4UAVmhQ= +github.com/cloudfoundry-community/go-uaa v0.4.1/go.mod h1:/XtePfYbK0nj/03gYCO7YOVqa0g1IZKpi+YlMByTEBg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -84,12 +84,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= -github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= +github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= -github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= +github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= +github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/pivotal-cf-experimental/rainmaker v0.0.0-20160401052143-d533d01b7c52 h1:YaPgxzuENXlCMHe/JVQICTIVtWdXbBodzNnQM/A8+rY= github.com/pivotal-cf-experimental/rainmaker v0.0.0-20160401052143-d533d01b7c52/go.mod h1:NkPUxSBmoagsX+nu++zlZZKxjFJ1E8VQnduId7VdfFY= github.com/pivotal-cf/uaa-sso-golang v0.0.0-20141119184546-0b91e8ad4bb6 h1:VIZLjNljVeBKRSGpAIElubnS3Aq1iuqAS7KpWYZayKA= @@ -148,8 +148,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -163,10 +163,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -176,8 +176,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -198,8 +198,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -218,8 +218,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -227,8 +227,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/Makefile b/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/Makefile new file mode 100644 index 00000000..8a71712c --- /dev/null +++ b/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/Makefile @@ -0,0 +1,16 @@ +.PHONY: all build ci format ginkgo test + +all: test build + +format: + go fmt ./... + +ginkgo: + go run github.com/onsi/ginkgo/v2/ginkgo -v -r --randomize-suites --randomize-all -race + +test: format ginkgo + +ci: ginkgo + +build: + go build ./... diff --git a/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/clients.go b/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/clients.go index 33110f8f..57cb78eb 100644 --- a/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/clients.go +++ b/src/notifications/vendor/github.com/cloudfoundry-community/go-uaa/clients.go @@ -22,24 +22,26 @@ type paginatedClientList struct { // Client is a UAA client // http://docs.cloudfoundry.org/api/uaa/version/4.19.0/index.html#clients. type Client struct { - ClientID string `json:"client_id,omitempty" generator:"id"` - AuthorizedGrantTypes []string `json:"authorized_grant_types,omitempty"` - RedirectURI []string `json:"redirect_uri,omitempty"` - Scope []string `json:"scope,omitempty"` - ResourceIDs []string `json:"resource_ids,omitempty"` - Authorities []string `json:"authorities,omitempty"` - AutoApproveRaw interface{} `json:"autoapprove,omitempty"` - AccessTokenValidity int64 `json:"access_token_validity,omitempty"` - RefreshTokenValidity int64 `json:"refresh_token_validity,omitempty"` - AllowedProviders []string `json:"allowedproviders,omitempty"` - DisplayName string `json:"name,omitempty"` - TokenSalt string `json:"token_salt,omitempty"` - CreatedWith string `json:"createdwith,omitempty"` - ApprovalsDeleted bool `json:"approvals_deleted,omitempty"` - RequiredUserGroups []string `json:"required_user_groups,omitempty"` - ClientSecret string `json:"client_secret,omitempty"` - LastModified int64 `json:"lastModified,omitempty"` - AllowPublic bool `json:"allowpublic,omitempty"` + ClientID string `json:"client_id,omitempty" generator:"id"` + AuthorizedGrantTypes []string `json:"authorized_grant_types,omitempty"` + RedirectURI []string `json:"redirect_uri,omitempty"` + Scope []string `json:"scope,omitempty"` + ResourceIDs []string `json:"resource_ids,omitempty"` + Authorities []string `json:"authorities,omitempty"` + AutoApproveRaw interface{} `json:"autoapprove,omitempty"` + AccessTokenValidity int64 `json:"access_token_validity,omitempty"` + RefreshTokenValidity int64 `json:"refresh_token_validity,omitempty"` + AllowedProviders []string `json:"allowedproviders,omitempty"` + DisplayName string `json:"name,omitempty"` + TokenSalt string `json:"token_salt,omitempty"` + CreatedWith string `json:"createdwith,omitempty"` + ApprovalsDeleted bool `json:"approvals_deleted,omitempty"` + RequiredUserGroups []string `json:"required_user_groups,omitempty"` + ClientSecret string `json:"client_secret,omitempty"` + LastModified int64 `json:"lastModified,omitempty"` + AllowPublic bool `json:"allowpublic,omitempty"` + JwksURI string `json:"jwks_uri,omitempty"` + Jwks json.RawMessage `json:"jwks,omitempty"` } // Identifier returns the field used to uniquely identify a Client. @@ -156,3 +158,42 @@ func (a *API) ChangeClientSecret(id string, newSecret string) error { } return nil } + +// ChangeClientJWTMode is the operation mode for ChangeClientJWT. +type ChangeClientJWTMode string + +const ( + ChangeClientJWTModeAdd = ChangeClientJWTMode("ADD") + ChangeClientJWTModeUpdate = ChangeClientJWTMode("UPDATE") + ChangeClientJWTModeDelete = ChangeClientJWTMode("DELETE") +) + +// ClientJWTChangeRequest is the request body for the PUT /oauth/clients/{id}/clientjwt endpoint. +type ClientJWTChangeRequest struct { + ClientID string `json:"client_id,omitempty"` + ChangeMode ChangeClientJWTMode `json:"changeMode,omitempty"` + JwksURI string `json:"jwks_uri,omitempty"` + Jwks json.RawMessage `json:"jwks,omitempty"` + Kid string `json:"kid,omitempty"` + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audience string `json:"aud,omitempty"` +} + +// ChangeClientJWT configures the JWT trust for the client with the given id. +// Use jwks_uri or jwks to specify public keys; use iss/sub/aud for federation JWT trust. +// changeMode controls whether the key is ADDed, UPDATEd, or DELETEd (kid required for DELETE). +func (a *API) ChangeClientJWT(req ClientJWTChangeRequest) error { + if req.ClientID == "" { + return errorMissingValue("client_id") + } + if req.ChangeMode == ChangeClientJWTModeDelete && req.Kid == "" { + return fmt.Errorf("kid must be specified when changeMode is %v", ChangeClientJWTModeDelete) + } + u := urlWithPath(*a.TargetURL, fmt.Sprintf("%s/%s/clientjwt", ClientsEndpoint, req.ClientID)) + j, err := json.Marshal(req) + if err != nil { + return err + } + return a.doJSON(http.MethodPut, &u, bytes.NewBuffer(j), nil, true) +} diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md b/src/notifications/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md index 224e8db5..10e60856 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md @@ -1,3 +1,28 @@ +## 2.32.0 + +`-fd` generate RSpec-style documentation output. Thank @woodie ! +--sleep-on-failure pauses a failed spec before teardown. Thanks @qinqon ! + +## 2.31.0 + +Add a bunch of Claude Skills via the marketplace: + +``` +/plugin marketplace add onsi/ginkgo +/plugin install ginkgo@ginkgo +``` + +## 2.30.0 + +### Features +Ginkgo now allows `extentions/global.Reset` to support running multiple suites from within a single process. This may take some massaging on your part (see [1672](https://github.com/onsi/ginkgo/issues/1672)) but can dramatically speed up codebases with O(hundreds) of test suites. + +Thanks @lawrencejones ! + +### Fixes + +- Fix nested --github-output group for progress report nested inside timeline [4f62d7a] + ## 2.29.0 `GinkgoHelperGo` makes it easier to write test helpers that need to run in goroutines. Specifically, it makes managing the failure state and capturing failure panics correctly straightforward. diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/README.md b/src/notifications/vendor/github.com/onsi/ginkgo/v2/README.md index 6d36e377..ad5e45f6 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/README.md +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/README.md @@ -106,6 +106,19 @@ And that's just Ginkgo! [Gomega](https://onsi.github.io/gomega/) brings a rich, Happy Testing! +## Using Ginkgo with Claude Code + +Ginkgo ships a set of [Claude Code](https://claude.com/claude-code) skills as a plugin, with this repo doubling as the marketplace, so an agent writing specs in *your* project has Ginkgo's idioms, decorators, and gotchas on hand. From inside Claude Code: + +``` +/plugin marketplace add onsi/ginkgo +/plugin install ginkgo@ginkgo +``` + +(or non-interactively: `claude plugin marketplace add onsi/ginkgo` then `claude plugin install ginkgo@ginkgo`) + +This installs a family of `ginkgo:*` skills that activate automatically while you write and run specs. Start with `ginkgo:overview`; see the [plugin README](plugins/ginkgo/README.md) for the full list. + ## License Ginkgo is MIT-Licensed diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/core_dsl.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/core_dsl.go index 1c5a39a1..fb5761c1 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/core_dsl.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/core_dsl.go @@ -39,7 +39,6 @@ var flagSet types.GinkgoFlagSet var deprecationTracker = types.NewDeprecationTracker() var suiteConfig = types.NewDefaultSuiteConfig() var reporterConfig = types.NewDefaultReporterConfig() -var suiteDidRun = false var outputInterceptor internal.OutputInterceptor var client parallel_support.Client @@ -259,10 +258,10 @@ for more on how specs are parallelized in Ginkgo. You can also pass suite-level Label() decorators to RunSpecs. The passed-in labels will apply to all specs in the suite. */ func RunSpecs(t GinkgoTestingT, description string, args ...any) bool { - if suiteDidRun { + if global.SuiteDidRun { exitIfErr(types.GinkgoErrors.RerunningSuite()) } - suiteDidRun = true + global.SuiteDidRun = true err := global.PushClone() if err != nil { exitIfErr(err) diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go index c5091e6d..7b6c2cc2 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go @@ -39,6 +39,10 @@ func BuildRunCommand() command.Command { cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) command.AbortIfErrors("Ginkgo detected configuration issues:", errors) + if types.ReconcileFdOutputConfiguration(reporterConfig, &suiteConfig, &cliConfig) { + fmt.Println("--fd is incompatible with parallel runs (-p/-procs) and -randomize-all; ignoring those flags and running specs in series, in declaration order.") + } + runner := &SpecRunner{ cliConfig: cliConfig, goFlagsConfig: goFlagsConfig, diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go index fe1ca305..c60d7115 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go @@ -37,6 +37,10 @@ func BuildWatchCommand() command.Command { cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) command.AbortIfErrors("Ginkgo detected configuration issues:", errors) + if types.ReconcileFdOutputConfiguration(reporterConfig, &suiteConfig, &cliConfig) { + fmt.Println("--fd is incompatible with parallel runs (-p/-procs) and -randomize-all; ignoring those flags and running specs in series, in declaration order.") + } + watcher := &SpecWatcher{ cliConfig: cliConfig, goFlagsConfig: goFlagsConfig, diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/global/init.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/global/init.go index 464e3c97..14d2552b 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/global/init.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/global/init.go @@ -8,6 +8,12 @@ var Suite *internal.Suite var Failer *internal.Failer var backupSuite *internal.Suite +// SuiteDidRun tracks whether RunSpecs has already been invoked for the current global +// suite. It lives here (rather than in package ginkgo) so that InitializeGlobals can +// clear it, allowing extensions/globals.Reset to support running multiple suites +// sequentially in a single process. +var SuiteDidRun bool + func init() { InitializeGlobals() } @@ -15,6 +21,7 @@ func init() { func InitializeGlobals() { Failer = internal.NewFailer() Suite = internal.NewSuite() + SuiteDidRun = false } func PushClone() error { diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/suite.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/suite.go index 57c1cffe..6c879855 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/suite.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/internal/suite.go @@ -996,6 +996,7 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ } else { failure.Message, failure.Location, failure.ForwardedPanic, failure.TimelineLocation = failureFromRun.Message, failureFromRun.Location, failureFromRun.ForwardedPanic, failureFromRun.TimelineLocation suite.reporter.EmitFailure(outcomeFromRun, failure) + suite.pauseOnFailureIfRequested(node) return outcomeFromRun, failure } case <-gracePeriodChannel: @@ -1079,6 +1080,42 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ } } +// pauseOnFailureIfRequested pauses the suite at the moment a failure is identified, +// when the user has set --sleep-on-failure. This hooks directly into runNode's failure +// path so the pause happens immediately at the point of failure - before any teardown +// or cleanup runs - leaving the system live for inspection. +// +// We only pause for failures in setup and subject nodes (It, Before*, BeforeSuite...), +// i.e. nodes that run before teardown. Pausing on a failure in a teardown/cleanup or +// reporting node would be pointless (the system is already being torn down) and could +// interfere with interrupt handling, so those are skipped. +// +// The pause is interruptible: pressing ^C (or any interrupt) ends the pause early and +// the suite proceeds to run cleanup as usual. It is a no-op if the feature is disabled. +func (suite *Suite) pauseOnFailureIfRequested(node Node) { + if suite.config.SleepOnFailure <= 0 { + return + } + // only pause before teardown - skip teardown/cleanup/reporting nodes + if node.NodeType.Is(types.NodeTypesAllowedDuringCleanupInterrupt | types.NodeTypesAllowedDuringReportInterrupt) { + return + } + + duration := suite.config.SleepOnFailure + report := suite.generateProgressReport(false) + report.Message = fmt.Sprintf("{{bold}}{{orange}}Paused on failure for up to %s.{{/}}\nThe spec failed and Ginkgo has paused before running any teardown so you can inspect the live system.\nPress {{bold}}^C{{/}} to end the pause and proceed to cleanup.", duration) + suite.emitProgressReport(report) + + timer := time.NewTimer(duration) + defer timer.Stop() + // wait for the pause to elapse, or for the user to interrupt - in which case we end + // the pause early and let runNode return so cleanup can proceed + select { + case <-timer.C: + case <-suite.interruptHandler.Status().Channel: + } +} + // TODO: search for usages and consider if reporter.EmitFailure() is necessary func (suite *Suite) failureForLeafNodeWithMessage(node Node, message string) types.Failure { return types.Failure{ diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go index ef66b228..f3e15a91 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go @@ -31,6 +31,7 @@ type DefaultReporter struct { specDenoter string retryDenoter string formatter formatter.Formatter + fdHierarchy []string runningInParallel bool lock *sync.Mutex @@ -82,6 +83,8 @@ func (r *DefaultReporter) SuiteWillBegin(report types.Report) { if report.SuiteConfig.ParallelTotal > 1 { r.emit(r.f("- %d procs ", report.SuiteConfig.ParallelTotal)) } + } else if r.conf.FdOutput { + return } else { banner := r.f("Running Suite: %s - %s", report.SuiteDescription, report.SuitePath) r.emitBlock(banner) @@ -124,7 +127,7 @@ func (r *DefaultReporter) SuiteWillBegin(report types.Report) { func (r *DefaultReporter) SuiteDidEnd(report types.Report) { failures := report.SpecReports.WithState(types.SpecStateFailureStates) - if len(failures) > 0 { + if !r.conf.FdOutput && len(failures) > 0 { r.emitBlock("\n") if len(failures) > 1 { r.emitBlock(r.f("{{red}}{{bold}}Summarizing %d Failures:{{/}}", len(failures))) @@ -227,6 +230,10 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) { return } + if r.conf.FdOutput { + r.didRunFd(report) + return + } header := r.specDenoter if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { header = fmt.Sprintf("[%s]", report.LeafNodeType) @@ -358,6 +365,51 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) { r.emitDelimiter(0) } +func (r *DefaultReporter) didRunFd(report types.SpecReport) { + r.lock.Lock() + defer r.lock.Unlock() + + if !report.LeafNodeType.Is(types.NodeTypeIt) { + return + } + + hierarchy := report.ContainerHierarchyTexts + + // blank line when top-level container changes + if len(r.fdHierarchy) > 0 && + (len(hierarchy) == 0 || hierarchy[0] != r.fdHierarchy[0]) { + fmt.Fprintln(r.writer) + } + + // emit newly-diverged container lines + divergeAt := 0 + for divergeAt < len(r.fdHierarchy) && divergeAt < len(hierarchy) && + r.fdHierarchy[divergeAt] == hierarchy[divergeAt] { + divergeAt++ + } + for i := divergeAt; i < len(hierarchy); i++ { + fmt.Fprintf(r.writer, "%s%s\n", strings.Repeat(" ", i), hierarchy[i]) + } + + // leaf label + depth := len(hierarchy) + indent := strings.Repeat(" ", depth) + label := report.LeafNodeText + + switch report.State { + case types.SpecStateFailed, types.SpecStatePanicked: + label = fmt.Sprintf("%s (FAILED)", label) + case types.SpecStatePending: + label = fmt.Sprintf("%s (PENDING)", label) + case types.SpecStateSkipped: + label = fmt.Sprintf("%s (SKIPPED)", label) + } + + color := r.highlightColorForState(report.State) + fmt.Fprintf(r.writer, "%s%s\n", indent, r.f(color+"%s{{/}}", label)) + r.fdHierarchy = hierarchy +} + func (r *DefaultReporter) highlightColorForState(state types.SpecState) string { switch state { case types.SpecStatePassed: @@ -423,7 +475,7 @@ func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, tim case types.ReportEntry: r.emitReportEntry(indent, x) case types.ProgressReport: - r.emitProgressReport(indent, false, isVeryVerbose, x) + r.emitProgressReport(indent, isVeryVerbose, false, x) case types.SpecEvent: if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents { r.emitSpecEvent(indent, x, isVeryVerbose) @@ -533,6 +585,7 @@ func (r *DefaultReporter) emitProgressReport(indent uint, emitGinkgoWriterOutput indent -= 1 } + // Emit only top-level groups because github logging cannot handle nested groups correctly. if r.conf.GithubOutput && emitGroup { r.emitBlock(r.fi(indent, "::group::Progress Report")) } diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/config.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/config.go index ca64acb2..854fec6a 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/config.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/config.go @@ -38,6 +38,7 @@ type SuiteConfig struct { OutputInterceptorMode string SourceRoots []string GracePeriod time.Duration + SleepOnFailure time.Duration ParallelProcess int ParallelTotal int @@ -94,6 +95,7 @@ type ReporterConfig struct { GithubOutput bool SilenceSkips bool ForceNewlines bool + FdOutput bool JSONReport string GoJSONReport string @@ -293,6 +295,8 @@ var SuiteConfigFlags = GinkgoFlags{ Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."}, {KeyPath: "S.FailOnEmpty", Name: "fail-on-empty", SectionKey: "failure", Usage: "If set, ginkgo will mark the test suite as failed if no specs are run."}, + {KeyPath: "S.SleepOnFailure", Name: "sleep-on-failure", SectionKey: "failure", UsageDefaultValue: "0 - disabled", + Usage: "If set, ginkgo will pause for this duration after a spec fails - before its teardown (AfterEach/JustAfterEach/DeferCleanup) runs - so you can inspect the live system. Press ^C to end the pause early and proceed to cleanup. Serial only: cannot be combined with -p/--procs."}, {KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags", Usage: "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v."}, @@ -358,7 +362,8 @@ var ReporterConfigFlags = GinkgoFlags{ Usage: "If set, default reporter will not print out skipped tests."}, {KeyPath: "R.ForceNewlines", Name: "force-newlines", SectionKey: "output", Usage: "If set, default reporter will ensure a newline appears after each test."}, - + {KeyPath: "R.FdOutput", Name: "fd", SectionKey: "output", + Usage: "If set, emits RSpec-style 'format documentation' output instead of Ginkgo's default output. --fd is exclusive: it overrides -p/-procs and -randomize-all, forcing specs to run serially in declaration order, since fd's hierarchical output can't be rendered sensibly when specs are parallelized or randomized."}, {KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output", Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."}, {KeyPath: "R.GoJSONReport", Name: "gojson-report", UsageArgument: "filename.json", SectionKey: "output", @@ -429,6 +434,14 @@ func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig Re errors = append(errors, GinkgoErrors.GracePeriodCannotBeZero()) } + if suiteConfig.SleepOnFailure < 0 { + errors = append(errors, GinkgoErrors.InvalidSleepOnFailureConfiguration()) + } + + if suiteConfig.SleepOnFailure > 0 && suiteConfig.ParallelTotal > 1 { + errors = append(errors, GinkgoErrors.SleepOnFailureInParallelConfiguration()) + } + if len(suiteConfig.FocusFiles) > 0 { _, err := ParseFileFilters(suiteConfig.FocusFiles) if err != nil { @@ -476,6 +489,30 @@ func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig Re return errors } +// ReconcileFdOutputConfiguration forces serial, in-order execution when --fd is combined with +// -p, -procs (>1), or -randomize-all. fd's RSpec-style hierarchical output assumes specs run +// one at a time, in declaration order -- parallelism and randomization both break that assumption +// and produce fragmented, hard-to-read output. Rather than refusing to run, Ginkgo ignores those +// flags and runs in series instead. suiteConfig and cliConfig are mutated in place; the return +// value is true if anything was overridden, so callers can let the user know what was ignored. +func ReconcileFdOutputConfiguration(reporterConfig ReporterConfig, suiteConfig *SuiteConfig, cliConfig *CLIConfig) bool { + if !reporterConfig.FdOutput { + return false + } + + changed := false + if cliConfig.ComputedProcs() > 1 { + cliConfig.Procs = 1 + cliConfig.Parallel = false + changed = true + } + if suiteConfig.RandomizeAllSpecs { + suiteConfig.RandomizeAllSpecs = false + changed = true + } + return changed +} + // GinkgoCLISharedFlags provides flags shared by the Ginkgo CLI's build, watch, and run commands var GinkgoCLISharedFlags = GinkgoFlags{ {KeyPath: "C.Recurse", Name: "r", SectionKey: "multiple-suites", diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/errors.go index 623e54b6..636070c1 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/errors.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/errors.go @@ -455,7 +455,7 @@ func (g ginkgoErrors) InvalidEmptyComponentForSemVerConstraint(cl CodeLocation) Heading: "Invalid Empty Component for ComponentSemVerConstraint", Message: "ComponentSemVerConstraint requires a non-empty component name", CodeLocation: cl, - DocLink: "spec-semantic-version-filtering", + DocLink: "spec-semantic-version-filtering", } } @@ -621,6 +621,21 @@ func (g ginkgoErrors) GracePeriodCannotBeZero() error { } } +func (g ginkgoErrors) InvalidSleepOnFailureConfiguration() error { + return GinkgoError{ + Heading: "Ginkgo requires a non-negative --sleep-on-failure.", + Message: "Please set --sleep-on-failure to a positive duration (e.g. 5m), or 0 to disable it.", + } +} + +func (g ginkgoErrors) SleepOnFailureInParallelConfiguration() error { + return GinkgoError{ + Heading: "Ginkgo only supports --sleep-on-failure in serial mode.", + Message: "--sleep-on-failure pauses a failed spec on a live system for inspection, which only makes sense when the suite runs serially. Please run again without -p or --procs, or unset --sleep-on-failure.", + DocLink: "spec-timeouts-and-interruptible-nodes", + } +} + func (g ginkgoErrors) ConflictingVerbosityConfiguration() error { return GinkgoError{ Heading: "Conflicting reporter verbosity settings.", diff --git a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/version.go b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/version.go index 003604bd..dc28324c 100644 --- a/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/version.go +++ b/src/notifications/vendor/github.com/onsi/ginkgo/v2/types/version.go @@ -1,3 +1,3 @@ package types -const VERSION = "2.29.0" +const VERSION = "2.32.0" diff --git a/src/notifications/vendor/github.com/onsi/gomega/CHANGELOG.md b/src/notifications/vendor/github.com/onsi/gomega/CHANGELOG.md index 5f9966fb..7cd26c2c 100644 --- a/src/notifications/vendor/github.com/onsi/gomega/CHANGELOG.md +++ b/src/notifications/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.42.1 + +Bump Dependencies + +## 1.42.0 + +Add a set of Claude skill as a marketplace plugin + ## 1.41.0 ### Features diff --git a/src/notifications/vendor/github.com/onsi/gomega/README.md b/src/notifications/vendor/github.com/onsi/gomega/README.md index d45a8c4e..6eb36fdf 100644 --- a/src/notifications/vendor/github.com/onsi/gomega/README.md +++ b/src/notifications/vendor/github.com/onsi/gomega/README.md @@ -6,6 +6,19 @@ Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega If you have a question, comment, bug report, feature request, etc. please open a GitHub issue. +## Using Gomega with Claude Code + +Gomega ships a set of [Claude Code](https://claude.com/claude-code) skills as a **plugin**, so an agent writing assertions in *your* suite has Gomega's idioms — the full matcher catalog, `Eventually`/`Consistently`, and the `gstruct`/`ghttp`/`gexec`/`gbytes`/`gleak`/`gmeasure` sub-libraries — on hand. The Gomega repo doubles as the plugin marketplace, so installation is two commands. From inside Claude Code: + +``` +/plugin marketplace add onsi/gomega +/plugin install gomega@gomega +``` + +(or non-interactively: `claude plugin marketplace add onsi/gomega` then `claude plugin install gomega@gomega`) + +This installs a family of `gomega:*` skills that activate automatically while you write tests. See the [docs](http://onsi.github.io/gomega/#using-gomega-with-claude-code) for the full list. + ## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/) diff --git a/src/notifications/vendor/github.com/onsi/gomega/gomega_dsl.go b/src/notifications/vendor/github.com/onsi/gomega/gomega_dsl.go index df16ede1..db84085b 100644 --- a/src/notifications/vendor/github.com/onsi/gomega/gomega_dsl.go +++ b/src/notifications/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -22,7 +22,7 @@ import ( "github.com/onsi/gomega/types" ) -const GOMEGA_VERSION = "1.41.0" +const GOMEGA_VERSION = "1.42.1" const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). diff --git a/src/notifications/vendor/golang.org/x/net/html/entity.go b/src/notifications/vendor/golang.org/x/net/html/entity.go index b628880a..4e8d5d55 100644 --- a/src/notifications/vendor/golang.org/x/net/html/entity.go +++ b/src/notifications/vendor/golang.org/x/net/html/entity.go @@ -2156,9 +2156,8 @@ var entity = map[string]rune{ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff --git a/src/notifications/vendor/golang.org/x/net/html/escape.go b/src/notifications/vendor/golang.org/x/net/html/escape.go index 12f22737..df3edc5b 100644 --- a/src/notifications/vendor/golang.org/x/net/html/escape.go +++ b/src/notifications/vendor/golang.org/x/net/html/escape.go @@ -6,6 +6,7 @@ package html import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ var replacementTable = [...]rune{ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ + } + + if i == i0 { // No characters matched. + return '&', 0, 1 } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) + } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". diff --git a/src/notifications/vendor/golang.org/x/net/html/foreign.go b/src/notifications/vendor/golang.org/x/net/html/foreign.go index e8515d8e..65d01d1e 100644 --- a/src/notifications/vendor/golang.org/x/net/html/foreign.go +++ b/src/notifications/vendor/golang.org/x/net/html/foreign.go @@ -23,7 +23,7 @@ func adjustForeignAttributes(aa []Attribute) { } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff --git a/src/notifications/vendor/golang.org/x/net/html/parse.go b/src/notifications/vendor/golang.org/x/net/html/parse.go index b3d2a255..165b6108 100644 --- a/src/notifications/vendor/golang.org/x/net/html/parse.go +++ b/src/notifications/vendor/golang.org/x/net/html/parse.go @@ -63,7 +63,7 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -78,7 +78,6 @@ const ( tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -133,10 +132,6 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } @@ -460,21 +455,6 @@ func (p *parser) resetInsertionMode() { } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -1002,7 +982,10 @@ func inBodyIM(p *parser) bool { p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1040,7 +1023,18 @@ func inBodyIM(p *parser) bool { p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1061,7 +1055,13 @@ func inBodyIM(p *parser) bool { p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1095,13 +1095,30 @@ func inBodyIM(p *parser) bool { // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1149,7 +1166,12 @@ func inBodyIM(p *parser) bool { return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1488,17 +1510,6 @@ func inTableIM(p *parser) bool { } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1547,12 +1558,6 @@ func inCaptionIM(p *parser) bool { p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1762,12 +1767,6 @@ func inCellIM(p *parser) bool { } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1798,118 +1797,6 @@ func inCellIM(p *parser) bool { return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore