main_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package main
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "net/http/httptest"
  7. "strings"
  8. "testing"
  9. "time"
  10. )
  11. const secret = "test-secret"
  12. func sign(body string) string {
  13. mac := hmac.New(sha256.New, []byte(secret))
  14. mac.Write([]byte(body))
  15. return hex.EncodeToString(mac.Sum(nil))
  16. }
  17. type call struct{ repo, sha, by string }
  18. func TestHandler(t *testing.T) {
  19. merged := `{"action":"closed","pull_request":{"merged":true,"base_branch":"main","merge_commit_sha":"0bfb14ee4067"},"repository":{"full_name":"bajdev/posts"},"sender":{"username":"bajdev"}}`
  20. // Every release is of origin/main, whatever merge_commit_sha says: Gogs
  21. // puts the head branch's commit there, not the merge commit.
  22. withHead := call{"bajdev/posts", "origin/main", "PR merged by bajdev (head 0bfb14e)"}
  23. tests := []struct {
  24. name, method, event, body, sig string
  25. status int
  26. want *call // nil: no release
  27. }{
  28. {"merged into main", "POST", "pull_request", merged, "", 202, &withHead},
  29. {"no merge commit sha", "POST", "pull_request", strings.Replace(merged, `"0bfb14ee4067"`, `""`, 1), "", 202,
  30. &call{"bajdev/posts", "origin/main", "PR merged by bajdev"}},
  31. {"bad signature", "POST", "pull_request", merged, "deadbeef", 401, nil},
  32. {"missing signature", "POST", "pull_request", merged, "-", 401, nil},
  33. {"GET", "GET", "pull_request", merged, "", 405, nil},
  34. {"push event", "POST", "push", merged, "", 204, nil},
  35. {"opened", "POST", "pull_request", strings.Replace(merged, `"closed"`, `"opened"`, 1), "", 204, nil},
  36. {"closed without merge", "POST", "pull_request", strings.Replace(merged, `"merged":true`, `"merged":false`, 1), "", 204, nil},
  37. {"merged into another branch", "POST", "pull_request", strings.Replace(merged, `"main"`, `"dev"`, 1), "", 204, nil},
  38. {"no repository", "POST", "pull_request", strings.Replace(merged, `"bajdev/posts"`, `""`, 1), "", 204, nil},
  39. {"not JSON", "POST", "pull_request", "not json", "", 204, nil},
  40. }
  41. for _, tt := range tests {
  42. t.Run(tt.name, func(t *testing.T) {
  43. calls := make(chan call, 1)
  44. h := handler([]byte(secret), func(repo, sha, by string) { calls <- call{repo, sha, by} })
  45. req := httptest.NewRequest(tt.method, "/deploy", strings.NewReader(tt.body))
  46. req.Header.Set("X-Gogs-Event", tt.event)
  47. switch tt.sig {
  48. case "":
  49. req.Header.Set("X-Gogs-Signature", sign(tt.body))
  50. case "-":
  51. default:
  52. req.Header.Set("X-Gogs-Signature", tt.sig)
  53. }
  54. rec := httptest.NewRecorder()
  55. h.ServeHTTP(rec, req)
  56. if rec.Code != tt.status {
  57. t.Fatalf("status = %d, want %d", rec.Code, tt.status)
  58. }
  59. select {
  60. case c := <-calls:
  61. if tt.want == nil {
  62. t.Fatalf("unexpected release %+v", c)
  63. }
  64. if c != *tt.want {
  65. t.Errorf("release called with %+v, want %+v", c, *tt.want)
  66. }
  67. case <-time.After(200 * time.Millisecond):
  68. if tt.want != nil {
  69. t.Fatal("release was not called")
  70. }
  71. }
  72. })
  73. }
  74. }
  75. func TestReleasesRunOneAtATime(t *testing.T) {
  76. body := `{"action":"closed","pull_request":{"merged":true,"base_branch":"main","merge_commit_sha":"x"},"repository":{"full_name":"a/b"}}`
  77. running, maxRunning := 0, 0
  78. done := make(chan struct{}, 3)
  79. h := handler([]byte(secret), func(repo, sha, by string) {
  80. // The handler's mutex is held here, so these updates don't race.
  81. running++
  82. if running > maxRunning {
  83. maxRunning = running
  84. }
  85. time.Sleep(20 * time.Millisecond)
  86. running--
  87. done <- struct{}{}
  88. })
  89. for i := 0; i < 3; i++ {
  90. req := httptest.NewRequest("POST", "/deploy", strings.NewReader(body))
  91. req.Header.Set("X-Gogs-Event", "pull_request")
  92. req.Header.Set("X-Gogs-Signature", sign(body))
  93. h.ServeHTTP(httptest.NewRecorder(), req)
  94. }
  95. for i := 0; i < 3; i++ {
  96. <-done
  97. }
  98. if maxRunning != 1 {
  99. t.Errorf("max concurrent releases = %d, want 1", maxRunning)
  100. }
  101. }