dify/cli/src/cache/nudge-store.test.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

99 lines
4.1 KiB
TypeScript

import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { load } from 'js-yaml'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { ENV_CACHE_DIR } from '@/store/dir'
import { CACHE_NUDGE, cachePath, getCache } from '@/store/manager'
import { loadNudgeStore, WARN_INTERVAL_MS } from './nudge-store'
function nudgeStorePath(dir: string): string {
return cachePath(dir, CACHE_NUDGE)
}
const HOST = 'https://cloud.dify.ai'
describe('NudgeStore', () => {
let dir: string
let prevCacheDir: string | undefined
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), 'difyctl-nudge-'))
prevCacheDir = process.env[ENV_CACHE_DIR]
process.env[ENV_CACHE_DIR] = dir
})
afterEach(async () => {
if (prevCacheDir === undefined) delete process.env[ENV_CACHE_DIR]
else process.env[ENV_CACHE_DIR] = prevCacheDir
await rm(dir, { recursive: true, force: true })
})
it('canWarn=true when no prior record exists', async () => {
const store = await loadNudgeStore({ store: getCache(CACHE_NUDGE) })
expect(store.canWarn(HOST)).toBe(true)
})
it('canWarn=false within the silence window, true past it', async () => {
const t0 = new Date('2026-05-19T12:00:00.000Z')
const store = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t0 })
await store.markWarned(HOST)
expect(store.canWarn(HOST, new Date('2026-05-19T18:00:00.000Z'))).toBe(false)
expect(store.canWarn(HOST, new Date('2026-05-20T12:00:00.000Z'))).toBe(true)
})
it('canWarn clamps negative elapsed under clock skew (treats as still in window)', async () => {
const t0 = new Date('2026-05-19T12:00:00.000Z')
const store = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t0 })
await store.markWarned(HOST)
const pastClock = new Date('2026-05-19T11:00:00.000Z') // clock moved backwards 1h
expect(store.canWarn(HOST, pastClock)).toBe(false)
})
it('markWarned persists across store reloads', async () => {
const t0 = new Date('2026-05-19T12:00:00.000Z')
const s1 = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t0 })
await s1.markWarned(HOST)
const s2 = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t0 })
expect(s2.canWarn(HOST)).toBe(false)
})
it('treats a corrupt cache file as empty', async () => {
const path = nudgeStorePath(dir)
await writeCacheFile(path, '{ not valid json')
const store = await loadNudgeStore({ store: getCache(CACHE_NUDGE) })
expect(store.canWarn(HOST)).toBe(true)
})
it('writes ISO timestamps under warned/<host> on disk', async () => {
const t = new Date('2026-05-19T12:00:00.000Z')
const store = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t })
await store.markWarned(HOST)
const raw = await readFile(nudgeStorePath(dir), 'utf8')
const parsed = load(raw) as Record<string, unknown>
expect((parsed.warned as Record<string, string>)[HOST]).toBe(t.toISOString())
})
it('concurrent writers across different hosts: both stamps survive (merge-on-write)', async () => {
// Two stores independently loaded (simulating two CLI processes), each
// warns about a different host. Without merge-on-write the second writer
// would clobber the first.
const t = new Date('2026-05-19T12:00:00.000Z')
const a = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t })
const b = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t })
await a.markWarned('https://a.example')
await b.markWarned('https://b.example')
const reread = await loadNudgeStore({ store: getCache(CACHE_NUDGE), now: () => t })
expect(reread.canWarn('https://a.example')).toBe(false)
expect(reread.canWarn('https://b.example')).toBe(false)
})
it('exposes WARN_INTERVAL_MS as 24h', () => {
expect(WARN_INTERVAL_MS).toBe(24 * 60 * 60 * 1000)
})
})
async function writeCacheFile(path: string, body: string): Promise<void> {
const { mkdir } = await import('node:fs/promises')
await mkdir(dirname(path), { recursive: true })
await writeFile(path, body)
}