1 package integration 2 3 import ( 4 "encoding/json" 5 "io" 6 "net/http" 7 "strings" 8 "sync" 9 10 "edge-infra.dev/pkg/edge/api/testutils" 11 "edge-infra.dev/pkg/edge/api/utils" 12 13 "github.com/google/uuid" 14 ) 15 16 type MockCouchDBServer struct { 17 sync.Mutex 18 *utils.MockHTTPTestServer 19 data map[string]bool 20 } 21 22 func NewMockCouchDBServer() *MockCouchDBServer { 23 testServer := utils.NewMockHTTPTestServer().AddAllowedContentType("application/json") 24 25 server := &MockCouchDBServer{ 26 MockHTTPTestServer: testServer, 27 data: make(map[string]bool), 28 } 29 30 return server.init() 31 } 32 33 func (c *MockCouchDBServer) init() *MockCouchDBServer { 34 c.Head("Ping", "/_up", c.emptyOk, nil) 35 36 c.AddNotFound(func(w http.ResponseWriter, r *http.Request) { 37 c.Lock() 38 defer c.Unlock() 39 log.Info("Not Found, request not mocked", 40 "Method", r.Method, 41 "URL", r.URL, 42 "Header", r.Header, 43 ) 44 w.WriteHeader(404) 45 _, _ = w.Write([]byte(nil)) 46 }) 47 48 c.Post("Session", "/_session", func(w http.ResponseWriter, _ *http.Request) { 49 c.Lock() 50 defer c.Unlock() 51 data := map[string]interface{}{ 52 "ok": true, 53 "userCtx": map[string]interface{}{ 54 "name": cushionNS, 55 "roles": []string{"_admin"}, 56 }, 57 } 58 c.ok(w, data) 59 }, nil) 60 return c 61 } 62 63 func (c *MockCouchDBServer) MockDB(db string) { 64 c.Head("Database Exists", "/"+db, func(w http.ResponseWriter, _ *http.Request) { 65 c.Lock() 66 defer c.Unlock() 67 if _, ok := c.data[db]; !ok { 68 w.WriteHeader(404) 69 _, _ = w.Write([]byte(nil)) 70 return 71 } 72 log.Info("db found", "db", db) 73 c.ok(w, struct{}{}) 74 }, nil) 75 76 c.Put("Create Database", "/"+db, func(w http.ResponseWriter, _ *http.Request) { 77 c.Lock() 78 defer c.Unlock() 79 if _, ok := c.data[db]; !ok { 80 c.data[db] = true 81 } 82 c.ok(w, struct{}{}) 83 }, nil) 84 85 c.Post("Post Bulk Docs", "/"+db+"/_bulk_docs", func(w http.ResponseWriter, r *http.Request) { 86 c.Lock() 87 defer c.Unlock() 88 89 if _, ok := c.data[db]; !ok { 90 w.WriteHeader(404) 91 _, _ = w.Write([]byte(nil)) 92 return 93 } 94 95 data := map[string][]map[string]interface{}{} 96 body, _ := io.ReadAll(r.Body) 97 log.Info("Request HEADERS _bulk_docs", "Header", r.Header) 98 log.Info("Request Body _bulk_docs", "body", string(body)) 99 err := json.Unmarshal(body, &data) 100 if err != nil { 101 log.Error(err, "fail to post _bulk_docs") 102 testutils.WriteHTTPBadResponse(w) 103 return 104 } 105 106 newDocs := data["docs"] 107 var res []map[string]interface{} 108 for _, doc := range newDocs { 109 id := doc["_id"].(string) 110 rev := "1-" + strings.ReplaceAll(uuid.NewString(), "-", "") 111 doc["_rev"] = rev 112 res = append(res, map[string]interface{}{ 113 "ok": true, 114 "id": id, 115 "rev": rev, 116 }) 117 c.Get("Get Document "+id, "/"+db+"/"+id, func(w http.ResponseWriter, _ *http.Request) { 118 c.Lock() 119 defer c.Unlock() 120 log.Info("doc found", "db", db) 121 c.ok(w, doc) 122 }, nil) 123 } 124 c.ok(w, res) 125 }, nil) 126 127 // replication doc 128 if strings.HasPrefix(db, "repl-") { 129 c.Put("Create Database", "/"+db+"/repl_doc", func(w http.ResponseWriter, r *http.Request) { 130 c.Lock() 131 defer c.Unlock() 132 if _, ok := c.data[db]; !ok { 133 c.data[db] = true 134 } 135 136 data := map[string]interface{}{} 137 body, _ := io.ReadAll(r.Body) 138 err := json.Unmarshal(body, &data) 139 if err != nil { 140 log.Error(err, "fail to post _bulk_docs") 141 testutils.WriteHTTPBadResponse(w) 142 return 143 } 144 145 c.Get("Get Replication Document", "/"+db+"/repl_doc", func(w http.ResponseWriter, _ *http.Request) { 146 c.Lock() 147 defer c.Unlock() 148 log.Info("doc found", "db", db) 149 c.ok(w, data) 150 }, nil) 151 152 c.ok(w, map[string]interface{}{ 153 "ok": true, 154 }) 155 }, nil) 156 } 157 } 158 159 func (c *MockCouchDBServer) emptyOk(w http.ResponseWriter, _ *http.Request) { 160 c.Lock() 161 defer c.Unlock() 162 c.ok(w, struct{}{}) 163 } 164 165 func (c *MockCouchDBServer) ok(w http.ResponseWriter, data interface{}) { 166 res, err := json.Marshal(data) 167 if err != nil { 168 testutils.WriteHTTPBadResponse(w) 169 return 170 } 171 w.Header().Set("Content-Type", "application/json") 172 _, _ = w.Write(res) 173 } 174