import { it, expect, vi } from 'vitest'
import { createMemoryBackend } from './memory.js'
import { healthCheck } from './health.js'
import type { CacheBackend } from './index.js'

it('reports ok on a working backend and cleans up the probe key', async () => {
  const backend = createMemoryBackend()
  const res = await healthCheck(backend).check()
  expect(res.name).toBe('cache')
  expect(res.status).toBe('ok')
  expect(typeof res.latencyMs).toBe('number')
  // probe key removed
  expect(await backend.get('__health_probe__')).toBeUndefined()
})

it('writes the probe key with the configured short TTL', async () => {
  const set = vi.fn(async () => {})
  const backend: CacheBackend = { get: async () => 'ok:0', set, del: async () => {} }
  // token mismatch is fine here; we only assert the TTL passed to set
  await healthCheck(backend, { ttlSeconds: 5 }).check()
  expect(set).toHaveBeenCalledWith('__health_probe__', expect.any(String), 5)
})

it('reports degraded when the backend silently drops the write', async () => {
  // set is a no-op, get returns nothing → value mismatch
  const backend: CacheBackend = { get: async () => undefined, set: async () => {}, del: async () => {} }
  const res = await healthCheck(backend).check()
  expect(res.status).toBe('degraded')
})

it('NEVER throws on a failing backend — returns status down', async () => {
  const backend: CacheBackend = {
    get: async () => undefined,
    set: async () => { throw new Error('redis timeout') },
    del: async () => {},
  }
  const res = await healthCheck(backend).check()
  expect(res.status).toBe('down')
})

it('SECURITY: a probe failure detail NEVER echoes the raw backend error', async () => {
  const secret = 'redis://default:s3cr3t@cache.internal:6379'
  const backend: CacheBackend = {
    get: async () => undefined,
    set: async () => { throw new Error(secret) },
    del: async () => {},
  }
  const res = await healthCheck(backend).check()
  expect(res.status).toBe('down')
  expect(res.detail).toBe('probe failed')
  expect(res.detail).not.toContain('s3cr3t')
})

it('still reports ok when only the del cleanup fails (key self-expires via TTL)', async () => {
  const store = new Map<string, string>()
  const backend: CacheBackend = {
    get: async (k) => store.get(k),
    set: async (k, v) => { store.set(k, v) },
    del: async () => { throw new Error('del failed') },
  }
  const res = await healthCheck(backend).check()
  expect(res.status).toBe('ok')
})
