// 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 with spaces"
var childPath = "/parent with spaces/child.txt"
var parentResponse = `
/parent%20with%20spaces/
Mon, 29 Apr 2024 19:52:23 GMT
Fri, 19 Apr 2024 04:13:34 GMT
HTTP/1.1 200 OK
`
var childResponse = `
/parent%20with%20spaces/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("