Ctrl K
ComunidadGuías…

internal/home/kv_helpers_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package home

import (
	"bytes"
	"context"
	"path/filepath"
	"strings"
	"testing"

	"github.com/router-for-me/CLIProxyAPI/v8/internal/config"
	"github.com/router-for-me/CLIProxyAPI/v8/internal/kvstore"
	log "github.com/sirupsen/logrus"
)

func TestHashKeyPart(t *testing.T) {
	first := HashKeyPart("secret-value")
	again := HashKeyPart("secret-value")
	other := HashKeyPart("other-value")
	if first == "" || len(first) != 64 {
		t.Fatalf("HashKeyPart() = %q, want 64 hex chars", first)
	}
	if first != again {
		t.Fatalf("HashKeyPart() is not stable")
	}
	if first == other {
		t.Fatalf("HashKeyPart() returned same hash for different inputs")
	}
	if strings.Contains(first, "secret") || strings.Contains(first, "value") {
		t.Fatalf("HashKeyPart() leaked input: %q", first)
	}
}

func TestKVRequiredHelpersReturnNonHomeMode(t *testing.T) {
	ClearCurrent()
	t.Cleanup(ClearCurrent)

	var out map[string]string
	homeMode, found, errGet := KVGetJSONRequired(context.Background(), "key", &out)
	if errGet != nil {
		t.Fatalf("KVGetJSONRequired() error = %v", errGet)
	}
	if homeMode || found {
		t.Fatalf("KVGetJSONRequired() = homeMode %v found %v, want false false", homeMode, found)
	}
}

func TestCurrentKVClientUnavailableErrors(t *testing.T) {
	t.Cleanup(ClearCurrent)

	disabled := New(config.HomeConfig{Enabled: false})
	SetCurrent(disabled)
	if _, homeMode, errClient := CurrentKVClient(); !homeMode || errClient == nil {
		t.Fatalf("CurrentKVClient(disabled) = homeMode %v err %v, want true error", homeMode, errClient)
	}

	notReady := New(config.HomeConfig{Enabled: true, Host: "127.0.0.1", Port: 1})
	SetCurrent(notReady)
	if _, homeMode, errClient := CurrentKVClient(); !homeMode || errClient == nil {
		t.Fatalf("CurrentKVClient(no heartbeat) = homeMode %v err %v, want true error", homeMode, errClient)
	}
}

func TestKVRequiredHelpersPropagateClientErrors(t *testing.T) {
	client, _ := newRedisCommandTestClient(t, func(args []string) string {
		return "-ERR home kv unavailable\r\n"
	})
	client.heartbeatOK.Store(true)
	SetCurrent(client)
	t.Cleanup(ClearCurrent)

	var out map[string]string
	homeMode, _, errGet := KVGetJSONRequired(context.Background(), "cpa:test:key", &out)
	if !homeMode || errGet == nil {
		t.Fatalf("KVGetJSONRequired() = homeMode %v err %v, want true error", homeMode, errGet)
	}
	homeMode, errSet := KVSetJSONRequired(context.Background(), "cpa:test:key", map[string]string{"value": "secret"}, 0)
	if !homeMode || errSet == nil {
		t.Fatalf("KVSetJSONRequired() = homeMode %v err %v, want true error", homeMode, errSet)
	}
}

func TestKVBestEffortWriteSwallowsErrorAndRedactsLog(t *testing.T) {
	client, _ := newRedisCommandTestClient(t, func(args []string) string {
		return "-ERR home kv unavailable\r\n"
	})
	client.heartbeatOK.Store(true)
	SetCurrent(client)
	t.Cleanup(ClearCurrent)

	logger := log.StandardLogger()
	previousOutput := logger.Out
	previousLevel := log.GetLevel()
	buffer := &bytes.Buffer{}
	log.SetOutput(buffer)
	log.SetLevel(log.ErrorLevel)
	t.Cleanup(func() {
		log.SetOutput(previousOutput)
		log.SetLevel(previousLevel)
	})

	ok := KVSetJSONBestEffort(context.Background(), "cpa:test:secret-key", map[string]string{"value": "secret-value"}, 0)
	if ok {
		t.Fatalf("KVSetJSONBestEffort() = true, want false")
	}
	logText := buffer.String()
	if !strings.Contains(logText, "cpa:test:*") {
		t.Fatalf("log = %q, want redacted key prefix", logText)
	}
	if strings.Contains(logText, "secret-key") || strings.Contains(logText, "secret-value") {
		t.Fatalf("log leaked key or value: %q", logText)
	}
}

func TestCurrentKVClientBridgesLocalBoltStoreAndPrioritizesHome(t *testing.T) {
	ClearCurrent()
	_ = kvstore.ShutdownLocal()
	t.Cleanup(func() {
		ClearCurrent()
		_ = kvstore.ShutdownLocal()
	})

	dbPath := filepath.Join(t.TempDir(), "bridge.db")
	if errCfg := kvstore.ConfigureLocal(true, dbPath); errCfg != nil {
		t.Fatalf("kvstore.ConfigureLocal(true, %q) error = %v", dbPath, errCfg)
	}

	client, kvMode, errClient := CurrentKVClient()
	if errClient != nil || !kvMode || client == nil {
		t.Fatalf("CurrentKVClient() with local enabled = (%v, %v, %v), want (*BoltStore, true, nil)", client, kvMode, errClient)
	}
	if _, ok := client.(*kvstore.BoltStore); !ok {
		t.Fatalf("CurrentKVClient() type = %T, want *kvstore.BoltStore", client)
	}

	// When Home is active, CurrentKVClient must return the Home client even while local bbolt is enabled.
	homeClient, _ := newRedisCommandTestClient(t, func(args []string) string {
		return "+OK\r\n"
	})
	homeClient.heartbeatOK.Store(true)
	SetCurrent(homeClient)

	activeClient, activeMode, errActive := CurrentKVClient()
	if errActive != nil || !activeMode || activeClient != homeClient {
		t.Fatalf("CurrentKVClient() with active Home = (%v, %v, %v), want (%v, true, nil)", activeClient, activeMode, errActive, homeClient)
	}
}