sdwan_handlers_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package api
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "net/http"
  6. "net/http/httptest"
  7. "path/filepath"
  8. "testing"
  9. "debtik/internal/auth"
  10. "debtik/internal/sdwan"
  11. )
  12. func setupTestSDWANServer(t *testing.T) (*Server, *sdwan.Manager) {
  13. tmpDir := t.TempDir()
  14. cfgPath := filepath.Join(tmpDir, "sdwan.json")
  15. sdwanMgr := sdwan.NewManager(cfgPath)
  16. authPath := filepath.Join(tmpDir, "users.json")
  17. authMgr := auth.NewManager(authPath)
  18. srv := &Server{
  19. sdwanMgr: sdwanMgr,
  20. authMgr: authMgr,
  21. mux: http.NewServeMux(),
  22. fsHandler: http.NotFoundHandler(),
  23. }
  24. srv.registerRoutes()
  25. return srv, sdwanMgr
  26. }
  27. func TestSDWANAPIEndpoints(t *testing.T) {
  28. srv, sdwanMgr := setupTestSDWANServer(t)
  29. defer sdwanMgr.Stop()
  30. // 1. GET /api/sdwan/groups (Initial empty list)
  31. req := httptest.NewRequest(http.MethodGet, "/api/sdwan/groups", nil)
  32. rec := httptest.NewRecorder()
  33. srv.mux.ServeHTTP(rec, req)
  34. if rec.Code != http.StatusOK {
  35. t.Fatalf("GET /api/sdwan/groups status = %d; want 200", rec.Code)
  36. }
  37. var groups []*sdwan.SDWANGroup
  38. if err := json.NewDecoder(rec.Body).Decode(&groups); err != nil {
  39. t.Fatalf("failed to decode groups: %v", err)
  40. }
  41. if len(groups) != 0 {
  42. t.Fatalf("expected 0 groups, got %d", len(groups))
  43. }
  44. // 2. POST /api/sdwan/groups (Create group)
  45. newGroup := sdwan.SDWANGroup{
  46. Name: "sdwan0",
  47. Mode: sdwan.ModeWeighted,
  48. AutoNAT: true,
  49. ProbeInterval: 3,
  50. Comment: "Primary Multi-WAN",
  51. }
  52. body, _ := json.Marshal(newGroup)
  53. req = httptest.NewRequest(http.MethodPost, "/api/sdwan/groups", bytes.NewReader(body))
  54. rec = httptest.NewRecorder()
  55. srv.mux.ServeHTTP(rec, req)
  56. if rec.Code != http.StatusOK {
  57. t.Fatalf("POST /api/sdwan/groups status = %d; body = %s", rec.Code, rec.Body.String())
  58. }
  59. // 3. GET /api/sdwan/groups (Check group created)
  60. req = httptest.NewRequest(http.MethodGet, "/api/sdwan/groups", nil)
  61. rec = httptest.NewRecorder()
  62. srv.mux.ServeHTTP(rec, req)
  63. if rec.Code != http.StatusOK {
  64. t.Fatalf("GET /api/sdwan/groups status = %d", rec.Code)
  65. }
  66. groups = nil
  67. _ = json.NewDecoder(rec.Body).Decode(&groups)
  68. if len(groups) != 1 || groups[0].Name != "sdwan0" {
  69. t.Fatalf("expected 1 group named sdwan0, got %+v", groups)
  70. }
  71. groupID := groups[0].ID
  72. // 4. POST /api/sdwan/groups/{id}/members (Add member)
  73. memPayload := map[string]interface{}{
  74. "interface": "eth0",
  75. "gateway": "192.168.1.1",
  76. "weight": 60,
  77. "priority": 1,
  78. "target_probe": "1.1.1.1",
  79. }
  80. body, _ = json.Marshal(memPayload)
  81. req = httptest.NewRequest(http.MethodPost, "/api/sdwan/groups/"+groupID+"/members", bytes.NewReader(body))
  82. rec = httptest.NewRecorder()
  83. srv.mux.ServeHTTP(rec, req)
  84. if rec.Code != http.StatusOK {
  85. t.Fatalf("POST /api/sdwan/groups/%s/members status = %d; body = %s", groupID, rec.Code, rec.Body.String())
  86. }
  87. // 4a. PUT /api/sdwan/groups/{id}/members (Update member via PUT)
  88. memPayload["weight"] = 75
  89. body, _ = json.Marshal(memPayload)
  90. req = httptest.NewRequest(http.MethodPut, "/api/sdwan/groups/"+groupID+"/members", bytes.NewReader(body))
  91. rec = httptest.NewRecorder()
  92. srv.mux.ServeHTTP(rec, req)
  93. if rec.Code != http.StatusOK {
  94. t.Fatalf("PUT /api/sdwan/groups/%s/members status = %d; body = %s", groupID, rec.Code, rec.Body.String())
  95. }
  96. // 4b. POST /api/sdwan/groups/{id}/members (Update member via POST when already exists)
  97. memPayload["weight"] = 85
  98. body, _ = json.Marshal(memPayload)
  99. req = httptest.NewRequest(http.MethodPost, "/api/sdwan/groups/"+groupID+"/members", bytes.NewReader(body))
  100. rec = httptest.NewRecorder()
  101. srv.mux.ServeHTTP(rec, req)
  102. if rec.Code != http.StatusOK {
  103. t.Fatalf("POST (update) /api/sdwan/groups/%s/members status = %d; body = %s", groupID, rec.Code, rec.Body.String())
  104. }
  105. gAfterUpdate, _ := sdwanMgr.Get(groupID)
  106. if len(gAfterUpdate.Members) != 1 || gAfterUpdate.Members[0].Weight != 85 {
  107. t.Fatalf("expected member weight 85, got %+v", gAfterUpdate.Members)
  108. }
  109. // 5. GET /api/sdwan/telemetry
  110. req = httptest.NewRequest(http.MethodGet, "/api/sdwan/telemetry", nil)
  111. rec = httptest.NewRecorder()
  112. srv.mux.ServeHTTP(rec, req)
  113. if rec.Code != http.StatusOK {
  114. t.Fatalf("GET /api/sdwan/telemetry status = %d", rec.Code)
  115. }
  116. var telemetry sdwan.TelemetrySummary
  117. if err := json.NewDecoder(rec.Body).Decode(&telemetry); err != nil {
  118. t.Fatalf("failed to decode telemetry: %v", err)
  119. }
  120. if telemetry.TotalGroups != 1 || telemetry.TotalMembers != 1 {
  121. t.Fatalf("unexpected telemetry: %+v", telemetry)
  122. }
  123. // 6. POST /api/sdwan/groups/{id}/state (Disable group)
  124. statePayload := map[string]interface{}{
  125. "disabled": true,
  126. }
  127. body, _ = json.Marshal(statePayload)
  128. req = httptest.NewRequest(http.MethodPost, "/api/sdwan/groups/"+groupID+"/state", bytes.NewReader(body))
  129. rec = httptest.NewRecorder()
  130. srv.mux.ServeHTTP(rec, req)
  131. if rec.Code != http.StatusOK {
  132. t.Fatalf("POST /api/sdwan/groups/%s/state status = %d", groupID, rec.Code)
  133. }
  134. // 7. DELETE /api/sdwan/groups/{id}/members/{iface}
  135. req = httptest.NewRequest(http.MethodDelete, "/api/sdwan/groups/"+groupID+"/members/eth0", nil)
  136. rec = httptest.NewRecorder()
  137. srv.mux.ServeHTTP(rec, req)
  138. if rec.Code != http.StatusOK {
  139. t.Fatalf("DELETE /api/sdwan/groups/%s/members/eth0 status = %d; body = %s", groupID, rec.Code, rec.Body.String())
  140. }
  141. // 8. DELETE /api/sdwan/groups/{id}
  142. req = httptest.NewRequest(http.MethodDelete, "/api/sdwan/groups/"+groupID, nil)
  143. rec = httptest.NewRecorder()
  144. srv.mux.ServeHTTP(rec, req)
  145. if rec.Code != http.StatusOK {
  146. t.Fatalf("DELETE /api/sdwan/groups/%s status = %d; body = %s", groupID, rec.Code, rec.Body.String())
  147. }
  148. }