github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/utils/api_test.go (about) 1 package utils 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/Redstoneguy129/cli/internal/testing/apitest" 11 "gopkg.in/h2non/gock.v1" 12 ) 13 14 func TestFallbackDNS(t *testing.T) { 15 const host = "api.supabase.io" 16 17 t.Run("resolves IPv4 with CloudFlare", func(t *testing.T) { 18 // Setup http mock 19 defer gock.OffAll() 20 gock.New("https://1.1.1.1"). 21 Get("/dns-query"). 22 MatchParam("name", host). 23 MatchHeader("accept", "application/dns-json"). 24 Reply(http.StatusOK). 25 JSON(&dnsResponse{Answer: []dnsAnswer{ 26 {Type: dnsIPv4Type, Data: "127.0.0.1"}, 27 }}) 28 // Run test 29 ip := fallbackLookupIP(context.Background(), host+":443") 30 // Validate output 31 assert.Equal(t, "127.0.0.1:443", ip) 32 assert.Empty(t, apitest.ListUnmatchedRequests()) 33 }) 34 35 t.Run("resolves IPv6 recursively", func(t *testing.T) { 36 // Setup http mock 37 defer gock.OffAll() 38 gock.New("https://1.1.1.1"). 39 Get("/dns-query"). 40 MatchParam("name", "api.supabase.com"). 41 MatchHeader("accept", "application/dns-json"). 42 Reply(http.StatusOK). 43 JSON(&dnsResponse{Answer: []dnsAnswer{ 44 {Type: 5, Data: "supabase-api.fly.dev."}, 45 {Type: dnsIPv6Type, Data: "2606:2800:220:1:248:1893:25c8:1946"}, 46 }}) 47 // Run test 48 ip := fallbackLookupIP(context.Background(), "api.supabase.com:443") 49 // Validate output 50 assert.Equal(t, "[2606:2800:220:1:248:1893:25c8:1946]:443", ip) 51 assert.Empty(t, apitest.ListUnmatchedRequests()) 52 }) 53 54 t.Run("empty on malformed address", func(t *testing.T) { 55 assert.Equal(t, "", fallbackLookupIP(context.Background(), "bad?url")) 56 }) 57 58 t.Run("empty on network failure", func(t *testing.T) { 59 // Setup http mock 60 defer gock.OffAll() 61 gock.New("https://1.1.1.1"). 62 Get("/dns-query"). 63 MatchParam("name", host). 64 MatchHeader("accept", "application/dns-json"). 65 ReplyError(errors.New("network error")) 66 // Run test 67 ip := fallbackLookupIP(context.Background(), host+":443") 68 // Validate output 69 assert.Equal(t, "", ip) 70 assert.Empty(t, apitest.ListUnmatchedRequests()) 71 }) 72 73 t.Run("empty on service unavailable", func(t *testing.T) { 74 // Setup http mock 75 defer gock.OffAll() 76 gock.New("https://1.1.1.1"). 77 Get("/dns-query"). 78 MatchParam("name", host). 79 MatchHeader("accept", "application/dns-json"). 80 Reply(http.StatusServiceUnavailable) 81 // Run test 82 ip := fallbackLookupIP(context.Background(), host+":443") 83 // Validate output 84 assert.Equal(t, "", ip) 85 assert.Empty(t, apitest.ListUnmatchedRequests()) 86 }) 87 88 t.Run("empty on malformed json", func(t *testing.T) { 89 // Setup http mock 90 defer gock.OffAll() 91 gock.New("https://1.1.1.1"). 92 Get("/dns-query"). 93 MatchParam("name", host). 94 MatchHeader("accept", "application/dns-json"). 95 Reply(http.StatusOK). 96 JSON("malformed") 97 // Run test 98 ip := fallbackLookupIP(context.Background(), host+":443") 99 // Validate output 100 assert.Equal(t, "", ip) 101 assert.Empty(t, apitest.ListUnmatchedRequests()) 102 }) 103 104 t.Run("empty on no answer", func(t *testing.T) { 105 // Setup http mock 106 defer gock.OffAll() 107 gock.New("https://1.1.1.1"). 108 Get("/dns-query"). 109 MatchParam("name", host). 110 MatchHeader("accept", "application/dns-json"). 111 Reply(http.StatusOK). 112 JSON(&dnsResponse{}) 113 // Run test 114 ip := fallbackLookupIP(context.Background(), host+":443") 115 // Validate output 116 assert.Equal(t, "", ip) 117 assert.Empty(t, apitest.ListUnmatchedRequests()) 118 }) 119 120 t.Run("resolves CNAMEs with CloudFlare", func(t *testing.T) { 121 defer gock.OffAll() 122 gock.New("https://1.1.1.1"). 123 Get("/dns-query"). 124 MatchParam("name", host). 125 MatchParam("type", "CNAME"). 126 MatchHeader("accept", "application/dns-json"). 127 Reply(http.StatusOK). 128 JSON(&dnsResponse{Answer: []dnsAnswer{ 129 {Type: cnameType, Data: "foobarbaz.supabase.co"}, 130 }}) 131 // Run test 132 cname, err := ResolveCNAME(context.Background(), host) 133 // Validate output 134 assert.Equal(t, "foobarbaz.supabase.co", cname) 135 assert.Nil(t, err) 136 assert.Empty(t, apitest.ListUnmatchedRequests()) 137 }) 138 139 t.Run("missing CNAMEs return an error", func(t *testing.T) { 140 defer gock.OffAll() 141 gock.New("https://1.1.1.1"). 142 Get("/dns-query"). 143 MatchParam("name", host). 144 MatchParam("type", "CNAME"). 145 MatchHeader("accept", "application/dns-json"). 146 Reply(http.StatusOK). 147 JSON(&dnsResponse{Answer: []dnsAnswer{}}) 148 // Run test 149 cname, err := ResolveCNAME(context.Background(), host) 150 // Validate output 151 assert.Empty(t, cname) 152 assert.ErrorContains(t, err, "failed to locate appropriate CNAME record for api.supabase.io") 153 assert.Empty(t, apitest.ListUnmatchedRequests()) 154 }) 155 156 t.Run("missing CNAMEs return an error", func(t *testing.T) { 157 defer gock.OffAll() 158 gock.New("https://1.1.1.1"). 159 Get("/dns-query"). 160 MatchParam("name", host). 161 MatchParam("type", "CNAME"). 162 MatchHeader("accept", "application/dns-json"). 163 Reply(http.StatusOK). 164 JSON(&dnsResponse{Answer: []dnsAnswer{ 165 {Type: dnsIPv4Type, Data: "127.0.0.1"}, 166 }}) 167 // Run test 168 cname, err := ResolveCNAME(context.Background(), host) 169 // Validate output 170 assert.Empty(t, cname) 171 assert.ErrorContains(t, err, "failed to locate appropriate CNAME record for api.supabase.io") 172 assert.Empty(t, apitest.ListUnmatchedRequests()) 173 }) 174 175 }