From 22150e13fd7d559f0491439c8de58ccc1e84672d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 26 Feb 2023 21:56:03 +0530 Subject: [PATCH] Add tests for cache file downloading --- tools/themes/collection.go | 12 +++-- tools/themes/collection_test.go | 93 +++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/tools/themes/collection.go b/tools/themes/collection.go index d9bc3b350..3eedf61f7 100644 --- a/tools/themes/collection.go +++ b/tools/themes/collection.go @@ -32,15 +32,16 @@ type JSONMetadata struct { var ErrNoCacheFound = errors.New("No cache found and max cache age is negative") -func fetch_cached(name, url string, max_cache_age time.Duration) (string, error) { - cache_path := filepath.Join(utils.CacheDir(), name+".zip") +func fetch_cached(name, url, cache_path string, max_cache_age time.Duration) (string, error) { + cache_path = filepath.Join(cache_path, name+".zip") zf, err := zip.OpenReader(cache_path) if err != nil && !errors.Is(err, fs.ErrNotExist) { return "", err } + var jm JSONMetadata - err = json.Unmarshal(utils.UnsafeStringToBytes(zf.Comment), &jm) if err == nil { + err = json.Unmarshal(utils.UnsafeStringToBytes(zf.Comment), &jm) if max_cache_age < 0 { return cache_path, nil } @@ -67,6 +68,9 @@ func fetch_cached(name, url string, max_cache_age time.Duration) (string, error) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotModified { + return cache_path, nil + } return "", fmt.Errorf("Failed to download %s with HTTP error: %s", url, resp.Status) } var tf, tf2 *os.File @@ -122,7 +126,7 @@ func fetch_cached(name, url string, max_cache_age time.Duration) (string, error) } func FetchCached(max_cache_age time.Duration) (string, error) { - return fetch_cached("kitty-themes", "https://codeload.github.com/kovidgoyal/kitty-themes/zip/master", max_cache_age) + return fetch_cached("kitty-themes", "https://codeload.github.com/kovidgoyal/kitty-themes/zip/master", utils.CacheDir(), max_cache_age) } type ThemeMetadata struct { diff --git a/tools/themes/collection_test.go b/tools/themes/collection_test.go index b1728a8c7..55f81cf49 100644 --- a/tools/themes/collection_test.go +++ b/tools/themes/collection_test.go @@ -3,11 +3,17 @@ package themes import ( + "archive/zip" + "bytes" + "encoding/json" "fmt" + "net/http" + "net/http/httptest" "os" "path/filepath" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" ) @@ -43,4 +49,91 @@ func TestThemeCollections(t *testing.T) { os.WriteFile(filepath.Join(tdir, "inc.conf"), []byte("background white"), 0o600) pt(ThemeMetadata{Name: "XYZ", Blurb: "a b", Author: "A", Num_settings: 2}, "# some crap", " ", "## ", "## author: A", "## name: XYZ", "## blurb: a", "## b", "", "color red", "background black", "include inc.conf") + + buf := bytes.Buffer{} + zw := zip.NewWriter(&buf) + fw, _ := zw.Create("x/themes.json") + fw.Write([]byte(`[ + { + "author": "X Y", + "blurb": "A dark color scheme for the kitty terminal.", + "file": "themes/Alabaster_Dark.conf", + "is_dark": true, + "license": "MIT", + "name": "Alabaster Dark", + "num_settings": 30, + "upstream": "https://xxx.com" + }, + { + "name": "Empty", "file": "empty.conf" + } + ]`)) + fw, _ = zw.Create("x/empty.conf") + fw.Write([]byte("empty")) + fw, _ = zw.Create("x/themes/Alabaster_Dark.conf") + fw.Write([]byte("alabaster")) + zw.Close() + + received_etag := "" + request_count := 0 + check_etag := true + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + request_count++ + received_etag = r.Header.Get("If-None-Match") + if check_etag && received_etag == `"xxx"` { + w.WriteHeader(http.StatusNotModified) + return + } + w.Header().Add("ETag", `"xxx"`) + w.Write(buf.Bytes()) + })) + defer ts.Close() + + _, err := fetch_cached("test", ts.URL, tdir, 0) + if err != nil { + t.Fatal(err) + } + r, err := zip.OpenReader(filepath.Join(tdir, "test.zip")) + if err != nil { + t.Fatal(err) + } + var jm JSONMetadata + err = json.Unmarshal([]byte(r.Comment), &jm) + if err != nil { + t.Fatal(err) + } + if jm.Etag != `"xxx"` { + t.Fatalf("Unexpected ETag: %#v", jm.Etag) + } + _, err = fetch_cached("test", ts.URL, tdir, time.Hour) + if err != nil { + t.Fatal(err) + } + if request_count != 1 { + t.Fatal("Cached zip file was not used") + } + before, _ := os.Stat(filepath.Join(tdir, "test.zip")) + _, err = fetch_cached("test", ts.URL, tdir, 0) + if err != nil { + t.Fatal(err) + } + if request_count != 2 { + t.Fatal("Cached zip file was incorrectly used") + } + if received_etag != `"xxx"` { + t.Fatalf("Got invalid ETag: %#v", received_etag) + } + after, _ := os.Stat(filepath.Join(tdir, "test.zip")) + if before.ModTime() != after.ModTime() { + t.Fatal("Cached zip file was incorrectly re-downloaded") + } + check_etag = false + _, err = fetch_cached("test", ts.URL, tdir, 0) + if err != nil { + t.Fatal(err) + } + after2, _ := os.Stat(filepath.Join(tdir, "test.zip")) + if after2.ModTime() != after.ModTime() { + t.Fatal("Cached zip file was incorrectly not re-downloaded") + } }