grpcserver_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package grpcserver
  2. import (
  3. "testing"
  4. pbv1 "git3.techno-world.net/lrosales/broad-announce/gen/go/broadannounce/v1"
  5. "git3.techno-world.net/lrosales/broad-announce/internal/pipeline"
  6. "google.golang.org/grpc/metadata"
  7. )
  8. func TestAuthenticate(t *testing.T) {
  9. sources := map[string]pipeline.SourceConfig{
  10. "acme-001:prom-prod": {
  11. CompanyID: "acme-001",
  12. SourceID: "prom-prod",
  13. HMACSecret: []byte("s3cret"),
  14. RateLimitPerSec: 1000,
  15. },
  16. }
  17. tests := []struct {
  18. name string
  19. apiKey string
  20. wantErr bool
  21. wantSrcID string
  22. }{
  23. {name: "valid key", apiKey: "acme-001:prom-prod:s3cret", wantErr: false, wantSrcID: "prom-prod"},
  24. {name: "wrong secret", apiKey: "acme-001:prom-prod:wrong", wantErr: true},
  25. {name: "unknown source", apiKey: "acme-001:unknown:s3cret", wantErr: true},
  26. {name: "malformed key", apiKey: "acme-001", wantErr: true},
  27. {name: "empty key", apiKey: "", wantErr: true},
  28. }
  29. for _, tt := range tests {
  30. t.Run(tt.name, func(t *testing.T) {
  31. ctx := metadata.NewIncomingContext(t.Context(), metadata.MD{
  32. "authorization": []string{"Bearer " + tt.apiKey},
  33. })
  34. src, err := authenticate(ctx, sources)
  35. if tt.wantErr {
  36. if err == nil {
  37. t.Errorf("authenticate() = %v, want error", src)
  38. }
  39. return
  40. }
  41. if err != nil {
  42. t.Errorf("authenticate() error = %v, want nil", err)
  43. return
  44. }
  45. if src.SourceID != tt.wantSrcID {
  46. t.Errorf("authenticate() sourceID = %v, want %v", src.SourceID, tt.wantSrcID)
  47. }
  48. })
  49. }
  50. }
  51. func TestErrorCode(t *testing.T) {
  52. tests := []struct {
  53. reason string
  54. want pbv1.Error_Code
  55. }{
  56. {"unknown_source", pbv1.Error_UNAUTHENTICATED},
  57. {"bad_signature", pbv1.Error_UNAUTHENTICATED},
  58. {"rate_limited_source", pbv1.Error_RATE_LIMITED},
  59. {"rate_limited_company", pbv1.Error_RATE_LIMITED},
  60. {"invalid", pbv1.Error_INVALID},
  61. {"invalid_json", pbv1.Error_INVALID},
  62. {"quarantined", pbv1.Error_INVALID},
  63. {"circuit_open", pbv1.Error_INTERNAL},
  64. {"broker_unavailable", pbv1.Error_INTERNAL},
  65. {"marshal_failed", pbv1.Error_INTERNAL},
  66. {"unknown_foo", pbv1.Error_UNKNOWN},
  67. }
  68. for _, tt := range tests {
  69. t.Run(tt.reason, func(t *testing.T) {
  70. if got := errorCode(tt.reason); got != tt.want {
  71. t.Errorf("errorCode(%q) = %v, want %v", tt.reason, got, tt.want)
  72. }
  73. })
  74. }
  75. }
  76. func TestParseRetryAfter(t *testing.T) {
  77. tests := []struct {
  78. input string
  79. want int
  80. ok bool
  81. }{
  82. {"123", 123, true},
  83. {"0", 0, true},
  84. {"5s", 5, true},
  85. {"", 0, false},
  86. {"abc", 0, false},
  87. }
  88. for _, tt := range tests {
  89. t.Run(tt.input, func(t *testing.T) {
  90. n, ok := parseRetryAfter(tt.input)
  91. if n != tt.want || ok != tt.ok {
  92. t.Errorf("parseRetryAfter(%q) = (%d, %v), want (%d, %v)",
  93. tt.input, n, ok, tt.want, tt.ok)
  94. }
  95. })
  96. }
  97. }
  98. func TestAlertToJSON(t *testing.T) {
  99. alert := &pbv1.Alert{
  100. CompanyId: "acme-001",
  101. SourceId: "prom-prod",
  102. Severity: "critical",
  103. Category: "monitoring",
  104. Title: "CPU spike",
  105. Body: "CPU usage above 90%",
  106. DedupeKey: "cpu-spike-001",
  107. ClientTsMs: 1712000000000,
  108. Data: map[string]string{"host": "prod-01", "value": "95"},
  109. }
  110. body, err := alertToJSON(alert)
  111. if err != nil {
  112. t.Fatalf("alertToJSON() error = %v", err)
  113. }
  114. bodyStr := string(body)
  115. for _, want := range []string{
  116. `"company_id":"acme-001"`,
  117. `"source_id":"prom-prod"`,
  118. `"severity":"critical"`,
  119. `"category":"monitoring"`,
  120. `"title":"CPU spike"`,
  121. `"dedupe_key":"cpu-spike-001"`,
  122. `"client_ts_ms":1712000000000`,
  123. } {
  124. if !contains(bodyStr, want) {
  125. t.Errorf("alertToJSON() body missing %q:\n%s", want, body)
  126. }
  127. }
  128. }
  129. func contains(s, substr string) bool {
  130. for i := 0; i <= len(s)-len(substr); i++ {
  131. if s[i:i+len(substr)] == substr {
  132. return true
  133. }
  134. }
  135. return false
  136. }
  137. func TestServerImplementsIngestServer(t *testing.T) {
  138. var _ pbv1.IngestServer = (*Server)(nil)
  139. }