http_test.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package notifications
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "mime"
  6. "net/http"
  7. "net/http/httptest"
  8. "reflect"
  9. "strconv"
  10. "testing"
  11. "github.com/docker/distribution/manifest/schema1"
  12. )
  13. // TestHTTPSink mocks out an http endpoint and notifies it under a couple of
  14. // conditions, ensuring correct behavior.
  15. func TestHTTPSink(t *testing.T) {
  16. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  17. defer r.Body.Close()
  18. if r.Method != "POST" {
  19. w.WriteHeader(http.StatusMethodNotAllowed)
  20. t.Fatalf("unexpected request method: %v", r.Method)
  21. return
  22. }
  23. // Extract the content type and make sure it matches
  24. contentType := r.Header.Get("Content-Type")
  25. mediaType, _, err := mime.ParseMediaType(contentType)
  26. if err != nil {
  27. w.WriteHeader(http.StatusBadRequest)
  28. t.Fatalf("error parsing media type: %v, contenttype=%q", err, contentType)
  29. return
  30. }
  31. if mediaType != EventsMediaType {
  32. w.WriteHeader(http.StatusUnsupportedMediaType)
  33. t.Fatalf("incorrect media type: %q != %q", mediaType, EventsMediaType)
  34. return
  35. }
  36. var envelope Envelope
  37. dec := json.NewDecoder(r.Body)
  38. if err := dec.Decode(&envelope); err != nil {
  39. w.WriteHeader(http.StatusBadRequest)
  40. t.Fatalf("error decoding request body: %v", err)
  41. return
  42. }
  43. // Let caller choose the status
  44. status, err := strconv.Atoi(r.FormValue("status"))
  45. if err != nil {
  46. t.Logf("error parsing status: %v", err)
  47. // May just be empty, set status to 200
  48. status = http.StatusOK
  49. }
  50. w.WriteHeader(status)
  51. }))
  52. metrics := newSafeMetrics()
  53. sink := newHTTPSink(server.URL, 0, nil,
  54. &endpointMetricsHTTPStatusListener{safeMetrics: metrics})
  55. var expectedMetrics EndpointMetrics
  56. expectedMetrics.Statuses = make(map[string]int)
  57. for _, tc := range []struct {
  58. events []Event // events to send
  59. url string
  60. failure bool // true if there should be a failure.
  61. statusCode int // if not set, no status code should be incremented.
  62. }{
  63. {
  64. statusCode: http.StatusOK,
  65. events: []Event{
  66. createTestEvent("push", "library/test", schema1.MediaTypeSignedManifest)},
  67. },
  68. {
  69. statusCode: http.StatusOK,
  70. events: []Event{
  71. createTestEvent("push", "library/test", schema1.MediaTypeSignedManifest),
  72. createTestEvent("push", "library/test", layerMediaType),
  73. createTestEvent("push", "library/test", layerMediaType),
  74. },
  75. },
  76. {
  77. statusCode: http.StatusTemporaryRedirect,
  78. },
  79. {
  80. statusCode: http.StatusBadRequest,
  81. failure: true,
  82. },
  83. {
  84. // Case where connection never goes through.
  85. url: "http://shoudlntresolve/",
  86. failure: true,
  87. },
  88. } {
  89. if tc.failure {
  90. expectedMetrics.Failures += len(tc.events)
  91. } else {
  92. expectedMetrics.Successes += len(tc.events)
  93. }
  94. if tc.statusCode > 0 {
  95. expectedMetrics.Statuses[fmt.Sprintf("%d %s", tc.statusCode, http.StatusText(tc.statusCode))] += len(tc.events)
  96. }
  97. url := tc.url
  98. if url == "" {
  99. url = server.URL + "/"
  100. }
  101. // setup endpoint to respond with expected status code.
  102. url += fmt.Sprintf("?status=%v", tc.statusCode)
  103. sink.url = url
  104. t.Logf("testcase: %v, fail=%v", url, tc.failure)
  105. // Try a simple event emission.
  106. err := sink.Write(tc.events...)
  107. if !tc.failure {
  108. if err != nil {
  109. t.Fatalf("unexpected error send event: %v", err)
  110. }
  111. } else {
  112. if err == nil {
  113. t.Fatalf("the endpoint should have rejected the request")
  114. }
  115. }
  116. if !reflect.DeepEqual(metrics.EndpointMetrics, expectedMetrics) {
  117. t.Fatalf("metrics not as expected: %#v != %#v", metrics.EndpointMetrics, expectedMetrics)
  118. }
  119. }
  120. if err := sink.Close(); err != nil {
  121. t.Fatalf("unexpected error closing http sink: %v", err)
  122. }
  123. // double close returns error
  124. if err := sink.Close(); err == nil {
  125. t.Fatalf("second close should have returned error: %v", err)
  126. }
  127. }
  128. func createTestEvent(action, repo, typ string) Event {
  129. event := createEvent(action)
  130. event.Target.MediaType = typ
  131. event.Target.Repository = repo
  132. return *event
  133. }