package cache

import (
	"strings"
	"testing"
	"time"
)

func TestLockContentionPublicProjectionNeverFormatsPathsOrHints(t *testing.T) {
	secretPath := "/Users/private-user/workspace/cache.db.lock"
	secretDSN := "file:/Users/private-user/cache.db?token=secret#fragment"
	err := ErrLockContention{
		Path:       secretPath,
		CachePath:  secretDSN,
		HolderHint: "writer at " + secretPath + " uses token=secret",
		Operation:  "bulk-sync-issues",
		RepoID:     "owner/repo",
		StartedAt:  time.Date(2026, 8, 18, 12, 0, 0, 0, time.UTC),
		PID:        42,
	}

	message := err.Error()
	for _, forbidden := range []string{secretPath, secretDSN, "private-user", "token=secret", "#fragment", err.HolderHint} {
		if strings.Contains(message, forbidden) {
			t.Fatalf("public message leaked %q: %q", forbidden, message)
		}
	}
	for _, want := range []string{"cache_ref=cache-", "operation=bulk-sync-issues", "repo_id=owner/repo", "started_at=2026-08-18T12:00:00Z", "pid=42"} {
		if !strings.Contains(message, want) {
			t.Fatalf("public message %q missing %q", message, want)
		}
	}
	if got, again := err.PublicCacheRef(), err.PublicCacheRef(); got == "" || got != again || strings.Contains(got, "private-user") {
		t.Fatalf("PublicCacheRef() = %q, again %q", got, again)
	}
}

func TestLockContentionPublicProjectionOmitsHostileLegacyOwnerFields(t *testing.T) {
	secretPath := "/Users/private-user/workspace/cache.db"
	err := ErrLockContention{
		Path:      secretPath + ".lock",
		Operation: "sync\npath=" + secretPath,
		RepoID:    "owner/repo?token=secret#fragment",
		PID:       -42,
	}
	message := err.Error()
	for _, forbidden := range []string{secretPath, "token=secret", "#fragment", "operation=", "repo_id=", "pid="} {
		if strings.Contains(message, forbidden) {
			t.Fatalf("public message leaked hostile owner field %q: %q", forbidden, message)
		}
	}
	if err.PublicOperation() != "" || err.PublicRepoID() != "" {
		t.Fatalf("hostile fields survived projection: operation=%q repo=%q", err.PublicOperation(), err.PublicRepoID())
	}
}