From e0dddff176a01d410904b6750b3395de4f7e54db Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Mon, 14 Sep 2026 21:39:00 +0200 Subject: plan 9: keyword cache keys on extension, max-read and Unicode tables, trims removed keywords --- internal/engine/cache_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'internal/engine/cache_test.go') diff --git a/internal/engine/cache_test.go b/internal/engine/cache_test.go index eee2860..8c52d48 100644 --- a/internal/engine/cache_test.go +++ b/internal/engine/cache_test.go @@ -6,9 +6,14 @@ import ( "context" "os" "path/filepath" + "runtime" "strings" "testing" "time" + "unicode" + + "krino/internal/config" + "krino/internal/extract" ) // countingPDFTree makes ~/dl holding the given .pdf files, and a fake @@ -222,3 +227,55 @@ func TestExplainUsesCacheWithoutWriting(t *testing.T) { t.Errorf("explain extracted although the run cached a.pdf") } } + +// TestCacheRereadsARenamedExtension: the extension picks the extractor, so +// a file renamed from .html to .txt - same inode, size and mtime - is read +// again, not answered from what the markup reader found (review M6). +func TestCacheRereadsARenamedExtension(t *testing.T) { + home, dl := excludeTree(t, map[string]string{"page.html": "

ACME

 Ltd"}) + main := writeConfig(t, home, `(include "dl")`, map[string]string{"dl": "(path \"~/dl\")\n(rule \"acme\" (when (content \"acme ltd\")) (move \"Acme\"))\n"}) + if r := cachedMatch(t, home, main); matchedNames(r) != "page.html" { + t.Fatalf("as html: matched %q", matchedNames(r)) + } + if err := os.Rename(filepath.Join(dl, "page.html"), filepath.Join(dl, "page.txt")); err != nil { + t.Fatal(err) + } + if r := cachedMatch(t, home, main); matchedNames(r) != "" { + t.Errorf("as txt, answered from the html read: matched %q", matchedNames(r)) + } +} + +// TestCacheFingerprintCoversMaxReadAndTables: answers depend on max-read +// (it caps what is read), on the Go release and on the Unicode tables, so +// all are in the fingerprint (review cache F2, F4). +func TestCacheFingerprintCoversMaxReadAndTables(t *testing.T) { + e := &Engine{Extract: extract.New()} + small := e.cacheFingerprint(&Dir{Settings: config.Resolved{MaxRead: 1 << 10}}) + large := e.cacheFingerprint(&Dir{Settings: config.Resolved{MaxRead: 1 << 20}}) + if small == large { + t.Error("max-read does not change the fingerprint") + } + for _, want := range []string{runtime.Version(), unicode.Version} { + if !strings.Contains(small, want) { + t.Errorf("fingerprint %q lacks %q", small, want) + } + } +} + +// TestCacheRemovedWhenNoContentTestsRemain: once a directory has no content +// test, its cache - holding keywords it no longer uses - is removed (review +// cache F6). +func TestCacheRemovedWhenNoContentTestsRemain(t *testing.T) { + home, _, _ := countingPDFTree(t, map[string]string{"a.pdf": "Invoice ACME"}) + main := writeConfig(t, home, `(include "dl")`, map[string]string{"dl": acmeRules}) + cachedMatch(t, home, main) + file := filepath.Join(home, ".cache", "krino", "dl.cache") + if _, err := os.Stat(file); err != nil { + t.Fatal(err) + } + writeConfig(t, home, `(include "dl")`, map[string]string{"dl": "(path \"~/dl\")\n(rule \"all\" (move \"Out\"))\n"}) + cachedMatch(t, home, main) + if _, err := os.Stat(file); !os.IsNotExist(err) { + t.Errorf("the cache of a directory without content tests is still there: %v", err) + } +} -- cgit v1.3