609 lines
22 KiB
TypeScript
609 lines
22 KiB
TypeScript
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'fs/promises'
|
|
import { tmpdir } from 'os'
|
|
import { join } from 'path'
|
|
import { createRequire } from 'node:module'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { isSqliteAvailable } from '../../src/sqlite.js'
|
|
import {
|
|
antigravityAppDataDirFromSourcePath,
|
|
antigravityCascadeIdFromPath,
|
|
createAntigravityProvider,
|
|
discoverAntigravitySessionSources,
|
|
extractAntigravityAppDataDirFromLine,
|
|
extractAntigravityGeneratorMetadata,
|
|
extractAntigravityModelMap,
|
|
getAntigravityStatusLineEventsPath,
|
|
parseAntigravityServerInfo,
|
|
parseAntigravityServerInfoFromLine,
|
|
recordAntigravityStatusLinePayload,
|
|
shouldReparseAntigravitySource,
|
|
} from '../../src/providers/antigravity.js'
|
|
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
|
|
|
const requireForTest = createRequire(import.meta.url)
|
|
|
|
type CurrentCliFixture = {
|
|
conversationId: string
|
|
rows: Array<{ idx: number; hex: string }>
|
|
}
|
|
|
|
type TestDb = {
|
|
exec(sql: string): void
|
|
prepare(sql: string): { run(...params: unknown[]): void }
|
|
close(): void
|
|
}
|
|
|
|
function createCurrentAntigravityCliDb(dbPath: string, fixture: CurrentCliFixture): void {
|
|
const { DatabaseSync: Database } = requireForTest('node:sqlite')
|
|
const db = new Database(dbPath) as TestDb
|
|
try {
|
|
db.exec('CREATE TABLE gen_metadata (idx integer, data blob, size integer NOT NULL DEFAULT 0, PRIMARY KEY (idx))')
|
|
db.exec('CREATE TABLE trajectory_metadata_blob (id text DEFAULT "main", data blob, PRIMARY KEY (id))')
|
|
db.prepare('INSERT INTO trajectory_metadata_blob (id, data) VALUES (?, ?)').run(
|
|
'main',
|
|
Buffer.from('file:///Users/example/private-project'),
|
|
)
|
|
for (const row of fixture.rows) {
|
|
const data = Buffer.from(row.hex, 'hex')
|
|
db.prepare('INSERT INTO gen_metadata (idx, data, size) VALUES (?, ?, ?)').run(row.idx, data, data.length)
|
|
}
|
|
} finally {
|
|
db.close()
|
|
}
|
|
}
|
|
|
|
async function collectAntigravityCalls(source: { path: string; project: string; provider: string }): Promise<ParsedProviderCall[]> {
|
|
const parser = createAntigravityProvider().createSessionParser(source, new Set())
|
|
const calls: ParsedProviderCall[] = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
return calls
|
|
}
|
|
|
|
describe('antigravity provider helpers', () => {
|
|
it('parses legacy https server flags from POSIX process args', () => {
|
|
const server = parseAntigravityServerInfoFromLine(
|
|
'/Applications/Antigravity.app/language_server_macos_arm --app_data_dir antigravity --https_server_port 57101 --csrf_token 01234567-89ab-cdef-0123-456789abcdef',
|
|
)
|
|
|
|
expect(server).toEqual({
|
|
port: 57101,
|
|
csrfToken: '01234567-89ab-cdef-0123-456789abcdef',
|
|
})
|
|
})
|
|
|
|
it('parses Windows extension server flags and equals syntax', () => {
|
|
const server = parseAntigravityServerInfoFromLine(
|
|
'C:\\Users\\Admin\\AppData\\Local\\Programs\\Antigravity\\resources\\app\\extensions\\antigravity\\bin\\language_server_windows_x64.exe --extension_server_port=62225 --extension_server_csrf_token=abcdef01-2345-6789-abcd-ef0123456789',
|
|
)
|
|
|
|
expect(server).toEqual({
|
|
port: 62225,
|
|
csrfToken: 'abcdef01-2345-6789-abcd-ef0123456789',
|
|
})
|
|
})
|
|
|
|
it('parses Windows extension server flags and space syntax', () => {
|
|
const server = parseAntigravityServerInfo([
|
|
'node something-unrelated',
|
|
'language_server_windows_x64.exe --app_data_dir C:\\Users\\Admin\\.gemini\\antigravity --extension_server_port 62300 --extension_server_csrf_token fedcba98-7654-3210-fedc-ba9876543210',
|
|
])
|
|
|
|
expect(server).toEqual({
|
|
port: 62300,
|
|
csrfToken: 'fedcba98-7654-3210-fedc-ba9876543210',
|
|
})
|
|
})
|
|
|
|
it('parses quoted flag values', () => {
|
|
const server = parseAntigravityServerInfoFromLine(
|
|
'Antigravity language_server_windows_x64.exe --extension_server_port "62301" --extension_server_csrf_token "fedcba98-7654-3210-fedc-ba9876543211"',
|
|
)
|
|
|
|
expect(server).toEqual({
|
|
port: 62301,
|
|
csrfToken: 'fedcba98-7654-3210-fedc-ba9876543211',
|
|
})
|
|
})
|
|
|
|
it('normalizes app_data_dir from app and CLI process args', () => {
|
|
expect(extractAntigravityAppDataDirFromLine(
|
|
'language_server --app_data_dir antigravity --https_server_port 0 --csrf_token 01234567-89ab-cdef-0123-456789abcdef',
|
|
)).toBe('antigravity')
|
|
|
|
expect(extractAntigravityAppDataDirFromLine(
|
|
'language_server --app_data_dir /Users/dev/.gemini/antigravity-cli --https_server_port 0 --csrf_token 01234567-89ab-cdef-0123-456789abcdef',
|
|
)).toBe('antigravity-cli')
|
|
|
|
expect(extractAntigravityAppDataDirFromLine(
|
|
'language_server.exe --app_data_dir "C:\\Users\\Admin\\.gemini\\antigravity-cli" --extension_server_port 62225 --extension_server_csrf_token abcdef01-2345-6789-abcd-ef0123456789',
|
|
)).toBe('antigravity-cli')
|
|
|
|
expect(extractAntigravityAppDataDirFromLine(
|
|
'language_server_windows_x64.exe --app_data_dir antigravity-ide --extension_server_port 8720 --extension_server_csrf_token 39800f1b-343a-40b0-8eb5-850702450346',
|
|
)).toBe('antigravity-ide')
|
|
})
|
|
|
|
it('accepts Antigravity 2 ephemeral port zero', () => {
|
|
const server = parseAntigravityServerInfoFromLine(
|
|
'antigravity language_server_macos_arm --https_server_port 0 --csrf_token 01234567-89ab-cdef-0123-456789abcdef',
|
|
)
|
|
|
|
expect(server).toEqual({
|
|
port: 0,
|
|
csrfToken: '01234567-89ab-cdef-0123-456789abcdef',
|
|
})
|
|
})
|
|
|
|
it('matches language-server and antigravity markers case-insensitively', () => {
|
|
const server = parseAntigravityServerInfoFromLine(
|
|
'ANTIGRAVITY LANGUAGE_SERVER_WINDOWS_X64.EXE --extension_server_port 62302 --extension_server_csrf_token fedcba98-7654-3210-fedc-ba9876543212',
|
|
)
|
|
|
|
expect(server).toEqual({
|
|
port: 62302,
|
|
csrfToken: 'fedcba98-7654-3210-fedc-ba9876543212',
|
|
})
|
|
})
|
|
|
|
it('ignores process args without an antigravity marker', () => {
|
|
expect(parseAntigravityServerInfoFromLine(
|
|
'language_server --extension_server_port 62300 --extension_server_csrf_token fedcba98-7654-3210-fedc-ba9876543210',
|
|
)).toBeNull()
|
|
})
|
|
|
|
it('ignores invalid ports', () => {
|
|
expect(parseAntigravityServerInfoFromLine(
|
|
'antigravity language_server --extension_server_port 99999 --extension_server_csrf_token fedcba98-7654-3210-fedc-ba9876543210',
|
|
)).toBeNull()
|
|
})
|
|
|
|
it('ignores chained flag names as values', () => {
|
|
expect(parseAntigravityServerInfoFromLine(
|
|
'antigravity language_server --extension_server_port=--extension_server_csrf_token --extension_server_csrf_token fedcba98-7654-3210-fedc-ba9876543210',
|
|
)).toBeNull()
|
|
})
|
|
|
|
it('ignores implausibly short CSRF tokens', () => {
|
|
expect(parseAntigravityServerInfoFromLine(
|
|
'antigravity language_server --extension_server_port 62300 --extension_server_csrf_token short',
|
|
)).toBeNull()
|
|
})
|
|
|
|
it('extracts model maps from wrapped and unwrapped RPC responses', () => {
|
|
expect(extractAntigravityModelMap({
|
|
response: { models: { high: { model: 'MODEL_PLACEHOLDER_M7' } } },
|
|
})).toEqual({ MODEL_PLACEHOLDER_M7: 'high' })
|
|
|
|
expect(extractAntigravityModelMap({
|
|
models: { low: { model: 'MODEL_PLACEHOLDER_M8' } },
|
|
})).toEqual({ MODEL_PLACEHOLDER_M8: 'low' })
|
|
expect(extractAntigravityModelMap({
|
|
models: { bad: null, good: { model: 'MODEL_PLACEHOLDER_M9' } },
|
|
})).toEqual({ MODEL_PLACEHOLDER_M9: 'good' })
|
|
expect(extractAntigravityModelMap({
|
|
models: { 'gemini-3-flash-agent': { model: 'MODEL_PLACEHOLDER_M133', displayName: 'Gemini 3.5 Flash (High)' } },
|
|
})).toEqual({ MODEL_PLACEHOLDER_M133: 'gemini-3.5-flash-high' })
|
|
expect(extractAntigravityModelMap(null)).toEqual({})
|
|
})
|
|
|
|
it('extracts generator metadata from wrapped and unwrapped RPC responses', () => {
|
|
const metadata = [{
|
|
chatModel: {
|
|
model: 'gemini-3-pro',
|
|
usage: {
|
|
model: 'gemini-3-pro',
|
|
inputTokens: '10',
|
|
outputTokens: '4',
|
|
apiProvider: 'google',
|
|
},
|
|
},
|
|
}]
|
|
|
|
expect(extractAntigravityGeneratorMetadata({ response: { generatorMetadata: metadata } })).toEqual(metadata)
|
|
expect(extractAntigravityGeneratorMetadata({ generatorMetadata: metadata })).toEqual(metadata)
|
|
expect(extractAntigravityGeneratorMetadata({ response: { generatorMetadata: null } })).toEqual([])
|
|
expect(extractAntigravityGeneratorMetadata(null)).toEqual([])
|
|
})
|
|
|
|
it('derives cascade ids from legacy .pb and Antigravity 2 .db files', () => {
|
|
expect(antigravityCascadeIdFromPath('/tmp/123.pb')).toBe('123')
|
|
expect(antigravityCascadeIdFromPath('/tmp/456.db')).toBe('456')
|
|
expect(antigravityCascadeIdFromPath('/tmp/789.db-wal')).toBe('789.db-wal')
|
|
})
|
|
|
|
it('routes app and CLI source paths to matching Antigravity app data dirs', () => {
|
|
expect(antigravityAppDataDirFromSourcePath(
|
|
'/Users/dev/.gemini/antigravity/conversations/session.db',
|
|
)).toBe('antigravity')
|
|
|
|
expect(antigravityAppDataDirFromSourcePath(
|
|
'/Users/dev/.gemini/antigravity-cli/conversations/session.pb',
|
|
)).toBe('antigravity-cli')
|
|
|
|
expect(antigravityAppDataDirFromSourcePath(
|
|
'C:\\Users\\Admin\\.gemini\\antigravity-cli\\implicit\\session.pb',
|
|
)).toBe('antigravity-cli')
|
|
|
|
expect(antigravityAppDataDirFromSourcePath(
|
|
'/Users/dev/.gemini/antigravity-ide/conversations/session.db',
|
|
)).toBe('antigravity-ide')
|
|
|
|
expect(antigravityAppDataDirFromSourcePath(
|
|
'C:\\Users\\Admin\\.gemini\\antigravity-ide\\implicit\\session.pb',
|
|
)).toBe('antigravity-ide')
|
|
})
|
|
|
|
it('discovers legacy .pb files and Antigravity 2 .db files only', async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-'))
|
|
|
|
try {
|
|
await writeFile(join(dir, 'legacy.pb'), '')
|
|
await writeFile(join(dir, 'antigravity-2.db'), '')
|
|
await writeFile(join(dir, 'uppercase.DB'), '')
|
|
await writeFile(join(dir, 'antigravity-2.db-wal'), '')
|
|
await mkdir(join(dir, 'directory.pb'))
|
|
|
|
const sources = await discoverAntigravitySessionSources([{
|
|
dir,
|
|
project: 'test-project',
|
|
extensions: ['.pb', '.db'],
|
|
}])
|
|
|
|
expect(sources).toEqual([
|
|
{ path: join(dir, 'antigravity-2.db'), project: 'test-project', provider: 'antigravity' },
|
|
{ path: join(dir, 'legacy.pb'), project: 'test-project', provider: 'antigravity' },
|
|
{ path: join(dir, 'uppercase.DB'), project: 'test-project', provider: 'antigravity' },
|
|
])
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('discovers antigravity-ide conversation and implicit files', async () => {
|
|
const tempHome = await mkdtemp(join(tmpdir(), 'codeburn-home-'))
|
|
const conversationsDir = join(tempHome, '.gemini', 'antigravity-ide', 'conversations')
|
|
const implicitDir = join(tempHome, '.gemini', 'antigravity-ide', 'implicit')
|
|
|
|
await mkdir(conversationsDir, { recursive: true })
|
|
await mkdir(implicitDir, { recursive: true })
|
|
|
|
await writeFile(join(conversationsDir, 'session1.db'), '')
|
|
await writeFile(join(implicitDir, 'session2.pb'), '')
|
|
|
|
const roots = [
|
|
{
|
|
dir: conversationsDir,
|
|
project: 'antigravity-ide',
|
|
extensions: ['.pb', '.db'] as const,
|
|
},
|
|
{
|
|
dir: implicitDir,
|
|
project: 'antigravity-ide',
|
|
extensions: ['.pb'] as const,
|
|
},
|
|
]
|
|
|
|
const sources = await discoverAntigravitySessionSources(roots)
|
|
expect(sources).toEqual([
|
|
{ path: join(conversationsDir, 'session1.db'), project: 'antigravity-ide', provider: 'antigravity' },
|
|
{ path: join(implicitDir, 'session2.pb'), project: 'antigravity-ide', provider: 'antigravity' },
|
|
])
|
|
|
|
await rm(tempHome, { recursive: true, force: true })
|
|
})
|
|
|
|
it('displays Gemini 3.5 Flash thinking variants as the base model', () => {
|
|
const provider = createAntigravityProvider()
|
|
|
|
expect(provider.modelDisplayName('gemini-3.5-flash')).toBe('Gemini 3.5 Flash')
|
|
expect(provider.modelDisplayName('gemini-3.5-flash-high')).toBe('Gemini 3.5 Flash')
|
|
expect(provider.modelDisplayName('gemini-3.5-flash-medium')).toBe('Gemini 3.5 Flash')
|
|
expect(provider.modelDisplayName('gemini-3.5-flash-low')).toBe('Gemini 3.5 Flash')
|
|
expect(provider.modelDisplayName('Gemini 3.5 Flash (High)')).toBe('Gemini 3.5 Flash')
|
|
})
|
|
|
|
it('captures exact Antigravity CLI statusLine usage as fallback calls', async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-'))
|
|
process.env['CODEBURN_CACHE_DIR'] = dir
|
|
|
|
try {
|
|
const payload = {
|
|
conversation_id: 'ce061468-2e2b-4c6f-bf4f-e072bd5fa986',
|
|
session_id: 'session-1',
|
|
cwd: '/workspace/project',
|
|
model: {
|
|
id: 'Gemini 3.5 Flash (High)',
|
|
display_name: 'Gemini 3.5 Flash (High)',
|
|
},
|
|
context_window: {
|
|
current_usage: {
|
|
input_tokens: 28407,
|
|
output_tokens: 137,
|
|
cache_creation_input_tokens: 0,
|
|
cache_read_input_tokens: 0,
|
|
},
|
|
},
|
|
}
|
|
|
|
expect(await recordAntigravityStatusLinePayload(payload)).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(payload)).toBe(true)
|
|
|
|
const recorded = await readFile(getAntigravityStatusLineEventsPath(), 'utf-8')
|
|
expect(recorded).not.toContain('/workspace/project')
|
|
expect(JSON.parse(recorded.split(/\r?\n/)[0]!)).not.toHaveProperty('cwd')
|
|
|
|
const source = {
|
|
path: getAntigravityStatusLineEventsPath(),
|
|
project: 'antigravity-cli',
|
|
provider: 'antigravity',
|
|
}
|
|
|
|
const parser = createAntigravityProvider().createSessionParser(source, new Set())
|
|
const calls = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
|
|
expect(calls).toHaveLength(1)
|
|
expect(calls[0]).toMatchObject({
|
|
provider: 'antigravity',
|
|
model: 'Gemini 3.5 Flash (High)',
|
|
inputTokens: 28407,
|
|
outputTokens: 137,
|
|
cacheCreationInputTokens: 0,
|
|
cacheReadInputTokens: 0,
|
|
cachedInputTokens: 0,
|
|
sessionId: 'ce061468-2e2b-4c6f-bf4f-e072bd5fa986',
|
|
project: 'antigravity-cli',
|
|
})
|
|
expect(calls[0]!.projectPath).toBeUndefined()
|
|
expect(calls[0]!.costUSD).toBeGreaterThan(0)
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('skips statusLine fallback calls when RPC cache already covered the conversation', async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-rpc-dedup-'))
|
|
process.env['CODEBURN_CACHE_DIR'] = dir
|
|
|
|
try {
|
|
expect(await recordAntigravityStatusLinePayload({
|
|
conversation_id: 'rpc-covered-conversation',
|
|
session_id: 'session-1',
|
|
model: 'Gemini 3.5 Flash (High)',
|
|
context_window: {
|
|
current_usage: {
|
|
input_tokens: 1000,
|
|
output_tokens: 100,
|
|
cache_creation_input_tokens: 0,
|
|
cache_read_input_tokens: 0,
|
|
},
|
|
},
|
|
})).toBe(true)
|
|
|
|
const parser = createAntigravityProvider().createSessionParser({
|
|
path: getAntigravityStatusLineEventsPath(),
|
|
project: 'antigravity-cli',
|
|
provider: 'antigravity',
|
|
}, new Set(['antigravity:rpc-covered-conversation:0']))
|
|
|
|
const calls = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
|
|
expect(calls).toEqual([])
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('skips singleton statusLine snapshots and deltas monotonic usage', async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-runs-'))
|
|
process.env['CODEBURN_CACHE_DIR'] = dir
|
|
|
|
const basePayload = {
|
|
conversation_id: 'statusline-runs',
|
|
session_id: 'session-1',
|
|
model: 'Gemini 3.5 Flash (High)',
|
|
}
|
|
|
|
const withUsage = (
|
|
input_tokens: number,
|
|
output_tokens: number,
|
|
cache_read_input_tokens = 0,
|
|
) => ({
|
|
...basePayload,
|
|
context_window: {
|
|
current_usage: {
|
|
input_tokens,
|
|
output_tokens,
|
|
cache_creation_input_tokens: 0,
|
|
cache_read_input_tokens,
|
|
},
|
|
},
|
|
})
|
|
|
|
try {
|
|
expect(await recordAntigravityStatusLinePayload(withUsage(100, 10))).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(withUsage(200, 20))).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(withUsage(200, 20))).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(withUsage(300, 30, 50))).toBe(true)
|
|
|
|
const parser = createAntigravityProvider().createSessionParser({
|
|
path: getAntigravityStatusLineEventsPath(),
|
|
project: 'antigravity-cli',
|
|
provider: 'antigravity',
|
|
}, new Set())
|
|
|
|
const calls = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
|
|
expect(calls).toHaveLength(2)
|
|
expect(calls.map(call => [call.inputTokens, call.outputTokens, call.cacheReadInputTokens])).toEqual([
|
|
[200, 20, 0],
|
|
[100, 10, 50],
|
|
])
|
|
expect(calls.map(call => call.cachedInputTokens)).toEqual([0, 0])
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('treats non-monotonic statusLine usage as a new request snapshot', async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-reset-'))
|
|
process.env['CODEBURN_CACHE_DIR'] = dir
|
|
|
|
const payload = (
|
|
input_tokens: number,
|
|
output_tokens: number,
|
|
cache_read_input_tokens = 0,
|
|
) => ({
|
|
conversation_id: 'statusline-reset',
|
|
session_id: 'session-1',
|
|
model: 'Gemini 3.5 Flash (High)',
|
|
context_window: {
|
|
current_usage: {
|
|
input_tokens,
|
|
output_tokens,
|
|
cache_creation_input_tokens: 0,
|
|
cache_read_input_tokens,
|
|
},
|
|
},
|
|
})
|
|
|
|
try {
|
|
expect(await recordAntigravityStatusLinePayload(payload(1000, 100))).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(payload(1000, 100))).toBe(true)
|
|
expect(await recordAntigravityStatusLinePayload(payload(200, 30, 500))).toBe(true)
|
|
|
|
const parser = createAntigravityProvider().createSessionParser({
|
|
path: getAntigravityStatusLineEventsPath(),
|
|
project: 'antigravity-cli',
|
|
provider: 'antigravity',
|
|
}, new Set())
|
|
|
|
const calls = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
|
|
expect(calls).toHaveLength(2)
|
|
expect(calls.map(call => [call.inputTokens, call.outputTokens, call.cacheReadInputTokens])).toEqual([
|
|
[1000, 100, 0],
|
|
[200, 30, 500],
|
|
])
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('always reparses append-only statusLine sources but not unchanged cached cascades', () => {
|
|
const statusLinePath = getAntigravityStatusLineEventsPath()
|
|
|
|
expect(shouldReparseAntigravitySource(statusLinePath, 1)).toBe(true)
|
|
expect(shouldReparseAntigravitySource('/tmp/antigravity/conversation.pb', 0)).toBe(true)
|
|
expect(shouldReparseAntigravitySource('/tmp/antigravity/conversation.pb', 1)).toBe(false)
|
|
})
|
|
|
|
it('parses current Antigravity CLI SQLite conversations with non-zero token usage', async () => {
|
|
if (!isSqliteAvailable()) return
|
|
|
|
const tempHome = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-current-cli-'))
|
|
const cacheDir = join(tempHome, 'cache')
|
|
const previousCacheDir = process.env['CODEBURN_CACHE_DIR']
|
|
process.env['CODEBURN_CACHE_DIR'] = cacheDir
|
|
|
|
try {
|
|
const fixture = JSON.parse(await readFile(
|
|
new URL('../fixtures/antigravity-cli-current/gen-metadata.json', import.meta.url),
|
|
'utf-8',
|
|
)) as CurrentCliFixture
|
|
const conversationsDir = join(tempHome, '.gemini', 'antigravity-cli', 'conversations')
|
|
const logsDir = join(
|
|
tempHome,
|
|
'.gemini',
|
|
'antigravity-cli',
|
|
'brain',
|
|
fixture.conversationId,
|
|
'.system_generated',
|
|
'logs',
|
|
)
|
|
|
|
await mkdir(conversationsDir, { recursive: true })
|
|
await mkdir(logsDir, { recursive: true })
|
|
await writeFile(
|
|
join(logsDir, 'transcript.jsonl'),
|
|
await readFile(
|
|
new URL(
|
|
'../fixtures/antigravity-cli-current/brain/fixture-current-cli/.system_generated/logs/transcript.jsonl',
|
|
import.meta.url,
|
|
),
|
|
'utf-8',
|
|
),
|
|
)
|
|
|
|
const dbPath = join(conversationsDir, `${fixture.conversationId}.db`)
|
|
createCurrentAntigravityCliDb(dbPath, fixture)
|
|
|
|
const sources = await discoverAntigravitySessionSources([{
|
|
dir: conversationsDir,
|
|
project: 'antigravity-cli',
|
|
extensions: ['.pb', '.db'],
|
|
}])
|
|
expect(sources).toEqual([{ path: dbPath, project: 'antigravity-cli', provider: 'antigravity' }])
|
|
|
|
const calls = await collectAntigravityCalls(sources[0]!)
|
|
|
|
expect(calls.length).toBeGreaterThanOrEqual(1)
|
|
expect(calls[0]).toMatchObject({
|
|
provider: 'antigravity',
|
|
model: 'gemini-3.1-pro-high',
|
|
inputTokens: 30265,
|
|
outputTokens: 659,
|
|
reasoningTokens: 71,
|
|
sessionId: fixture.conversationId,
|
|
project: 'antigravity-cli',
|
|
})
|
|
expect(calls[0]!.projectPath).toBeUndefined()
|
|
expect(calls[0]!.costUSD).toBeGreaterThan(0)
|
|
} finally {
|
|
if (previousCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
|
else process.env['CODEBURN_CACHE_DIR'] = previousCacheDir
|
|
await rm(tempHome, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('deduplicates current SQLite rows against RPC response ids with hyphens', async () => {
|
|
if (!isSqliteAvailable()) return
|
|
|
|
const tempHome = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-current-cli-dedup-'))
|
|
const cacheDir = join(tempHome, 'cache')
|
|
const previousCacheDir = process.env['CODEBURN_CACHE_DIR']
|
|
process.env['CODEBURN_CACHE_DIR'] = cacheDir
|
|
|
|
try {
|
|
const fixture = JSON.parse(await readFile(
|
|
new URL('../fixtures/antigravity-cli-current/gen-metadata.json', import.meta.url),
|
|
'utf-8',
|
|
)) as CurrentCliFixture
|
|
const conversationsDir = join(tempHome, '.gemini', 'antigravity-cli', 'conversations')
|
|
|
|
await mkdir(conversationsDir, { recursive: true })
|
|
|
|
const dbPath = join(conversationsDir, `${fixture.conversationId}.db`)
|
|
createCurrentAntigravityCliDb(dbPath, fixture)
|
|
|
|
const parser = createAntigravityProvider().createSessionParser({
|
|
path: dbPath,
|
|
project: 'antigravity-cli',
|
|
provider: 'antigravity',
|
|
}, new Set([`antigravity:${fixture.conversationId}:fixture-response-1`]))
|
|
const calls = []
|
|
for await (const call of parser.parse()) calls.push(call)
|
|
|
|
expect(calls).toEqual([])
|
|
} finally {
|
|
if (previousCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
|
else process.env['CODEBURN_CACHE_DIR'] = previousCacheDir
|
|
await rm(tempHome, { recursive: true, force: true })
|
|
}
|
|
})
|
|
})
|