You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
9.1 KiB

8 years ago
8 years ago
8 years ago
8 years ago
  1. package travisci
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "net/http"
  8. "net/http/httptest"
  9. "strings"
  10. "testing"
  11. "github.com/matrix-org/go-neb/database"
  12. "github.com/matrix-org/go-neb/testutils"
  13. "github.com/matrix-org/go-neb/types"
  14. "maunium.net/go/mautrix"
  15. mevt "maunium.net/go/mautrix/event"
  16. )
  17. const travisOrgPEMPublicKey = (`-----BEGIN PUBLIC KEY-----
  18. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvtjdLkS+FP+0fPC09j25
  19. y/PiuYDDivIT86COVedvlElk99BBYTrqNaJybxjXbIZ1Q6xFNhOY+iTcBr4E1zJu
  20. tizF3Xi0V9tOuP/M8Wn4Y/1lCWbQKlWrNQuqNBmhovF4K3mDCYswVbpgTmp+JQYu
  21. Bm9QMdieZMNry5s6aiMA9aSjDlNyedvSENYo18F+NYg1J0C0JiPYTxheCb4optr1
  22. 5xNzFKhAkuGs4XTOA5C7Q06GCKtDNf44s/CVE30KODUxBi0MCKaxiXw/yy55zxX2
  23. /YdGphIyQiA5iO1986ZmZCLLW8udz9uhW5jUr3Jlp9LbmphAC61bVSf4ou2YsJaN
  24. 0QIDAQAB
  25. -----END PUBLIC KEY-----`)
  26. const travisComPEMPublicKey = (`-----BEGIN PUBLIC KEY-----
  27. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnQU2j9lnRtyuW36arNOc
  28. dzCzyKVirLUi3/aLh6UfnTVXzTnx8eHUnBn1ZeQl7Eh3J3qqdbIKl6npS27ONzCy
  29. 3PIcfjpLPaVyGagIL8c8XgDEvB45AesC0osVP5gkXQkPUM3B2rrUmp1AZzG+Fuo0
  30. SAeNnS71gN63U3brL9fN/MTCXJJ6TvMt3GrcJUq5uq56qNeJTsiowK6eiiWFUSfh
  31. e1qapOdMFmcEs9J/R1XQ/scxbAnLcWfl8lqH/MjMdCMe0j3X2ZYMTqOHsb3cQGSS
  32. dMPwZGeLWV+OaxjJ7TrJ+riqMANOgqCBGpvWUnUfo046ACOx7p6u4fFc3aRiuqYK
  33. VQIDAQAB
  34. -----END PUBLIC KEY-----`)
  35. const exampleSignature = ("pW0CDpmcAeWw3qf2Ufx8UvzfrZRUpYx30HBl9nJcDkh2v9FrF1GjJVsrcqx7ly0FPjb7dkfMJ/d0Q3JpDb1EL4p509cN4Vy8+HpfINw35Wg6JqzOQqTa" +
  36. "TidwoDLXo0NgL78zfiL3dra7ZwOGTA+LmnLSuNp38ROxn70u26uqJzWprGSdVNbRu1LkF1QKLa61NZegfxK7RZn1PlIsznWIyTS0qj81mg2sXMDLH1J4" +
  37. "pHxjEpzydjSb5b8tCjrN+vFLDdAtP5RjU8NwvQM4LRRGbLDIlRsO77HDwfXrPgUE3DjPIqVpHhMcCusygp0ClH2b1J1O3LkhxSS9ol5w99Hkpg==")
  38. const exampleBody = ("payload=%7B%22id%22%3A176075135%2C%22repository%22%3A%7B%22id%22%3A6461770%2C%22name%22%3A%22flow-jsdoc%22%2C%22owner_" +
  39. "name%22%3A%22Kegsay%22%2C%22url%22%3Anull%7D%2C%22number%22%3A%2218%22%2C%22config%22%3A%7B%22notifications%22%3A%7B%22web" +
  40. "hooks%22%3A%5B%22http%3A%2F%2F7abbe705.ngrok.io%22%5D%7D%2C%22language%22%3A%22node_js%22%2C%22node_js%22%3A%5B%224.1%22%5D%2C%22.resu" +
  41. "lt%22%3A%22configured%22%2C%22group%22%3A%22stable%22%2C%22dist%22%3A%22precise%22%7D%2C%22status%22%3A0%2C%22result%22%3A0%2C%22status_" +
  42. "message%22%3A%22Passed%22%2C%22result_message%22%3A%22Passed%22%2C%22started_at%22%3A%222016-11-15T15%3A10%3A22Z%22%2C%22finished_" +
  43. "at%22%3A%222016-11-15T15%3A10%3A54Z%22%2C%22duration%22%3A32%2C%22build_url%22%3A%22https%3A%2F%2Ftravis-ci.org%2FKegsay%2Fflow-js" +
  44. "doc%2Fbuilds%2F176075135%22%2C%22commit_id%22%3A50222535%2C%22commit%22%3A%223a092c3a6032ebb50384c99b445f947e9ce86e2a%22%2C%22base_com" +
  45. "mit%22%3Anull%2C%22head_commit%22%3Anull%2C%22branch%22%3A%22master%22%2C%22message%22%3A%22Test+Travis+webhook+support%22%2C%22compare_" +
  46. "url%22%3A%22https%3A%2F%2Fgithub.com%2FKegsay%2Fflow-jsdoc%2Fcompare%2F9f9d459ba082...3a092c3a6032%22%2C%22committed_at%22%3A%222016-1" +
  47. "1-15T15%3A08%3A16Z%22%2C%22author_name%22%3A%22Kegan+Dougal%22%2C%22author_email%22%3A%22kegan%40matrix.org%22%2C%22committer_" +
  48. "name%22%3A%22Kegan+Dougal%22%2C%22committer_email%22%3A%22kegan%40matrix.org%22%2C%22matrix%22%3A%5B%7B%22id%22%3A176075137%2C%22reposit" +
  49. "ory_id%22%3A6461770%2C%22parent_id%22%3A176075135%2C%22number%22%3A%2218.1%22%2C%22state%22%3A%22finished%22%2C%22config%22%3A%7B%22notifi" +
  50. "cations%22%3A%7B%22webhooks%22%3A%5B%22http%3A%2F%2F7abbe705.ngrok.io%22%5D%7D%2C%22language%22%3A%22node_js%22%2C%22node_" +
  51. "js%22%3A%224.1%22%2C%22.result%22%3A%22configured%22%2C%22group%22%3A%22stable%22%2C%22dist%22%3A%22precise%22%2C%22os%22%3A%22li" +
  52. "nux%22%7D%2C%22status%22%3A0%2C%22result%22%3A0%2C%22commit%22%3A%223a092c3a6032ebb50384c99b445f947e9ce86e2a%22%2C%22branch%22%3A%22mas" +
  53. "ter%22%2C%22message%22%3A%22Test+Travis+webhook+support%22%2C%22compare_url%22%3A%22https%3A%2F%2Fgithub.com%2FKegsay%2Fflow-jsdoc%2Fcomp" +
  54. "are%2F9f9d459ba082...3a092c3a6032%22%2C%22started_at%22%3A%222016-11-15T15%3A10%3A22Z%22%2C%22finished_at%22%3A%222016-11-" +
  55. "15T15%3A10%3A54Z%22%2C%22committed_at%22%3A%222016-11-15T15%3A08%3A16Z%22%2C%22author_name%22%3A%22Kegan+Dougal%22%2C%22author_ema" +
  56. "il%22%3A%22kegan%40matrix.org%22%2C%22committer_name%22%3A%22Kegan+Dougal%22%2C%22committer_email%22%3A%22kegan%40matrix.org%22%2C%22allow_f" +
  57. "ailure%22%3Afalse%7D%5D%2C%22type%22%3A%22push%22%2C%22state%22%3A%22passed%22%2C%22pull_request%22%3Afalse%2C%22pull_request_number%22%3Anu" +
  58. "ll%2C%22pull_request_title%22%3Anull%2C%22tag%22%3Anull%7D")
  59. var travisTests = []struct {
  60. Signature string
  61. ValidSignature bool
  62. Body string
  63. Template string
  64. ExpectedOutput string
  65. }{
  66. {
  67. exampleSignature, true, exampleBody,
  68. "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}",
  69. "Kegsay/flow-jsdoc#18 (master - 3a092c3a60 : Kegan Dougal): Passed",
  70. },
  71. {
  72. "obviously_invalid_signature", false, exampleBody,
  73. "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}",
  74. "Kegsay/flow-jsdoc#18 (master - 3a092c3a60 : Kegan Dougal): Passed",
  75. },
  76. {
  77. // Payload is valid but doesn't match signature now
  78. exampleSignature, false, strings.TrimSuffix(exampleBody, "%7D") + "%2C%22EXTRA_KEY%22%3Anull%7D",
  79. "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}",
  80. "Kegsay/flow-jsdoc#18 (master - 3a092c3a60 : Kegan Dougal): Passed",
  81. },
  82. {
  83. exampleSignature, true, exampleBody,
  84. "%{repository}#%{build_number} %{duration}",
  85. "Kegsay/flow-jsdoc#18 32s",
  86. },
  87. }
  88. func TestTravisCI(t *testing.T) {
  89. database.SetServiceDB(&database.NopStorage{})
  90. // When the service tries to get Travis' public key, return the constant
  91. urlToKey := make(map[string]string)
  92. urlToKey["https://api.travis-ci.org/config"] = travisOrgPEMPublicKey
  93. urlToKey["https://api.travis-ci.com/config"] = travisComPEMPublicKey
  94. travisTransport := testutils.NewRoundTripper(func(req *http.Request) (*http.Response, error) {
  95. if key := urlToKey[req.URL.String()]; key != "" {
  96. escKey, _ := json.Marshal(key)
  97. return &http.Response{
  98. StatusCode: 200,
  99. Body: ioutil.NopCloser(bytes.NewBufferString(
  100. `{"config":{"notifications":{"webhook":{"public_key":` + string(escKey) + `}}}}`,
  101. )),
  102. }, nil
  103. }
  104. return nil, fmt.Errorf("Unhandled URL %s", req.URL.String())
  105. })
  106. // clobber the http client that the service uses to talk to Travis
  107. httpClient = &http.Client{Transport: travisTransport}
  108. // Intercept message sending to Matrix and mock responses
  109. msgs := []mevt.MessageEventContent{}
  110. matrixTrans := struct{ testutils.MockTransport }{}
  111. matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
  112. if !strings.Contains(req.URL.String(), "/send/m.room.message") {
  113. return nil, fmt.Errorf("Unhandled URL: %s", req.URL.String())
  114. }
  115. var msg mevt.MessageEventContent
  116. if err := json.NewDecoder(req.Body).Decode(&msg); err != nil {
  117. return nil, fmt.Errorf("Failed to decode request JSON: %s", err)
  118. }
  119. msgs = append(msgs, msg)
  120. return &http.Response{
  121. StatusCode: 200,
  122. Body: ioutil.NopCloser(bytes.NewBufferString(`{"event_id":"$yup:event"}`)),
  123. }, nil
  124. }
  125. matrixCli, _ := mautrix.NewClient("https://hyrule", "@travisci:hyrule", "its_a_secret")
  126. matrixCli.Client = &http.Client{Transport: matrixTrans}
  127. // BEGIN running the Travis-CI table tests
  128. // ---------------------------------------
  129. for _, test := range travisTests {
  130. msgs = []mevt.MessageEventContent{} // reset sent messages
  131. mockWriter := httptest.NewRecorder()
  132. travis := makeService(t, test.Template)
  133. if travis == nil {
  134. t.Error("TestTravisCI Failed to create service")
  135. continue
  136. }
  137. if err := travis.Register(nil, matrixCli); err != nil {
  138. t.Errorf("TestTravisCI Failed to Register(): %s", err)
  139. continue
  140. }
  141. req, err := http.NewRequest(
  142. "POST", "https://neb.endpoint/travis-ci-service", bytes.NewBufferString(test.Body),
  143. )
  144. if err != nil {
  145. t.Errorf("TestTravisCI Failed to create webhook request: %s", err)
  146. continue
  147. }
  148. req.Header.Set("Signature", test.Signature)
  149. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  150. travis.OnReceiveWebhook(mockWriter, req, matrixCli)
  151. if test.ValidSignature {
  152. if !assertResponse(t, mockWriter, msgs, 200, 1) {
  153. continue
  154. }
  155. if msgs[0].Body != test.ExpectedOutput {
  156. t.Errorf("TestTravisCI want matrix body '%s', got '%s'", test.ExpectedOutput, msgs[0].Body)
  157. }
  158. } else {
  159. assertResponse(t, mockWriter, msgs, 403, 0)
  160. }
  161. }
  162. }
  163. func assertResponse(t *testing.T, w *httptest.ResponseRecorder, msgs []mevt.MessageEventContent, expectCode int, expectMsgLength int) bool {
  164. if w.Code != expectCode {
  165. t.Errorf("TestTravisCI OnReceiveWebhook want HTTP code %d, got %d", expectCode, w.Code)
  166. return false
  167. }
  168. if len(msgs) != expectMsgLength {
  169. t.Errorf("TestTravisCI want %d sent messages, got %d ", expectMsgLength, len(msgs))
  170. return false
  171. }
  172. return true
  173. }
  174. func makeService(t *testing.T, template string) *Service {
  175. srv, err := types.CreateService("id", ServiceType, "@travisci:hyrule", []byte(
  176. `{
  177. "rooms":{
  178. "!ewfug483gsfe:localhost": {
  179. "repos": {
  180. "Kegsay/flow-jsdoc": {
  181. "template": "`+template+`"
  182. }
  183. }
  184. }
  185. }
  186. }`,
  187. ))
  188. if err != nil {
  189. t.Error("Failed to create Travis-CI service: ", err)
  190. return nil
  191. }
  192. return srv.(*Service)
  193. }