//go:build integration package httpcache_test import ( "encoding/json" "os" "path/filepath" "testing" "github.com/therootcompany/golib/net/httpcache" ) var inboundSources = []struct { name string url string }{ { "inbound_single_ips", "https://github.com/bitwire-it/ipblocklist/raw/refs/heads/main/tables/inbound/single_ips.txt", }, { "inbound_networks", "https://github.com/bitwire-it/ipblocklist/raw/refs/heads/main/tables/inbound/networks.txt", }, } func testdataDir(t *testing.T) string { t.Helper() dir, _ := filepath.Abs(".") for { if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { return filepath.Join(dir, "testdata") } parent := filepath.Dir(dir) if parent == dir { t.Fatal("could not find module root (go.mod)") } dir = parent } } func TestCacher_Download(t *testing.T) { for _, src := range inboundSources { t.Run(src.name, func(t *testing.T) { path := filepath.Join(testdataDir(t), src.name+".txt") os.Remove(path) os.Remove(path + ".meta") c := httpcache.New(src.url, path) updated, err := c.Fetch() if err != nil { t.Fatalf("Fetch: %v", err) } if !updated { t.Error("first Fetch: expected updated=true") } info, err := os.Stat(path) if err != nil { t.Fatalf("file not created: %v", err) } if info.Size() == 0 { t.Error("downloaded file is empty") } t.Logf("downloaded %d bytes to %s", info.Size(), path) }) } } func TestCacher_SidecarWritten(t *testing.T) { for _, src := range inboundSources { t.Run(src.name, func(t *testing.T) { path := filepath.Join(testdataDir(t), src.name+".txt") os.Remove(path) os.Remove(path + ".meta") c := httpcache.New(src.url, path) if _, err := c.Fetch(); err != nil { t.Fatalf("Fetch: %v", err) } data, err := os.ReadFile(path + ".meta") if err != nil { t.Fatalf("sidecar not written: %v", err) } var meta map[string]string if err := json.Unmarshal(data, &meta); err != nil { t.Fatalf("sidecar not valid JSON: %v", err) } if meta["etag"] == "" && meta["last_modified"] == "" { t.Error("sidecar has neither etag nor last_modified") } t.Logf("sidecar: %s", data) }) } } func TestCacher_ConditionalGet_SameCacher(t *testing.T) { for _, src := range inboundSources { t.Run(src.name, func(t *testing.T) { path := filepath.Join(testdataDir(t), src.name+".txt") c := httpcache.New(src.url, path) if _, err := c.Fetch(); err != nil { t.Fatalf("initial Fetch: %v", err) } updated, err := c.Fetch() if err != nil { t.Fatalf("second Fetch: %v", err) } if updated { t.Error("second Fetch on same cacher: expected updated=false") } t.Log("same-cacher conditional GET correctly skipped re-download") }) } } func TestCacher_ConditionalGet_FreshCacher(t *testing.T) { for _, src := range inboundSources { t.Run(src.name, func(t *testing.T) { path := filepath.Join(testdataDir(t), src.name+".txt") first := httpcache.New(src.url, path) if _, err := first.Fetch(); err != nil { t.Fatalf("initial Fetch: %v", err) } if _, err := os.Stat(path + ".meta"); err != nil { t.Fatalf("sidecar missing after first fetch: %v", err) } // New Cacher with no in-memory state — must read sidecar. fresh := httpcache.New(src.url, path) updated, err := fresh.Fetch() if err != nil { t.Fatalf("fresh-cacher Fetch: %v", err) } if updated { t.Error("fresh-cacher Fetch: expected updated=false (sidecar should have provided ETag)") } t.Log("fresh-cacher conditional GET correctly used sidecar ETag") }) } }