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  }