github.com/equinox-io/equinox@v1.2.1-0.20200723040547-60ffe7f858fe/sdk_test.go (about) 1 package equinox 2 3 import ( 4 "bytes" 5 "crypto/ecdsa" 6 "crypto/elliptic" 7 "crypto/rand" 8 "crypto/sha256" 9 "encoding/hex" 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "testing" 17 "time" 18 19 "github.com/equinox-io/equinox/proto" 20 ) 21 22 const fakeAppID = "fake_app_id" 23 24 var ( 25 fakeBinary = []byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1} 26 newFakeBinary = []byte{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2} 27 ts *httptest.Server 28 key *ecdsa.PrivateKey 29 sha string 30 newSHA string 31 signature string 32 ) 33 34 func init() { 35 shaBytes := sha256.Sum256(fakeBinary) 36 sha = hex.EncodeToString(shaBytes[:]) 37 newSHABytes := sha256.Sum256(newFakeBinary) 38 newSHA = hex.EncodeToString(newSHABytes[:]) 39 40 var err error 41 key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 42 if err != nil { 43 panic(fmt.Sprintf("Failed to generate ecdsa key: %v", err)) 44 } 45 sig, err := key.Sign(rand.Reader, newSHABytes[:], nil) 46 if err != nil { 47 panic(fmt.Sprintf("Failed to sign new binary: %v", err)) 48 } 49 signature = hex.EncodeToString(sig) 50 } 51 52 func TestNotAvailable(t *testing.T) { 53 opts := setup(t, "TestNotAvailable", proto.Response{ 54 Available: false, 55 }) 56 defer cleanup(opts) 57 58 _, err := Check(fakeAppID, opts) 59 if err != NotAvailableErr { 60 t.Fatalf("Expected not available error, got: %v", err) 61 } 62 } 63 64 func TestEndToEnd(t *testing.T) { 65 opts := setup(t, "TestEndtoEnd", proto.Response{ 66 Available: true, 67 Release: proto.Release{ 68 Version: "0.1.2.3", 69 Title: "Release Title", 70 Description: "Release Description", 71 CreateDate: time.Now(), 72 }, 73 Checksum: newSHA, 74 Signature: signature, 75 }) 76 defer cleanup(opts) 77 78 resp, err := Check(fakeAppID, opts) 79 if err != nil { 80 t.Fatalf("Failed check: %v", err) 81 } 82 err = resp.Apply() 83 if err != nil { 84 t.Fatalf("Failed apply: %v", err) 85 } 86 87 buf, err := ioutil.ReadFile(opts.TargetPath) 88 if err != nil { 89 t.Fatalf("Failed to read file: %v", err) 90 } 91 if !bytes.Equal(buf, newFakeBinary) { 92 t.Fatalf("Binary did not update to new expected value. Got %v, expected %v", buf, newFakeBinary) 93 } 94 } 95 96 func TestInvalidPatch(t *testing.T) { 97 opts := setup(t, "TestInavlidPatch", proto.Response{ 98 Available: true, 99 Release: proto.Release{ 100 Version: "0.1.2.3", 101 Title: "Release Title", 102 Description: "Release Description", 103 CreateDate: time.Now(), 104 }, 105 DownloadURL: "bad-request", 106 Checksum: newSHA, 107 Signature: signature, 108 Patch: proto.PatchBSDiff, 109 }) 110 defer cleanup(opts) 111 112 resp, err := Check(fakeAppID, opts) 113 if err != nil { 114 t.Fatalf("Failed check: %v", err) 115 } 116 err = resp.Apply() 117 if err == nil { 118 t.Fatalf("Apply succeeded") 119 } 120 if err.Error() != "error downloading patch: bad-request" { 121 t.Fatalf("Expected a different error message: %s", err) 122 } 123 } 124 125 func setup(t *testing.T, name string, resp proto.Response) Options { 126 checkUserAgent := func(req *http.Request) { 127 if req.Header.Get("User-Agent") != userAgent { 128 t.Errorf("Expected user agent to be %s, not %s", userAgent, req.Header.Get("User-Agent")) 129 } 130 } 131 132 mux := http.NewServeMux() 133 mux.HandleFunc("/check", func(w http.ResponseWriter, r *http.Request) { 134 checkUserAgent(r) 135 var req proto.Request 136 err := json.NewDecoder(r.Body).Decode(&req) 137 if err != nil { 138 t.Fatalf("Failed to decode proto request: %v", err) 139 } 140 if resp.Available { 141 if req.AppID != fakeAppID { 142 t.Fatalf("Unexpected app ID. Got %v, expected %v", req.AppID, fakeAppID) 143 } 144 if req.CurrentSHA256 != sha { 145 t.Fatalf("Unexpected request SHA: %v", sha) 146 } 147 } 148 json.NewEncoder(w).Encode(resp) 149 }) 150 151 // Keying off the download URL may not be the best idea... 152 if resp.DownloadURL == "bad-request" { 153 mux.HandleFunc("/bin", func(w http.ResponseWriter, r *http.Request) { 154 checkUserAgent(r) 155 http.Error(w, "bad-request", http.StatusBadRequest) 156 }) 157 } else { 158 mux.HandleFunc("/bin", func(w http.ResponseWriter, r *http.Request) { 159 checkUserAgent(r) 160 w.Write(newFakeBinary) 161 }) 162 } 163 164 ts = httptest.NewServer(mux) 165 resp.DownloadURL = ts.URL + "/bin" 166 167 var opts Options 168 opts.CheckURL = ts.URL + "/check" 169 opts.PublicKey = key.Public() 170 171 if name != "" { 172 opts.TargetPath = name 173 ioutil.WriteFile(name, fakeBinary, 0644) 174 } 175 return opts 176 } 177 178 func cleanup(opts Options) { 179 if opts.TargetPath != "" { 180 os.Remove(opts.TargetPath) 181 } 182 ts.Close() 183 }