Files
orcs-code/src/utils/user.test.ts
emsanakhchivan 91f93ce615 feat: SDK Foundation — Type Declarations, Errors, and Utilities (#866)
* feat(sdk): add SDK foundation — type declarations, errors, and utilities

Adds standalone SDK building blocks with no SDK source dependencies:
- sdk.d.ts: ambient type declarations for SDK bundle
- coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types
- errors.ts: SDK-specific error classes
- validation.ts: input validation utilities
- messageFilters.ts: extracted message filter logic
- handlePromptSubmit.ts: imports from messageFilters
- 16 generated-types tests

* fix(sdk): narrow assertFunction type from broad Function to callable signature

Code review finding: assertFunction used `asserts value is Function` which
accepts any function-like value without narrowing. Changed to
`(...args: any[]) => any` for better type safety.

* fix(sdk): update sdk.d.ts header — manually maintained, not generated

Reviewer noted the header said "Generated from index.ts" but no generator
produces this file. Updated to "Manually maintained — keep in sync with
index.ts". Drift detection added in validate-externals.ts (PR 3).

* fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts

Tighten SDK public type contract to resolve reviewer blockers:

- PermissionResult: unknown[] → precise 6-shape discriminated union
  (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories)
- SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.)
- ForkSessionResult: session_id → sessionId
- SDKPermissionRequestMessage: uuid + session_id now required
- SDKPermissionTimeoutMessage: added uuid + session_id
- SessionMessage: parent_uuid → parentUuid
- SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline
  definitions with re-exports from coreTypes.generated.ts

---------

Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
2026-04-29 14:53:01 +08:00

94 lines
2.7 KiB
TypeScript

import { afterEach, describe, expect, mock, test } from 'bun:test'
const originalEnv = { ...process.env }
async function importFreshUserModule() {
return import(`./user.ts?ts=${Date.now()}-${Math.random()}`)
}
function installCommonMocks(options?: {
oauthEmail?: string
gitEmail?: string
}) {
// NOTE: Do NOT mock ../bootstrap/state.js here.
// mock.module() is process-global in bun:test and mock.restore() does NOT
// undo it. Mocking state.js leaks getSessionId = () => 'session-test' into
// every other test file that imports state.js (e.g. SDK CON-1 tests).
// The dynamic import (importFreshUserModule) will use the real state.js,
// which is fine — these tests only assert email, not sessionId.
mock.module('./auth.js', () => ({
getOauthAccountInfo: () =>
options?.oauthEmail
? {
emailAddress: options.oauthEmail,
organizationUuid: 'org-test',
accountUuid: 'acct-test',
}
: undefined,
getRateLimitTier: () => null,
getSubscriptionType: () => null,
}))
mock.module('./config.js', () => ({
getGlobalConfig: () => ({}),
getOrCreateUserID: () => 'device-test',
}))
mock.module('./cwd.js', () => ({
getCwd: () => 'C:\\repo',
}))
mock.module('./env.js', () => ({
env: { platform: 'windows' },
getHostPlatformForAnalytics: () => 'windows',
}))
mock.module('./envUtils.js', () => ({
isEnvTruthy: (value: string | undefined) =>
!!value && value !== '0' && value.toLowerCase() !== 'false',
}))
mock.module('execa', () => ({
execa: async () => ({
exitCode: options?.gitEmail ? 0 : 1,
stdout: options?.gitEmail ?? '',
}),
}))
}
afterEach(() => {
mock.restore()
process.env = { ...originalEnv }
delete (globalThis as Record<string, unknown>).MACRO
})
describe('user email fallbacks', () => {
test('getCoreUserData does not synthesize Anthropic email from COO_CREATOR', async () => {
process.env.USER_TYPE = 'ant'
process.env.COO_CREATOR = 'alice'
;(globalThis as Record<string, unknown>).MACRO = { VERSION: '0.0.0' }
installCommonMocks()
const { getCoreUserData } = await importFreshUserModule()
const result = getCoreUserData()
expect(result.email).toBeUndefined()
})
test('initUser falls back to git email when oauth email is missing', async () => {
process.env.USER_TYPE = 'ant'
process.env.COO_CREATOR = 'alice'
;(globalThis as Record<string, unknown>).MACRO = { VERSION: '0.0.0' }
installCommonMocks({ gitEmail: 'git@example.com' })
const { initUser, getCoreUserData } = await importFreshUserModule()
await initUser()
const result = getCoreUserData()
expect(result.email).toBe('git@example.com')
})
})