// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package compositedav import ( "fmt" "log" "net/http" "path" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "tailscale.com/tstest" ) var parentPath = "/parent" var childPath = "/parent/child.txt" var parentResponse = ` /parent/ Mon, 29 Apr 2024 19:52:23 GMT Fri, 19 Apr 2024 04:13:34 GMT HTTP/1.1 200 OK ` var childResponse = ` /parent/child.txt Mon, 29 Apr 2024 19:52:23 GMT Fri, 19 Apr 2024 04:13:34 GMT HTTP/1.1 200 OK ` var fullParent = []byte( strings.ReplaceAll( fmt.Sprintf(`%s%s`, parentResponse, childResponse), "\n", "")) var partialParent = []byte( strings.ReplaceAll( fmt.Sprintf(`%s`, parentResponse), "\n", "")) var fullChild = []byte( strings.ReplaceAll( fmt.Sprintf(`%s`, childResponse), "\n", "")) func TestStatCacheNoTimeout(t *testing.T) { // Make sure we don't leak goroutines tstest.ResourceCheck(t) c := &StatCache{TTL: 5 * time.Second} defer c.stop() // check get before set fetched := c.get(childPath, 0) if fetched != nil { t.Errorf("got %v, want nil", fetched) } // set new stat ce := newCacheEntry(http.StatusMultiStatus, fullChild) c.set(childPath, 0, ce) fetched = c.get(childPath, 0) if diff := cmp.Diff(fetched, ce); diff != "" { t.Errorf("should have gotten cached value; (-got+want):%v", diff) } // fetch stat again, should still be cached fetched = c.get(childPath, 0) if diff := cmp.Diff(fetched, ce); diff != "" { t.Errorf("should still have gotten cached value; (-got+want):%v", diff) } } func TestStatCacheTimeout(t *testing.T) { // Make sure we don't leak goroutines tstest.ResourceCheck(t) c := &StatCache{TTL: 250 * time.Millisecond} defer c.stop() // set new stat ce := newCacheEntry(http.StatusMultiStatus, fullChild) c.set(childPath, 0, ce) fetched := c.get(childPath, 0) if diff := cmp.Diff(fetched, ce); diff != "" { t.Errorf("should have gotten cached value; (-got+want):%v", diff) } // wait for cache to expire and refetch stat, should be empty now time.Sleep(c.TTL * 2) fetched = c.get(childPath, 0) if fetched != nil { t.Errorf("cached value should have expired") } c.set(childPath, 0, ce) // invalidate the cache and make sure nothing is returned c.invalidate() fetched = c.get(childPath, 0) if fetched != nil { t.Errorf("invalidate should have cleared cached value") } } func TestParentChildRelationship(t *testing.T) { // Make sure we don't leak goroutines tstest.ResourceCheck(t) c := &StatCache{TTL: 24 * time.Hour} // don't expire defer c.stop() missingParentPath := "/missingparent" unparseableParentPath := "/unparseable" c.set(parentPath, 1, newCacheEntry(http.StatusMultiStatus, fullParent)) c.set(missingParentPath, 1, newCacheEntry(http.StatusNotFound, nil)) c.set(unparseableParentPath, 1, newCacheEntry(http.StatusMultiStatus, []byte("