test: fix the five environment-sensitive failures the first ubuntu run exposed

- cli-durable-totals: the live fixture session was stamped at noon today,
  so every before-noon run saw it in the future; the provider-filtered
  path drops future instants while the all-provider path keeps the whole
  day, failing the parity assertion. Relative-and-clamped timestamps,
  the same fix project-filter-durable-totals got in 1596220.
- parser (copilot, 2 cases): the fixture's fixed 2026-05-01 dates crossed
  copilot's durable 90-day age-out on 2026-07-30, so the first parse
  pruned the freshly-cached session. Relative timestamps.
- parser-incremental-append: unlink-then-create let ext4 hand the freed
  inode straight back, breaking the new-inode premise. The replacement
  is now created beside the original and renamed over it.
- parser-proxy-pricing: normalizeProxyPath folds case only on darwin and
  win32, deliberately; the test now asserts the platform-correct
  behavior on both kinds of filesystem instead of hardcoding macOS.
- cli-status-menubar: the config-source filter case does real multi-parse
  work and needs more than the 5s default on shared runners; 30s cap.
This commit is contained in:
ozymandiashh 2026-08-04 03:38:18 +03:00
parent 0e3a1125c9
commit bcf1155255
5 changed files with 36 additions and 12 deletions

View file

@ -89,8 +89,16 @@ async function seedLiveTodaySession(): Promise<void> {
const projectDir = join(ROOT, 'home', '.claude', 'projects', 'p') const projectDir = join(ROOT, 'home', '.claude', 'projects', 'p')
await mkdir(projectDir, { recursive: true }) await mkdir(projectDir, { recursive: true })
const now = new Date() const now = new Date()
const ts = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 12, 0, 0).toISOString() // Timestamps a few minutes OLD, clamped into today: a fixed wall-clock hour
const ts2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 12, 30, 0).toISOString() // (12:00) is in the future whenever the suite runs before noon, and the
// instant-granular provider-filtered path drops future calls while the
// day-granular all-provider path keeps them, so the parity assertion failed
// for every before-noon run (ubuntu CI at 00:17 UTC included). Same fix as
// project-filter-durable-totals got in 1596220.
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
const minutesAgo = (m: number): string => new Date(Math.max(midnight, now.getTime() - m * 60_000)).toISOString()
const ts = minutesAgo(40)
const ts2 = minutesAgo(10)
const line = (id: string, t: string): string => JSON.stringify({ const line = (id: string, t: string): string => JSON.stringify({
type: 'assistant', type: 'assistant',
timestamp: t, timestamp: t,

View file

@ -268,7 +268,7 @@ describe('codeburn status --format menubar-json', () => {
} finally { } finally {
await rm(home, { recursive: true, force: true }) await rm(home, { recursive: true, force: true })
} }
}) }, 30_000)
it('keeps idle Claude config options visible for the selected period', async () => { it('keeps idle Claude config options visible for the selected period', async () => {
const home = await mkdtemp(join(tmpdir(), 'codeburn-menubar-claude-config-idle-')) const home = await mkdtemp(join(tmpdir(), 'codeburn-menubar-claude-config-idle-'))

View file

@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { mkdtemp, mkdir, writeFile, appendFile, readFile, rm, stat, unlink } from 'fs/promises' import { mkdtemp, mkdir, writeFile, appendFile, readFile, rename, rm, stat, unlink } from 'fs/promises'
import { join } from 'path' import { join } from 'path'
import { tmpdir } from 'os' import { tmpdir } from 'os'
@ -286,14 +286,19 @@ describe('incremental append parsing', () => {
await parseWith(warmCache) await parseWith(warmCache)
const inoBefore = (await stat(sessionPath)).ino const inoBefore = (await stat(sessionPath)).ino
// Replace the file (new inode) with different, LARGER content. // Replace the file (new inode) with different, LARGER content. The
await unlink(sessionPath) // replacement is created BESIDE the original and renamed over it: an
// unlink-then-create lets ext4 hand the freed inode straight back, which
// broke the new-inode premise on Linux CI. Two files alive at once are
// guaranteed distinct inodes, and rename keeps the replacement's.
const replaced = [ const replaced = [
...baseLines(), ...baseLines(),
userLine('2026-05-01T12:00:00.000Z', 'brand new task'), userLine('2026-05-01T12:00:00.000Z', 'brand new task'),
asstLine('msg-z', '2026-05-01T12:00:02.000Z', { input_tokens: 500, output_tokens: 120 }, [readBlock('/z.ts')]), asstLine('msg-z', '2026-05-01T12:00:02.000Z', { input_tokens: 500, output_tokens: 120 }, [readBlock('/z.ts')]),
].join('\n') + '\n' ].join('\n') + '\n'
await writeFile(sessionPath, replaced) const replacementPath = sessionPath + '.replacement'
await writeFile(replacementPath, replaced)
await rename(replacementPath, sessionPath)
expect((await stat(sessionPath)).ino).not.toBe(inoBefore) expect((await stat(sessionPath)).ino).not.toBe(inoBefore)
readLineCalls.length = 0 readLineCalls.length = 0

View file

@ -40,9 +40,14 @@ describe('isProxiedPath: path matching rule', () => {
expect(isProxiedPath('/Users/me/work/')).toBe(true) expect(isProxiedPath('/Users/me/work/')).toBe(true)
}) })
it('is case-insensitive (macOS/Windows default filesystems)', () => { it('folds case exactly where the default filesystem does (macOS/Windows yes, Linux no)', () => {
// normalizeProxyPath lowercases only on darwin/win32, deliberately: ext4 is
// case-sensitive and folding there could credit unrelated spend. Assert the
// platform-correct behavior instead of hardcoding the macOS one, which made
// this case fail on Linux CI by design.
setProxyPaths(['/Users/Me/Work']) setProxyPaths(['/Users/Me/Work'])
expect(isProxiedPath('/users/me/work/acme')).toBe(true) const foldsCase = process.platform === 'darwin' || process.platform === 'win32'
expect(isProxiedPath('/users/me/work/acme')).toBe(foldsCase)
}) })
it('matches a Windows-style config against a forward-slash cwd', () => { it('matches a Windows-style config against a forward-slash cwd', () => {

View file

@ -143,10 +143,16 @@ async function createJsonlSession(
const dir = join(sessionStateDir, sessionId) const dir = join(sessionStateDir, sessionId)
await mkdir(dir, { recursive: true }) await mkdir(dir, { recursive: true })
await writeFile(join(dir, 'workspace.yaml'), `id: ${sessionId}\ncwd: /home/user/testproj\n`) await writeFile(join(dir, 'workspace.yaml'), `id: ${sessionId}\ncwd: /home/user/testproj\n`)
// Relative timestamps: fixed calendar dates rot. The original '2026-05-01'
// crossed copilot's durable 90-day age-out on 2026-07-30, at which point the
// very first parse pruned the freshly-cached session and both durable tests
// started failing everywhere with "expected +0 to be 200".
const base = Date.now() - 5 * 24 * 60 * 60 * 1000
const at = (offsetSec: number): string => new Date(base + offsetSec * 1000).toISOString()
const lines = [ const lines = [
JSON.stringify({ type: 'session.model_change', timestamp: '2026-05-01T10:00:00Z', data: { newModel: 'gpt-4.1' } }), JSON.stringify({ type: 'session.model_change', timestamp: at(0), data: { newModel: 'gpt-4.1' } }),
JSON.stringify({ type: 'user.message', timestamp: '2026-05-01T10:00:05Z', data: { content: 'hello', interactionId: 'int-1' } }), JSON.stringify({ type: 'user.message', timestamp: at(5), data: { content: 'hello', interactionId: 'int-1' } }),
JSON.stringify({ type: 'assistant.message', timestamp: '2026-05-01T10:00:10Z', data: { messageId: 'msg-1', outputTokens, interactionId: 'int-1', toolRequests: [] } }), JSON.stringify({ type: 'assistant.message', timestamp: at(10), data: { messageId: 'msg-1', outputTokens, interactionId: 'int-1', toolRequests: [] } }),
] ]
await writeFile(join(dir, 'events.jsonl'), lines.join('\n') + '\n') await writeFile(join(dir, 'events.jsonl'), lines.join('\n') + '\n')
return join(dir, 'events.jsonl') return join(dir, 'events.jsonl')