You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
4.2 KiB
184 lines
4.2 KiB
package equinox
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/equinox-io/equinox/proto"
|
|
)
|
|
|
|
const fakeAppID = "fake_app_id"
|
|
|
|
var (
|
|
fakeBinary = []byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}
|
|
newFakeBinary = []byte{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2}
|
|
ts *httptest.Server
|
|
key *ecdsa.PrivateKey
|
|
sha string
|
|
newSHA string
|
|
signature string
|
|
)
|
|
|
|
func init() {
|
|
shaBytes := sha256.Sum256(fakeBinary)
|
|
sha = hex.EncodeToString(shaBytes[:])
|
|
newSHABytes := sha256.Sum256(newFakeBinary)
|
|
newSHA = hex.EncodeToString(newSHABytes[:])
|
|
|
|
var err error
|
|
key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to generate ecdsa key: %v", err))
|
|
}
|
|
sig, err := key.Sign(rand.Reader, newSHABytes[:], nil)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to sign new binary: %v", err))
|
|
}
|
|
signature = hex.EncodeToString(sig)
|
|
}
|
|
|
|
func TestNotAvailable(t *testing.T) {
|
|
opts := setup(t, "TestNotAvailable", proto.Response{
|
|
Available: false,
|
|
})
|
|
defer cleanup(opts)
|
|
|
|
_, err := Check(fakeAppID, opts)
|
|
if err != NotAvailableErr {
|
|
t.Fatalf("Expected not available error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEndToEnd(t *testing.T) {
|
|
opts := setup(t, "TestEndtoEnd", proto.Response{
|
|
Available: true,
|
|
Release: proto.Release{
|
|
Version: "0.1.2.3",
|
|
Title: "Release Title",
|
|
Description: "Release Description",
|
|
CreateDate: time.Now(),
|
|
},
|
|
Checksum: newSHA,
|
|
Signature: signature,
|
|
})
|
|
defer cleanup(opts)
|
|
|
|
resp, err := Check(fakeAppID, opts)
|
|
if err != nil {
|
|
t.Fatalf("Failed check: %v", err)
|
|
}
|
|
err = resp.Apply()
|
|
if err != nil {
|
|
t.Fatalf("Failed apply: %v", err)
|
|
}
|
|
|
|
buf, err := ioutil.ReadFile(opts.TargetPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read file: %v", err)
|
|
}
|
|
if !bytes.Equal(buf, newFakeBinary) {
|
|
t.Fatalf("Binary did not update to new expected value. Got %v, expected %v", buf, newFakeBinary)
|
|
}
|
|
}
|
|
|
|
func TestInvalidPatch(t *testing.T) {
|
|
opts := setup(t, "TestInavlidPatch", proto.Response{
|
|
Available: true,
|
|
Release: proto.Release{
|
|
Version: "0.1.2.3",
|
|
Title: "Release Title",
|
|
Description: "Release Description",
|
|
CreateDate: time.Now(),
|
|
},
|
|
DownloadURL: "bad-request",
|
|
Checksum: newSHA,
|
|
Signature: signature,
|
|
Patch: proto.PatchBSDiff,
|
|
})
|
|
defer cleanup(opts)
|
|
|
|
resp, err := Check(fakeAppID, opts)
|
|
if err != nil {
|
|
t.Fatalf("Failed check: %v", err)
|
|
}
|
|
err = resp.Apply()
|
|
if err == nil {
|
|
t.Fatalf("Apply succeeded")
|
|
}
|
|
if err.Error() != "error downloading patch: bad-request" {
|
|
t.Fatalf("Expected a different error message: %s", err)
|
|
}
|
|
}
|
|
|
|
func setup(t *testing.T, name string, resp proto.Response) Options {
|
|
checkUserAgent := func(req *http.Request) {
|
|
if req.Header.Get("User-Agent") != userAgent {
|
|
t.Errorf("Expected user agent to be %s, not %s", userAgent, req.Header.Get("User-Agent"))
|
|
}
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/check", func(w http.ResponseWriter, r *http.Request) {
|
|
checkUserAgent(r)
|
|
var req proto.Request
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
t.Fatalf("Failed to decode proto request: %v", err)
|
|
}
|
|
if resp.Available {
|
|
if req.AppID != fakeAppID {
|
|
t.Fatalf("Unexpected app ID. Got %v, expected %v", err)
|
|
}
|
|
if req.CurrentSHA256 != sha {
|
|
t.Fatalf("Unexpected request SHA: %v", sha)
|
|
}
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
})
|
|
|
|
// Keying off the download URL may not be the best idea...
|
|
if resp.DownloadURL == "bad-request" {
|
|
mux.HandleFunc("/bin", func(w http.ResponseWriter, r *http.Request) {
|
|
checkUserAgent(r)
|
|
http.Error(w, "bad-request", http.StatusBadRequest)
|
|
})
|
|
} else {
|
|
mux.HandleFunc("/bin", func(w http.ResponseWriter, r *http.Request) {
|
|
checkUserAgent(r)
|
|
w.Write(newFakeBinary)
|
|
})
|
|
}
|
|
|
|
ts = httptest.NewServer(mux)
|
|
resp.DownloadURL = ts.URL + "/bin"
|
|
|
|
var opts Options
|
|
opts.CheckURL = ts.URL + "/check"
|
|
opts.PublicKey = key.Public()
|
|
|
|
if name != "" {
|
|
opts.TargetPath = name
|
|
ioutil.WriteFile(name, fakeBinary, 0644)
|
|
}
|
|
return opts
|
|
}
|
|
|
|
func cleanup(opts Options) {
|
|
if opts.TargetPath != "" {
|
|
os.Remove(opts.TargetPath)
|
|
}
|
|
ts.Close()
|
|
}
|