diff --git a/src/shared/fixtures/grok/README.md b/src/shared/fixtures/grok/README.md new file mode 100644 index 00000000000..bfb55574f93 --- /dev/null +++ b/src/shared/fixtures/grok/README.md @@ -0,0 +1,15 @@ +# Grok Build compatibility fixtures + +Golden inputs captured against Grok Build behavior (and the open-source +`xai-org/grok-build` contracts where noted). Use these from unit tests so Orca’s +Grok integration does not drift on: + +| File | Contract | +|------|----------| +| `billing-*.json` | cli-chat-proxy billing / proto3 zero omission | +| `hook-stop-envelope.json` | Stop hook camelCase envelope | +| `chat_history-sample.jsonl` | Native chat / AI Vault transcript rows | +| `encode-cwd-goldens.json` | `encode_cwd_dirname` short + slug-blake3 long form | + +`encode-cwd-goldens.json` is the OSS algorithm oracle. Wire it to +`encodeGrokCwdDirName` once that helper is on main (see related PRs). diff --git a/src/shared/fixtures/grok/billing-credits-no-weekly.json b/src/shared/fixtures/grok/billing-credits-no-weekly.json new file mode 100644 index 00000000000..ffd2c0cccf0 --- /dev/null +++ b/src/shared/fixtures/grok/billing-credits-no-weekly.json @@ -0,0 +1,11 @@ +{ + "config": { + "currentPeriod": { + "type": "USAGE_PERIOD_TYPE_WEEKLY", + "start": "2026-07-10T19:38:56.948570+00:00", + "end": "2026-07-17T19:38:56.948570+00:00" + }, + "isUnifiedBillingUser": true, + "subscriptionTier": "SuperGrok" + } +} diff --git a/src/shared/fixtures/grok/billing-monthly-unified.json b/src/shared/fixtures/grok/billing-monthly-unified.json new file mode 100644 index 00000000000..337ecc1608b --- /dev/null +++ b/src/shared/fixtures/grok/billing-monthly-unified.json @@ -0,0 +1,8 @@ +{ + "config": { + "monthlyLimit": { "val": 150000 }, + "used": { "val": 837 }, + "billingPeriodStart": "2026-07-01T00:00:00+00:00", + "billingPeriodEnd": "2026-08-01T00:00:00+00:00" + } +} diff --git a/src/shared/fixtures/grok/billing-weekly-42.json b/src/shared/fixtures/grok/billing-weekly-42.json new file mode 100644 index 00000000000..fcfff273366 --- /dev/null +++ b/src/shared/fixtures/grok/billing-weekly-42.json @@ -0,0 +1,14 @@ +{ + "config": { + "creditUsagePercent": 42, + "currentPeriod": { + "type": "USAGE_PERIOD_TYPE_WEEKLY", + "start": "2026-06-30T18:36:14.268512+00:00", + "end": "2026-07-07T18:36:14.268512+00:00" + }, + "billingPeriodStart": "2026-06-30T18:36:14.268512+00:00", + "billingPeriodEnd": "2026-07-07T18:36:14.268512+00:00", + "subscriptionTier": "SuperGrok", + "isUnifiedBillingUser": true + } +} diff --git a/src/shared/fixtures/grok/billing-weekly-zero-omitted.json b/src/shared/fixtures/grok/billing-weekly-zero-omitted.json new file mode 100644 index 00000000000..3ee1466d18a --- /dev/null +++ b/src/shared/fixtures/grok/billing-weekly-zero-omitted.json @@ -0,0 +1,13 @@ +{ + "config": { + "currentPeriod": { + "type": "USAGE_PERIOD_TYPE_WEEKLY", + "start": "2026-06-30T18:36:14.268512+00:00", + "end": "2026-07-07T18:36:14.268512+00:00" + }, + "billingPeriodStart": "2026-06-30T18:36:14.268512+00:00", + "billingPeriodEnd": "2026-07-07T18:36:14.268512+00:00", + "subscriptionTier": "SuperGrok", + "isUnifiedBillingUser": true + } +} diff --git a/src/shared/fixtures/grok/chat_history-sample.jsonl b/src/shared/fixtures/grok/chat_history-sample.jsonl new file mode 100644 index 00000000000..bdfc99a9656 --- /dev/null +++ b/src/shared/fixtures/grok/chat_history-sample.jsonl @@ -0,0 +1,4 @@ +{"type":"user","content":[{"type":"text","text":"\nFix the flaky test\n"}],"timestamp":"2026-07-18T12:00:01.000Z"} +{"type":"assistant","content":[{"type":"text","text":"I'll look at the failing suite."}],"timestamp":"2026-07-18T12:00:02.000Z"} +{"type":"user","content":[{"type":"text","text":"\nos: macos\n"}],"timestamp":"2026-07-18T12:00:00.500Z","synthetic_reason":"bootstrap"} +{"type":"assistant","tool_calls":[{"id":"call_1","name":"run_terminal_command","arguments":{"command":"pnpm test"}}],"timestamp":"2026-07-18T12:00:03.000Z"} diff --git a/src/shared/fixtures/grok/encode-cwd-goldens.json b/src/shared/fixtures/grok/encode-cwd-goldens.json new file mode 100644 index 00000000000..a323f48d8aa --- /dev/null +++ b/src/shared/fixtures/grok/encode-cwd-goldens.json @@ -0,0 +1,31 @@ +{ + "source": "xai-org/grok-build xai_grok_config::paths::encode_cwd_dirname", + "short": [ + { + "cwd": "/tmp/work", + "encoded": "%2Ftmp%2Fwork" + } + ], + "long": [ + { + "cwd": "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "encoded": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-e22e6487078c7674" + }, + { + "cwd": "/Users/dev/Documents/開発プロジェクト/機能追加/テスト環境/ソースコード/main-branch", + "encoded": "main-branch-6aaeefdde2a621aa" + }, + { + "cwd": "/Users/user/Library/Mobile Documents/com~apple~CloudDocs/项目文件/深层嵌套目录/更深层次的/工作区域/project", + "encoded": "project-5a22eee5d15e14bd" + }, + { + "cwd": "/Users/user/Documents/工作文件夹/二零二六年项目/子目录一/子目录二/子目录三/源代码/code", + "encoded": "code-e5f13e136e4516ab" + }, + { + "cwd": "/Users/user/Library/CloudStorage/OneDrive-대한민국회사/프로젝트/개발환경/소스코드/백엔드/서비스/my-app", + "encoded": "my-app-00d240a68a30d482" + } + ] +} diff --git a/src/shared/fixtures/grok/grok-fixtures.test.ts b/src/shared/fixtures/grok/grok-fixtures.test.ts new file mode 100644 index 00000000000..fed8bb0fc2a --- /dev/null +++ b/src/shared/fixtures/grok/grok-fixtures.test.ts @@ -0,0 +1,135 @@ +import { describe, expect, it, vi } from 'vitest' +import { decodeGrokTranscriptLine } from '../../../main/native-chat/transcript-line-decoders' +import { fetchGrokRateLimits } from '../../../main/rate-limits/grok-fetcher' +import { + readGrokFixtureJson, + readGrokFixtureJsonl, + readGrokFixtureText +} from './load-grok-fixtures' + +const netFetchMock = vi.hoisted(() => vi.fn()) +const authState = vi.hoisted<{ file: string | null }>(() => ({ file: null })) + +vi.mock('electron', () => ({ + net: { fetch: netFetchMock } +})) + +vi.mock('node:fs', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + existsSync: (path: string) => { + if (String(path).endsWith('auth.json')) { + return authState.file !== null + } + return actual.existsSync(path) + }, + readFileSync: (path: string, encoding?: BufferEncoding) => { + if (String(path).endsWith('auth.json')) { + if (authState.file === null) { + throw new Error('ENOENT') + } + return authState.file + } + return actual.readFileSync(path, encoding as BufferEncoding) + } + } +}) + +vi.mock('node:os', () => ({ homedir: () => '/home/test' })) + +function jsonResponse(body: unknown, status = 200): Response { + return { + ok: status >= 200 && status < 300, + status, + json: async () => body + } as Response +} + +function freshAuthJson(): string { + return JSON.stringify({ + 'https://auth.x.ai::client': { + key: 'access-token', + user_id: 'user-1', + email: 'dev@example.com', + expires_at: '2099-01-01T00:00:00.000Z' + } + }) +} + +describe('Grok Build fixtures (compatibility goldens)', () => { + it('loads billing weekly 42% golden into the rate-limit mapper', async () => { + authState.file = freshAuthJson() + netFetchMock.mockResolvedValueOnce( + jsonResponse(readGrokFixtureJson('billing-weekly-42.json')) + ) + const result = await fetchGrokRateLimits() + expect(result.status).toBe('ok') + expect(result.weekly?.usedPercent).toBe(42) + }) + + it('maps proto3-omitted weekly zero from the golden billing body', async () => { + authState.file = freshAuthJson() + netFetchMock.mockResolvedValueOnce( + jsonResponse(readGrokFixtureJson('billing-weekly-zero-omitted.json')) + ) + const result = await fetchGrokRateLimits() + expect(result.status).toBe('ok') + expect(result.weekly?.usedPercent).toBe(0) + }) + + it('falls back to monthly unified budget from the golden bodies', async () => { + authState.file = freshAuthJson() + // Credits view: ambiguous weekly period, no percent → default /billing monthly. + netFetchMock + .mockResolvedValueOnce(jsonResponse(readGrokFixtureJson('billing-credits-no-weekly.json'))) + .mockResolvedValueOnce(jsonResponse(readGrokFixtureJson('billing-monthly-unified.json'))) + const result = await fetchGrokRateLimits() + expect(result.status).toBe('ok') + expect(result.weekly).toBeNull() + expect(result.monthly?.usedPercent).toBeCloseTo((837 / 150000) * 100, 5) + }) + + it('decodes chat_history sample lines for native chat', () => { + const lines = readGrokFixtureJsonl('chat_history-sample.jsonl') + expect(lines.length).toBeGreaterThanOrEqual(3) + + const user = decodeGrokTranscriptLine(lines[0]!, 'row-0') + expect(user?.role).toBe('user') + expect(user?.blocks.some((b) => b.type === 'text' && b.text.includes('Fix the flaky'))).toBe( + true + ) + + const assistant = decodeGrokTranscriptLine(lines[1]!, 'row-1') + expect(assistant?.role).toBe('assistant') + + // Bootstrap / synthetic user rows are omitted from the conversation view. + expect(decodeGrokTranscriptLine(lines[2]!, 'row-2')).toBeNull() + }) + + it('keeps Stop hook envelope fields Grok actually emits', () => { + const envelope = readGrokFixtureJson<{ + hookEventName: string + sessionId: string + cwd: string + }>('hook-stop-envelope.json') + expect(envelope.hookEventName).toBe('Stop') + expect(envelope.sessionId).toMatch(/^[0-9a-f-]{36}$/i) + expect(envelope.cwd.startsWith('/')).toBe(true) + }) + + it('documents encode_cwd_dirname goldens from open-source Grok Build', () => { + const goldens = readGrokFixtureJson<{ + short: Array<{ cwd: string; encoded: string }> + long: Array<{ cwd: string; encoded: string }> + }>('encode-cwd-goldens.json') + expect(goldens.short[0]?.encoded).toBe('%2Ftmp%2Fwork') + expect(goldens.long.length).toBeGreaterThanOrEqual(4) + // Long-form oracle: slug + 16 hex chars (blake3 prefix). + for (const row of goldens.long) { + expect(row.encoded).toMatch(/^[a-z0-9-]+-[0-9a-f]{16}$/) + } + // Fixture text must stay stable for CI lock. + expect(readGrokFixtureText('encode-cwd-goldens.json')).toContain('main-branch-6aaeefdde2a621aa') + }) +}) diff --git a/src/shared/fixtures/grok/hook-stop-envelope.json b/src/shared/fixtures/grok/hook-stop-envelope.json new file mode 100644 index 00000000000..906d0ebdc4e --- /dev/null +++ b/src/shared/fixtures/grok/hook-stop-envelope.json @@ -0,0 +1,11 @@ +{ + "hookEventName": "Stop", + "sessionId": "019e37f4-5135-7b63-a4ab-6d13aa6bf534", + "cwd": "/Users/dev/project", + "workspaceRoot": "/Users/dev/project", + "timestamp": "2026-07-18T12:00:00.000Z", + "payload": { + "hook_event_name": "stop", + "session_id": "019e37f4-5135-7b63-a4ab-6d13aa6bf534" + } +} diff --git a/src/shared/fixtures/grok/load-grok-fixtures.ts b/src/shared/fixtures/grok/load-grok-fixtures.ts new file mode 100644 index 00000000000..4fd1b846177 --- /dev/null +++ b/src/shared/fixtures/grok/load-grok-fixtures.ts @@ -0,0 +1,21 @@ +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const FIXTURE_DIR = dirname(fileURLToPath(import.meta.url)) + +/** Read a fixture file next to this module (works under Vitest/Node ESM). */ +export function readGrokFixtureText(name: string): string { + return readFileSync(join(FIXTURE_DIR, name), 'utf8') +} + +export function readGrokFixtureJson(name: string): T { + return JSON.parse(readGrokFixtureText(name)) as T +} + +export function readGrokFixtureJsonl(name: string): string[] { + return readGrokFixtureText(name) + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0) +}