// steamcache/steamcache_test.go package steamcache import ( "io" "net/http" "os" "path/filepath" "testing" ) func TestCaching(t *testing.T) { td := t.TempDir() os.WriteFile(filepath.Join(td, "key2"), []byte("value2"), 0644) sc := New("localhost:8080", "1G", "1G", td, "", "lru", "lru") w, err := sc.vfs.Create("key", 5) if err != nil { t.Errorf("Create failed: %v", err) } w.Write([]byte("value")) w.Close() w, err = sc.vfs.Create("key1", 6) if err != nil { t.Errorf("Create failed: %v", err) } w.Write([]byte("value1")) w.Close() if sc.diskgc.Size() != 17 { t.Errorf("Size failed: got %d, want %d", sc.diskgc.Size(), 17) } if sc.vfs.Size() != 17 { t.Errorf("Size failed: got %d, want %d", sc.vfs.Size(), 17) } rc, err := sc.vfs.Open("key") if err != nil { t.Errorf("Open failed: %v", err) } d, _ := io.ReadAll(rc) rc.Close() if string(d) != "value" { t.Errorf("Get failed: got %s, want %s", d, "value") } rc, err = sc.vfs.Open("key1") if err != nil { t.Errorf("Open failed: %v", err) } d, _ = io.ReadAll(rc) rc.Close() if string(d) != "value1" { t.Errorf("Get failed: got %s, want %s", d, "value1") } rc, err = sc.vfs.Open("key2") if err != nil { t.Errorf("Open failed: %v", err) } d, _ = io.ReadAll(rc) rc.Close() if string(d) != "value2" { t.Errorf("Get failed: got %s, want %s", d, "value2") } if sc.diskgc.Size() != 17 { t.Errorf("Size failed: got %d, want %d", sc.diskgc.Size(), 17) } if sc.vfs.Size() != 17 { t.Errorf("Size failed: got %d, want %d", sc.vfs.Size(), 17) } sc.memory.Delete("key2") os.Remove(filepath.Join(td, "key2")) if _, err := sc.vfs.Open("key2"); err == nil { t.Errorf("Open failed: got nil, want error") } } func TestCacheMissAndHit(t *testing.T) { sc := New("localhost:8080", "0", "1G", t.TempDir(), "", "lru", "lru") key := "testkey" value := []byte("testvalue") // Simulate miss: but since no upstream, skip full ServeHTTP, test VFS w, err := sc.vfs.Create(key, int64(len(value))) if err != nil { t.Fatal(err) } w.Write(value) w.Close() rc, err := sc.vfs.Open(key) if err != nil { t.Fatal(err) } got, _ := io.ReadAll(rc) rc.Close() if string(got) != string(value) { t.Errorf("expected %s, got %s", value, got) } } func TestHashCalculation(t *testing.T) { // Test data testData := []byte("Hello, World!") // Calculate hash hash := calculateFileHash(testData) // Expected SHA1 hash of "Hello, World!" expectedHash := "0a0a9f2a6772942557ab5355d76af442f8f65e01" if hash != expectedHash { t.Errorf("Hash calculation failed: expected %s, got %s", expectedHash, hash) } // Test verification if !verifyFileHash(testData, expectedHash) { t.Error("Hash verification failed for correct hash") } if verifyFileHash(testData, "wronghash") { t.Error("Hash verification passed for wrong hash") } } func TestHashVerificationWithRealData(t *testing.T) { // Test with some real data to ensure our hash calculation is correct testCases := []struct { data string expected string }{ {"", "da39a3ee5e6b4b0d3255bfef95601890afd80709"}, // SHA1 of empty string {"test", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"}, // SHA1 of "test" {"Hello, World!", "0a0a9f2a6772942557ab5355d76af442f8f65e01"}, // SHA1 of "Hello, World!" } for _, tc := range testCases { data := []byte(tc.data) hash := calculateFileHash(data) if hash != tc.expected { t.Errorf("Hash calculation failed for '%s': expected %s, got %s", tc.data, tc.expected, hash) } if !verifyFileHash(data, tc.expected) { t.Errorf("Hash verification failed for '%s'", tc.data) } } } func TestResponseHashCalculation(t *testing.T) { // Create a mock HTTP response resp := &http.Response{ StatusCode: 200, Status: "200 OK", Header: http.Header{ "Content-Type": []string{"application/octet-stream"}, "Content-Length": []string{"13"}, "Cache-Control": []string{"public, max-age=3600"}, }, } bodyData := []byte("Hello, World!") // Calculate response hash responseHash := calculateResponseHash(resp, bodyData) // The hash should be different from just the body hash bodyHash := calculateFileHash(bodyData) if responseHash == bodyHash { t.Error("Response hash should be different from body hash when headers are present") } // Test that the same response produces the same hash responseHash2 := calculateResponseHash(resp, bodyData) if responseHash != responseHash2 { t.Error("Response hash should be consistent for the same response") } // Test with different headers resp2 := &http.Response{ StatusCode: 200, Status: "200 OK", Header: http.Header{ "Content-Type": []string{"text/plain"}, "Content-Length": []string{"13"}, }, } responseHash3 := calculateResponseHash(resp2, bodyData) if responseHash == responseHash3 { t.Error("Response hash should be different for different headers") } } func TestSteamKeySharding(t *testing.T) { sc := New("localhost:8080", "0", "1G", t.TempDir(), "", "lru", "lru") // Test with a Steam-style key that should trigger sharding steamKey := "steam/0016cfc5019b8baa6026aa1cce93e685d6e06c6e" testData := []byte("test steam cache data") // Create a file with the steam key w, err := sc.vfs.Create(steamKey, int64(len(testData))) if err != nil { t.Fatalf("Failed to create file with steam key: %v", err) } w.Write(testData) w.Close() // Verify we can read it back rc, err := sc.vfs.Open(steamKey) if err != nil { t.Fatalf("Failed to open file with steam key: %v", err) } got, _ := io.ReadAll(rc) rc.Close() if string(got) != string(testData) { t.Errorf("Data mismatch: expected %s, got %s", testData, got) } // Verify that the file was created (sharding is working if no error occurred) // The key difference is that with sharding, the file should be created successfully // and be readable, whereas without sharding it might not work correctly } func TestKeyGeneration(t *testing.T) { testCases := []struct { input string expected string desc string }{ { input: "/depot/1684171/chunk/0016cfc5019b8baa6026aa1cce93e685d6e06c6e", expected: "steam/0016cfc5019b8baa6026aa1cce93e685d6e06c6e", desc: "chunk file URL", }, { input: "/depot/1684171/manifest/944076726177422892/5/12001286503415372840", expected: "steam/12001286503415372840", desc: "manifest file URL", }, { input: "/depot/invalid/path", expected: "", desc: "invalid depot URL format", }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { result := generateSteamCacheKey(tc.input) if result != tc.expected { t.Errorf("generateSteamCacheKey(%s) = %s, expected %s", tc.input, result, tc.expected) } }) } }