diff --git a/CLAUDE.md b/CLAUDE.md index 67cddcb..c5d4e98 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ npm run build # compile TypeScript to dist/ npm run dev # watch mode (rebuild on changes) npm run type-check # typecheck without emitting npm run test # run all tests -npx vitest run src/__tests__/output.test.ts # run a single test file +npx vitest run src/lib/output.test.ts # run a single test file npm run format # biome lint + format (auto-fix) ``` @@ -40,7 +40,7 @@ Each file in `src/commands/` exports a `registerXxxCommand(program)` function th ## Testing -Vitest with module mocking. Tests live in `src/__tests__/`. Common patterns: +Vitest with module mocking. Tests are colocated next to the source they cover (`foo.ts` → `foo.test.ts`); shared test fixtures live in `src/_fixtures/`. Common patterns: - Mock `apiRequest` with `vi.mock()` - Stub `fetch` globally for API tests diff --git a/src/__tests__/_fixtures/auth.ts b/src/_fixtures/auth.ts similarity index 96% rename from src/__tests__/_fixtures/auth.ts rename to src/_fixtures/auth.ts index dc868ce..526758b 100644 --- a/src/__tests__/_fixtures/auth.ts +++ b/src/_fixtures/auth.ts @@ -1,5 +1,5 @@ import type { MigrateAuthResult } from '@doist/cli-core/auth' -import type { OutlineAccount } from '../../lib/outline-account.js' +import type { OutlineAccount } from '../lib/outline-account.js' /** Canonical persisted `OutlineAccount` used across auth tests. */ export const STORED_ACCOUNT: OutlineAccount = { diff --git a/src/__tests__/auth-command.test.ts b/src/commands/auth-command.test.ts similarity index 96% rename from src/__tests__/auth-command.test.ts rename to src/commands/auth-command.test.ts index 35b17d7..94d7bd6 100644 --- a/src/__tests__/auth-command.test.ts +++ b/src/commands/auth-command.test.ts @@ -1,6 +1,6 @@ import { Command } from 'commander' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { AUTH_INFO } from './_fixtures/auth.js' +import { AUTH_INFO } from '../_fixtures/auth.js' vi.mock('../lib/auth.js', () => ({ getApiToken: async () => 'test-token', @@ -48,7 +48,7 @@ async function captureAttachOptions() { const { attachLoginCommand } = await import('@doist/cli-core/auth') const login = new Command('login') vi.mocked(attachLoginCommand).mockReturnValue(login) - const { registerAuthCommand } = await import('../commands/auth.js') + const { registerAuthCommand } = await import('./auth.js') const program = new Command() program.exitOverride() registerAuthCommand(program) @@ -245,7 +245,7 @@ describe('logTokenStorageResult', () => { it('prints the secure-store confirmation to stdout in human mode', async () => { const { logs, errs } = captureStreams() - const { logTokenStorageResult } = await import('../commands/auth.js') + const { logTokenStorageResult } = await import('./auth.js') logTokenStorageResult({ storage: 'secure-store' }, 'Token stored securely', false) @@ -255,7 +255,7 @@ describe('logTokenStorageResult', () => { it('suppresses the stdout confirmation in machine-output mode', async () => { const { logs } = captureStreams() - const { logTokenStorageResult } = await import('../commands/auth.js') + const { logTokenStorageResult } = await import('./auth.js') logTokenStorageResult({ storage: 'secure-store' }, 'Token stored securely', true) @@ -264,7 +264,7 @@ describe('logTokenStorageResult', () => { it('routes the keyring-fallback warning to stderr (in both human and machine modes)', async () => { const { logs, errs } = captureStreams() - const { logTokenStorageResult } = await import('../commands/auth.js') + const { logTokenStorageResult } = await import('./auth.js') logTokenStorageResult( { storage: 'config-file', warning: 'system credential manager unavailable' }, diff --git a/src/__tests__/changelog-integration.test.ts b/src/commands/changelog-integration.test.ts similarity index 96% rename from src/__tests__/changelog-integration.test.ts rename to src/commands/changelog-integration.test.ts index c8697e2..3df8d19 100644 --- a/src/__tests__/changelog-integration.test.ts +++ b/src/commands/changelog-integration.test.ts @@ -1,8 +1,8 @@ import { Command } from 'commander' import { describe, expect, it } from 'vitest' -import { registerChangelogCommand } from '../commands/changelog.js' import { BaseCliError } from '../lib/errors.js' import { formatError, formatErrorJson } from '../lib/output.js' +import { registerChangelogCommand } from './changelog.js' describe('changelog command end-to-end', () => { it('rejects with BaseCliError(INVALID_TYPE) when --count is not a number', async () => { diff --git a/src/__tests__/changelog.test.ts b/src/commands/changelog.test.ts similarity index 95% rename from src/__tests__/changelog.test.ts rename to src/commands/changelog.test.ts index 24f0b03..7e406c2 100644 --- a/src/__tests__/changelog.test.ts +++ b/src/commands/changelog.test.ts @@ -11,7 +11,7 @@ vi.mock('@doist/cli-core/commands', () => ({ })) import packageJson from '../../package.json' with { type: 'json' } -import { registerChangelogCommand } from '../commands/changelog.js' +import { registerChangelogCommand } from './changelog.js' describe('changelog wrapper', () => { beforeEach(() => { diff --git a/src/__tests__/commands.test.ts b/src/commands/commands.test.ts similarity index 93% rename from src/__tests__/commands.test.ts rename to src/commands/commands.test.ts index a51d8ff..fe872e4 100644 --- a/src/__tests__/commands.test.ts +++ b/src/commands/commands.test.ts @@ -45,7 +45,7 @@ describe('search command', () => { pagination: { offset: 0, limit: 25 }, }) - const { registerSearchCommand } = await import('../commands/search.js') + const { registerSearchCommand } = await import('./search.js') const program = new Command() program.exitOverride() registerSearchCommand(program) @@ -75,7 +75,7 @@ describe('search command', () => { ], }) - const { registerSearchCommand } = await import('../commands/search.js') + const { registerSearchCommand } = await import('./search.js') const program = new Command() program.exitOverride() registerSearchCommand(program) @@ -114,7 +114,7 @@ describe('document commands', () => { }, }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -133,7 +133,7 @@ describe('document commands', () => { pagination: { offset: 0, limit: 10 }, }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -186,7 +186,7 @@ describe('document commands', () => { return Promise.reject(new Error(`Unexpected endpoint: ${endpoint}`)) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -218,7 +218,7 @@ describe('document commands', () => { errors.push(args.join(' ')) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -263,7 +263,7 @@ describe('document commands', () => { return Promise.reject(new Error(`Unexpected endpoint: ${endpoint}`)) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -294,7 +294,7 @@ describe('document commands', () => { errors.push(args.join(' ')) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -326,7 +326,7 @@ describe('document commands', () => { errors.push(args.join(' ')) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -366,7 +366,7 @@ describe('document commands', () => { errors.push(args.join(' ')) }) - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) @@ -408,7 +408,7 @@ describe('collection commands', () => { data: [{ id: 'c1', name: 'Engineering', documentCount: 42 }], }) - const { registerCollectionCommand } = await import('../commands/collection.js') + const { registerCollectionCommand } = await import('./collection.js') const program = new Command() program.exitOverride() registerCollectionCommand(program) diff --git a/src/__tests__/empty-output.test.ts b/src/commands/empty-output.test.ts similarity index 91% rename from src/__tests__/empty-output.test.ts rename to src/commands/empty-output.test.ts index 614fd55..78b8cd6 100644 --- a/src/__tests__/empty-output.test.ts +++ b/src/commands/empty-output.test.ts @@ -17,7 +17,7 @@ vi.mock('../lib/api.js', () => ({ describeEmptyMachineOutput('ol document list', { setup: () => {}, run: async (extraArgs) => { - const { registerDocumentCommand } = await import('../commands/document.js') + const { registerDocumentCommand } = await import('./document.js') const program = new Command() program.exitOverride() registerDocumentCommand(program) diff --git a/src/__tests__/update-surface.test.ts b/src/commands/update/update-surface.test.ts similarity index 91% rename from src/__tests__/update-surface.test.ts rename to src/commands/update/update-surface.test.ts index b91abe2..a53d1d7 100644 --- a/src/__tests__/update-surface.test.ts +++ b/src/commands/update/update-surface.test.ts @@ -1,15 +1,15 @@ import { Command } from 'commander' import { describe, expect, it, vi } from 'vitest' -import { registerUpdateCommand } from '../commands/update/index.js' +import { registerUpdateCommand } from './index.js' // Stub out config + spinner — this test only cares about the command surface // (subcommand names + flags) wired up via the real cli-core, so a bump to // cli-core can't silently change `ol update`'s public CLI shape. -vi.mock('../lib/config.js', () => ({ +vi.mock('../../lib/config.js', () => ({ getConfigPath: () => '/tmp/outline-cli-test/config.json', })) -vi.mock('../lib/spinner.js', () => ({ +vi.mock('../../lib/spinner.js', () => ({ withSpinner: vi.fn((_opts: unknown, fn: () => Promise) => fn()), })) diff --git a/src/__tests__/update.test.ts b/src/commands/update/update.test.ts similarity index 85% rename from src/__tests__/update.test.ts rename to src/commands/update/update.test.ts index 3ee2c0a..58cb6aa 100644 --- a/src/__tests__/update.test.ts +++ b/src/commands/update/update.test.ts @@ -1,6 +1,6 @@ import { Command } from 'commander' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import packageJson from '../../package.json' with { type: 'json' } +import packageJson from '../../../package.json' with { type: 'json' } const registerCoreUpdateCommandMock = vi.fn() const withSpinnerMock = vi.fn((_opts: unknown, fn: () => Promise) => fn()) @@ -9,11 +9,11 @@ vi.mock('@doist/cli-core/commands', () => ({ registerUpdateCommand: registerCoreUpdateCommandMock, })) -vi.mock('../lib/config.js', () => ({ +vi.mock('../../lib/config.js', () => ({ getConfigPath: () => '/tmp/outline-cli-test/config.json', })) -vi.mock('../lib/spinner.js', () => ({ +vi.mock('../../lib/spinner.js', () => ({ withSpinner: withSpinnerMock, })) @@ -27,7 +27,7 @@ describe('ol update wiring', () => { }) it('forwards packageName, currentVersion, configPath, changelogCommandName, withSpinner', async () => { - const { registerUpdateCommand } = await import('../commands/update/index.js') + const { registerUpdateCommand } = await import('./index.js') const program = new Command() registerUpdateCommand(program) diff --git a/src/__tests__/api.test.ts b/src/lib/api.test.ts similarity index 92% rename from src/__tests__/api.test.ts rename to src/lib/api.test.ts index 5dcd8a5..dec9e81 100644 --- a/src/__tests__/api.test.ts +++ b/src/lib/api.test.ts @@ -7,7 +7,7 @@ const authMocks = vi.hoisted(() => ({ reactiveRefresh: vi.fn(async () => false), })) -vi.mock('../lib/auth.js', () => authMocks) +vi.mock('./auth.js', () => authMocks) vi.mock('../transport/fetch-with-retry.js', () => ({ fetchWithRetry: vi.fn(), @@ -32,7 +32,7 @@ describe('apiRequest', () => { const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') ;(fetchWithRetry as ReturnType).mockResolvedValue(mockResponse) - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') await apiRequest('documents.info', { id: 'abc' }) expect(fetchWithRetry).toHaveBeenCalledWith({ @@ -56,7 +56,7 @@ describe('apiRequest', () => { const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') ;(fetchWithRetry as ReturnType).mockResolvedValue(mockResponse) - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') const result = await apiRequest('documents.list') expect(result.data).toEqual([{ id: '1' }]) @@ -73,7 +73,7 @@ describe('apiRequest', () => { const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') ;(fetchWithRetry as ReturnType).mockResolvedValue(mockResponse) - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') await expect(apiRequest('documents.list')).rejects.toThrow('Server exploded') }) @@ -89,7 +89,7 @@ describe('apiRequest', () => { const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') ;(fetchWithRetry as ReturnType).mockResolvedValue(mockResponse) - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') await expect(apiRequest('documents.list')).rejects.toThrow( 'API error: 500 Internal Server Error', ) @@ -110,7 +110,7 @@ describe('apiRequest', () => { .mockResolvedValueOnce('stale-token') .mockResolvedValueOnce('rotated-token') - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') const result = await apiRequest('documents.info', { id: 'abc' }) expect(result.data).toEqual({ id: 'ok' }) @@ -126,7 +126,7 @@ describe('apiRequest', () => { f.mockResolvedValue({ ok: true, json: async () => ({ data: {} }) }) authMocks.proactiveRefresh.mockResolvedValueOnce('rotated-proactive') - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') await apiRequest('documents.list') expect(authMocks.proactiveRefresh).toHaveBeenCalledTimes(1) @@ -144,7 +144,7 @@ describe('apiRequest', () => { json: async () => ({ data: {} }), }) - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') await apiRequest('documents.list') expect(authMocks.proactiveRefresh).not.toHaveBeenCalled() diff --git a/src/__tests__/auth-pages.test.ts b/src/lib/auth-pages.test.ts similarity index 92% rename from src/__tests__/auth-pages.test.ts rename to src/lib/auth-pages.test.ts index b517d32..097adf5 100644 --- a/src/__tests__/auth-pages.test.ts +++ b/src/lib/auth-pages.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { renderError, renderSuccess } from '../lib/auth-pages.js' +import { renderError, renderSuccess } from './auth-pages.js' describe('auth pages', () => { it('renderSuccess returns the branded post-login page', () => { diff --git a/src/__tests__/auth-provider.test.ts b/src/lib/auth-provider.test.ts similarity index 95% rename from src/__tests__/auth-provider.test.ts rename to src/lib/auth-provider.test.ts index 863992c..b06b7e1 100644 --- a/src/__tests__/auth-provider.test.ts +++ b/src/lib/auth-provider.test.ts @@ -7,16 +7,16 @@ import { okResponse, SKIPPED_RESULT, STORED_ACCOUNT, -} from './_fixtures/auth.js' +} from '../_fixtures/auth.js' vi.mock('../transport/fetch-with-retry.js', () => ({ fetchWithRetry: vi.fn() })) -vi.mock('../lib/api.js', () => ({ apiRequest: vi.fn() })) +vi.mock('./api.js', () => ({ apiRequest: vi.fn() })) const migrateMocks = vi.hoisted(() => ({ runMigrateLegacyAuth: vi.fn(), })) -vi.mock('../lib/migrate-auth.js', () => migrateMocks) +vi.mock('./migrate-auth.js', () => migrateMocks) const keyringMocks = vi.hoisted(() => ({ createKeyringTokenStore: vi.fn(), @@ -47,8 +47,8 @@ const configMocks = vi.hoisted(() => ({ updateConfig: vi.fn(), })) -vi.mock('../lib/config.js', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('./config.js', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, getConfigPath: () => '/home/user/.config/outline-cli/config.json', @@ -61,10 +61,10 @@ const TOKEN_ENV_VAR = 'OUTLINE_API_TOKEN' /** Reset the module-level migration memo for each test by re-importing. */ async function loadCreateOutlineTokenStore(): Promise< - typeof import('../lib/auth-provider.js').createOutlineTokenStore + typeof import('./auth-provider.js').createOutlineTokenStore > { vi.resetModules() - const mod = await import('../lib/auth-provider.js') + const mod = await import('./auth-provider.js') return mod.createOutlineTokenStore } @@ -77,7 +77,7 @@ describe('createOutlineAuthProvider', () => { }) it('authorize builds an outline /oauth/authorize URL with PKCE params from flags', async () => { - const { createOutlineAuthProvider } = await import('../lib/auth-provider.js') + const { createOutlineAuthProvider } = await import('./auth-provider.js') const result = await createOutlineAuthProvider().authorize({ redirectUri: 'http://localhost:54969/callback', state: 'state-123', @@ -109,7 +109,7 @@ describe('createOutlineAuthProvider', () => { const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') vi.mocked(fetchWithRetry).mockResolvedValueOnce(okResponse({ access_token: 'tok-abc' })) - const { createOutlineAuthProvider } = await import('../lib/auth-provider.js') + const { createOutlineAuthProvider } = await import('./auth-provider.js') const provider = createOutlineAuthProvider() const handshake = { baseUrl: 'https://wiki.example.com', @@ -150,10 +150,10 @@ describe('createOutlineAuthProvider', () => { }) it('validateToken builds an OutlineAccount using the base URL captured at authorize', async () => { - const { apiRequest } = await import('../lib/api.js') + const { apiRequest } = await import('./api.js') vi.mocked(apiRequest).mockResolvedValue({ data: AUTH_INFO }) - const { createOutlineAuthProvider } = await import('../lib/auth-provider.js') + const { createOutlineAuthProvider } = await import('./auth-provider.js') const provider = createOutlineAuthProvider() // authorize seeds the provider's base-URL closure (from the flag), // which validate then reuses instead of a handshake field. @@ -210,7 +210,7 @@ describe('createOutlineAuthProvider', () => { }), ) - const { createOutlineAuthProvider } = await import('../lib/auth-provider.js') + const { createOutlineAuthProvider } = await import('./auth-provider.js') const result = await createOutlineAuthProvider().refreshToken?.({ refreshToken: 'r-old', handshake: {}, @@ -262,7 +262,7 @@ describe('createOutlineTokenStore', () => { const options = keyringMocks.createKeyringTokenStore.mock.calls[0][0] expect(options.serviceName).toBe('outline-cli') expect(options.recordsLocation).toBe('/home/user/.config/outline-cli/config.json') - const { matchOutlineAccount } = await import('../lib/auth-provider.js') + const { matchOutlineAccount } = await import('./auth-provider.js') expect(options.matchAccount).toBe(matchOutlineAccount) }) @@ -497,7 +497,7 @@ describe('createOutlineTokenStore', () => { describe('matchOutlineAccount', () => { it('matches the UUID exactly and the label case-insensitively', async () => { - const { matchOutlineAccount } = await import('../lib/auth-provider.js') + const { matchOutlineAccount } = await import('./auth-provider.js') expect(matchOutlineAccount(STORED_ACCOUNT, 'user-uuid')).toBe(true) expect(matchOutlineAccount(STORED_ACCOUNT, 'ADA')).toBe(true) expect(matchOutlineAccount(STORED_ACCOUNT, 'ada')).toBe(true) @@ -518,7 +518,7 @@ describe('getActiveTokenSource', () => { }) it('reports the storage location of the active token, mirroring active()s resolution order', async () => { - const { getActiveTokenSource } = await import('../lib/auth-provider.js') + const { getActiveTokenSource } = await import('./auth-provider.js') vi.stubEnv(TOKEN_ENV_VAR, 'tk') configMocks.getConfig.mockResolvedValue({}) diff --git a/src/__tests__/auth.test.ts b/src/lib/auth.test.ts similarity index 86% rename from src/__tests__/auth.test.ts rename to src/lib/auth.test.ts index 43f04a2..afb0777 100644 --- a/src/__tests__/auth.test.ts +++ b/src/lib/auth.test.ts @@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { SKIPPED_RESULT } from './_fixtures/auth.js' +import { SKIPPED_RESULT } from '../_fixtures/auth.js' const TEST_XDG = join(tmpdir(), `outline-cli-test-${process.pid}-auth`) const TEST_CONFIG_DIR = join(TEST_XDG, 'outline-cli') @@ -13,7 +13,7 @@ const TEST_CONFIG_PATH = join(TEST_CONFIG_DIR, 'config.json') // behind it. Mocking `runMigrateLegacyAuth` directly is more robust than // stubbing transitive network deps: tests don't have to know how // migration internally decides to skip. -vi.mock('../lib/migrate-auth.js', () => ({ +vi.mock('./migrate-auth.js', () => ({ runMigrateLegacyAuth: vi.fn(async () => SKIPPED_RESULT), })) @@ -36,35 +36,35 @@ describe('auth', () => { it('getApiToken reads from env var first', async () => { process.env.OUTLINE_API_TOKEN = 'env-token' - const { getApiToken } = await import('../lib/auth.js') + const { getApiToken } = await import('./auth.js') await expect(getApiToken()).resolves.toBe('env-token') }) it('getApiToken falls back to the legacy plaintext config token', async () => { writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ api_token: 'file-token' })) - const { getApiToken } = await import('../lib/auth.js') + const { getApiToken } = await import('./auth.js') await expect(getApiToken()).resolves.toBe('file-token') }) it('getApiToken throws when no token available', async () => { - const { getApiToken } = await import('../lib/auth.js') + const { getApiToken } = await import('./auth.js') await expect(getApiToken()).rejects.toThrow('No API token found') }) it('getBaseUrl returns env var first', async () => { process.env.OUTLINE_URL = 'https://custom.example.com' - const { getBaseUrl } = await import('../lib/auth.js') + const { getBaseUrl } = await import('./auth.js') await expect(getBaseUrl()).resolves.toBe('https://custom.example.com') }) it('getBaseUrl strips trailing slash', async () => { process.env.OUTLINE_URL = 'https://custom.example.com/' - const { getBaseUrl } = await import('../lib/auth.js') + const { getBaseUrl } = await import('./auth.js') await expect(getBaseUrl()).resolves.toBe('https://custom.example.com') }) it('getBaseUrl returns default when nothing configured', async () => { - const { getBaseUrl } = await import('../lib/auth.js') + const { getBaseUrl } = await import('./auth.js') await expect(getBaseUrl()).resolves.toBe('https://app.getoutline.com') }) @@ -83,25 +83,25 @@ describe('auth', () => { ], }), ) - const { getBaseUrl } = await import('../lib/auth.js') + const { getBaseUrl } = await import('./auth.js') await expect(getBaseUrl()).resolves.toBe('https://wiki.example.com') }) it('getBaseUrl reads from the legacy v1 base_url slot', async () => { writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ base_url: 'https://legacy.example.com' })) - const { getBaseUrl } = await import('../lib/auth.js') + const { getBaseUrl } = await import('./auth.js') await expect(getBaseUrl()).resolves.toBe('https://legacy.example.com') }) it('getOAuthClientId returns env var first', async () => { process.env.OUTLINE_OAUTH_CLIENT_ID = 'env-client-id' - const { getOAuthClientId } = await import('../lib/auth.js') + const { getOAuthClientId } = await import('./auth.js') await expect(getOAuthClientId()).resolves.toBe('env-client-id') }) it('getOAuthClientId reads from the legacy v1 config slot', async () => { writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ oauth_client_id: 'file-client-id' })) - const { getOAuthClientId } = await import('../lib/auth.js') + const { getOAuthClientId } = await import('./auth.js') await expect(getOAuthClientId()).resolves.toBe('file-client-id') }) @@ -120,7 +120,7 @@ describe('auth', () => { ], }), ) - const { getOAuthClientId } = await import('../lib/auth.js') + const { getOAuthClientId } = await import('./auth.js') await expect(getOAuthClientId()).resolves.toBe('cid-record') }) @@ -144,7 +144,7 @@ describe('auth', () => { default_user_id: 'u', }), ) - const { reactiveRefresh } = await import('../lib/auth.js') + const { reactiveRefresh } = await import('./auth.js') let caught: unknown try { diff --git a/src/__tests__/migrate-auth.test.ts b/src/lib/migrate-auth.test.ts similarity index 92% rename from src/__tests__/migrate-auth.test.ts rename to src/lib/migrate-auth.test.ts index 39e525d..5bb735e 100644 --- a/src/__tests__/migrate-auth.test.ts +++ b/src/lib/migrate-auth.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { errResponse, okResponse } from './_fixtures/auth.js' +import { errResponse, okResponse } from '../_fixtures/auth.js' const fetchMock = vi.hoisted(() => ({ fetchWithRetry: vi.fn() })) vi.mock('../transport/fetch-with-retry.js', () => fetchMock) @@ -14,15 +14,15 @@ const configMock = vi.hoisted(() => ({ getConfig: vi.fn(), updateConfig: vi.fn(), })) -vi.mock('../lib/config.js', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('./config.js', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, getConfig: configMock.getConfig, updateConfig: configMock.updateConfig } }) /** Run the wrapper once, return the options handed to cli-core. */ async function captureCliCoreOptions() { cliCoreMock.migrateLegacyAuth.mockResolvedValue({ status: 'no-legacy-state' }) - const { runMigrateLegacyAuth } = await import('../lib/migrate-auth.js') + const { runMigrateLegacyAuth } = await import('./migrate-auth.js') await runMigrateLegacyAuth() return cliCoreMock.migrateLegacyAuth.mock.calls[0][0] } diff --git a/src/__tests__/output.test.ts b/src/lib/output.test.ts similarity index 96% rename from src/__tests__/output.test.ts rename to src/lib/output.test.ts index 5c36935..d3bde7b 100644 --- a/src/__tests__/output.test.ts +++ b/src/lib/output.test.ts @@ -1,12 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { BaseCliError } from '../lib/errors.js' -import { - formatError, - formatErrorJson, - getOutputOptions, - outputItem, - outputList, -} from '../lib/output.js' +import { BaseCliError } from './errors.js' +import { formatError, formatErrorJson, getOutputOptions, outputItem, outputList } from './output.js' describe('output', () => { let logs: string[] diff --git a/src/__tests__/refs.test.ts b/src/lib/refs.test.ts similarity index 89% rename from src/__tests__/refs.test.ts rename to src/lib/refs.test.ts index d71309b..b123ab5 100644 --- a/src/__tests__/refs.test.ts +++ b/src/lib/refs.test.ts @@ -1,12 +1,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -vi.mock('../lib/auth.js', () => ({ +vi.mock('./auth.js', () => ({ getApiToken: async () => 'test-token', getBaseUrl: async () => 'https://test.outline.com', })) const mockApiRequest = vi.fn() -vi.mock('../lib/api.js', () => ({ +vi.mock('./api.js', () => ({ apiRequest: (...args: unknown[]) => mockApiRequest(...args), })) @@ -28,7 +28,7 @@ describe('resolveDocumentRef', () => { } mockApiRequest.mockResolvedValueOnce({ data: mockDoc }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') const result = await resolveDocumentRef('550e8400-e29b-41d4-a716-446655440000') expect(result).toEqual(mockDoc) @@ -41,7 +41,7 @@ describe('resolveDocumentRef', () => { const mockDoc = { id: 'abc123', title: 'Test Doc', urlId: 'test-doc-abc' } mockApiRequest.mockResolvedValueOnce({ data: mockDoc }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') const result = await resolveDocumentRef('abc123') expect(result).toEqual(mockDoc) @@ -58,7 +58,7 @@ describe('resolveDocumentRef', () => { } mockApiRequest.mockResolvedValueOnce({ data: mockDoc }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') const result = await resolveDocumentRef('my-document-xyz789') expect(result).toEqual(mockDoc) @@ -75,7 +75,7 @@ describe('resolveDocumentRef', () => { // "engineering guide" has spaces, doesn't look like an ID mockApiRequest.mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') const result = await resolveDocumentRef('engineering guide') expect(result).toEqual(mockDocs[0]) @@ -91,7 +91,7 @@ describe('resolveDocumentRef', () => { .mockRejectedValueOnce(new Error('Not found')) // ID lookup fails .mockResolvedValueOnce({ data: mockDocs }) // falls back to list - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') const result = await resolveDocumentRef('product') expect(result).toEqual(mockDocs[1]) @@ -107,7 +107,7 @@ describe('resolveDocumentRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') await expect(resolveDocumentRef('engineering')).rejects.toThrow( /Ambiguous Document reference/, ) @@ -122,7 +122,7 @@ describe('resolveDocumentRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') try { await resolveDocumentRef('engineering') } catch (error) { @@ -143,7 +143,7 @@ describe('resolveDocumentRef', () => { // "meeting notes" has spaces, doesn't look like an ID mockApiRequest.mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') await expect(resolveDocumentRef('meeting notes')).rejects.toThrow( /Ambiguous Document reference.*Multiple items have this exact name/, ) @@ -156,7 +156,7 @@ describe('resolveDocumentRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') await expect(resolveDocumentRef('nonexistent')).rejects.toThrow( 'Document not found: "nonexistent"', ) @@ -172,7 +172,7 @@ describe('resolveDocumentRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockDocs }) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') // Should fall back to name search and throw "not found" await expect(resolveDocumentRef('abc123')).rejects.toThrow('Document not found: "abc123"') @@ -203,7 +203,7 @@ describe('resolveDocumentRef', () => { .mockResolvedValueOnce({ data: page1 }) // first page (100 items) .mockResolvedValueOnce({ data: page2 }) // second page (50 items) - const { resolveDocumentRef } = await import('../lib/refs.js') + const { resolveDocumentRef } = await import('./refs.js') // Search for a doc that's on page 2 const result = await resolveDocumentRef('Doc 125') @@ -234,7 +234,7 @@ describe('resolveCollectionRef', () => { const mockCol = { id: 'col123', name: 'Engineering' } mockApiRequest.mockResolvedValueOnce({ data: mockCol }) - const { resolveCollectionRef } = await import('../lib/refs.js') + const { resolveCollectionRef } = await import('./refs.js') const result = await resolveCollectionRef('col123') expect(result).toEqual(mockCol) @@ -253,7 +253,7 @@ describe('resolveCollectionRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockCols }) - const { resolveCollectionRef } = await import('../lib/refs.js') + const { resolveCollectionRef } = await import('./refs.js') const result = await resolveCollectionRef('Engineering') expect(result).toEqual(mockCols[0]) @@ -267,7 +267,7 @@ describe('resolveCollectionRef', () => { // "prod" is 4 chars, doesn't look like an ID (needs 6+) mockApiRequest.mockResolvedValueOnce({ data: mockCols }) - const { resolveCollectionRef } = await import('../lib/refs.js') + const { resolveCollectionRef } = await import('./refs.js') const result = await resolveCollectionRef('prod') expect(result).toEqual(mockCols[1]) @@ -283,7 +283,7 @@ describe('resolveCollectionRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockCols }) - const { resolveCollectionRef } = await import('../lib/refs.js') + const { resolveCollectionRef } = await import('./refs.js') await expect(resolveCollectionRef('engineering')).rejects.toThrow( /Ambiguous Collection reference/, ) @@ -296,7 +296,7 @@ describe('resolveCollectionRef', () => { .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce({ data: mockCols }) - const { resolveCollectionRef } = await import('../lib/refs.js') + const { resolveCollectionRef } = await import('./refs.js') await expect(resolveCollectionRef('nonexistent')).rejects.toThrow( 'Collection not found: "nonexistent"', ) @@ -317,7 +317,7 @@ describe('resolveDocumentId', () => { const mockDoc = { id: 'doc-id-123', title: 'Test', urlId: 'test-abc' } mockApiRequest.mockResolvedValueOnce({ data: mockDoc }) - const { resolveDocumentId } = await import('../lib/refs.js') + const { resolveDocumentId } = await import('./refs.js') const result = await resolveDocumentId('testabc') expect(result).toBe('doc-id-123') @@ -338,7 +338,7 @@ describe('resolveCollectionId', () => { const mockCol = { id: 'col-id-123', name: 'Engineering' } mockApiRequest.mockResolvedValueOnce({ data: mockCol }) - const { resolveCollectionId } = await import('../lib/refs.js') + const { resolveCollectionId } = await import('./refs.js') const result = await resolveCollectionId('colid123') expect(result).toBe('col-id-123') diff --git a/src/__tests__/skill.test.ts b/src/lib/skills/skill.test.ts similarity index 95% rename from src/__tests__/skill.test.ts rename to src/lib/skills/skill.test.ts index abc1c4d..8c881c0 100644 --- a/src/__tests__/skill.test.ts +++ b/src/lib/skills/skill.test.ts @@ -2,8 +2,8 @@ import { mkdir, rm } from 'node:fs/promises' import { homedir, tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import { createInstaller } from '../lib/skills/create-installer.js' -import { getInstaller, listAgents, skillInstallers } from '../lib/skills/index.js' +import { createInstaller } from './create-installer.js' +import { getInstaller, listAgents, skillInstallers } from './index.js' const AGENTS = [ { diff --git a/src/__tests__/spinner.test.ts b/src/lib/spinner.test.ts similarity index 98% rename from src/__tests__/spinner.test.ts rename to src/lib/spinner.test.ts index 889db0a..ac9b08b 100644 --- a/src/__tests__/spinner.test.ts +++ b/src/lib/spinner.test.ts @@ -39,7 +39,7 @@ describe('spinner wiring', () => { async function loadIsDisabled(): Promise<() => boolean> { vi.resetModules() createSpinnerMock.mockClear() - await import('../lib/spinner.js') + await import('./spinner.js') expect(createSpinnerMock).toHaveBeenCalledWith( expect.objectContaining({ isDisabled: expect.any(Function) }), ) diff --git a/src/__tests__/user-records.test.ts b/src/lib/user-records.test.ts similarity index 92% rename from src/__tests__/user-records.test.ts rename to src/lib/user-records.test.ts index 01a61c8..d61ea37 100644 --- a/src/__tests__/user-records.test.ts +++ b/src/lib/user-records.test.ts @@ -1,13 +1,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { STORED_ACCOUNT } from './_fixtures/auth.js' +import { STORED_ACCOUNT } from '../_fixtures/auth.js' const configMock = vi.hoisted(() => ({ getConfig: vi.fn(), updateConfig: vi.fn(), })) -vi.mock('../lib/config.js', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('./config.js', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, getConfig: configMock.getConfig, updateConfig: configMock.updateConfig } }) @@ -35,7 +35,7 @@ describe('createOutlineUserRecordStore', () => { configMock.getConfig.mockResolvedValue({ users: [ADA, { ...GRACE, token: ' plaintext-token ' }], }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') const records = await createOutlineUserRecordStore().list() @@ -48,7 +48,7 @@ describe('createOutlineUserRecordStore', () => { it('upsert() appends a brand-new record', async () => { configMock.getConfig.mockResolvedValue({ users: [ADA] }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().upsert({ account: { @@ -75,7 +75,7 @@ describe('createOutlineUserRecordStore', () => { configMock.getConfig.mockResolvedValue({ users: [{ ...ADA, token: 'stale-plaintext' }, GRACE], }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().upsert({ account: STORED_ACCOUNT }) @@ -86,7 +86,7 @@ describe('createOutlineUserRecordStore', () => { it('upsert() persists fallbackToken back to the StoredUser.token slot', async () => { configMock.getConfig.mockResolvedValue({ users: [] }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().upsert({ account: STORED_ACCOUNT, @@ -103,7 +103,7 @@ describe('createOutlineUserRecordStore', () => { // expiry), but the refresh token is a long-lived secret and must stay // in the secure store only — `fallbackRefreshToken` is dropped, never // written to config, even when cli-core supplies it (keyring offline). - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().upsert({ account: STORED_ACCOUNT, @@ -137,7 +137,7 @@ describe('createOutlineUserRecordStore', () => { it('remove() drops the matching record', async () => { configMock.getConfig.mockResolvedValue({ users: [ADA, GRACE] }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().remove('user-uuid') @@ -149,7 +149,7 @@ describe('createOutlineUserRecordStore', () => { users: [ADA, GRACE], default_user_id: 'user-uuid', }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().remove('user-uuid') @@ -161,7 +161,7 @@ describe('createOutlineUserRecordStore', () => { it('remove() is a no-op when the id is unknown (does not touch config)', async () => { configMock.getConfig.mockResolvedValue({ users: [ADA] }) - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') await createOutlineUserRecordStore().remove('nobody') @@ -169,7 +169,7 @@ describe('createOutlineUserRecordStore', () => { }) it('getDefaultId / setDefaultId round-trip via the default_user_id config field', async () => { - const { createOutlineUserRecordStore } = await import('../lib/user-records.js') + const { createOutlineUserRecordStore } = await import('./user-records.js') const store = createOutlineUserRecordStore() configMock.getConfig.mockResolvedValueOnce({}) @@ -188,13 +188,13 @@ describe('createOutlineUserRecordStore', () => { describe('getDefaultUserRecord', () => { it('returns null when no users are stored', async () => { - const { getDefaultUserRecord } = await import('../lib/user-records.js') + const { getDefaultUserRecord } = await import('./user-records.js') expect(getDefaultUserRecord({})).toBeNull() expect(getDefaultUserRecord({ users: [] })).toBeNull() }) it('returns the pinned default when default_user_id matches', async () => { - const { getDefaultUserRecord } = await import('../lib/user-records.js') + const { getDefaultUserRecord } = await import('./user-records.js') const record = getDefaultUserRecord({ users: [ADA, GRACE], default_user_id: 'grace-uuid', @@ -203,7 +203,7 @@ describe('getDefaultUserRecord', () => { }) it('falls back to the first record when default_user_id is absent or stale', async () => { - const { getDefaultUserRecord } = await import('../lib/user-records.js') + const { getDefaultUserRecord } = await import('./user-records.js') const noDefault = getDefaultUserRecord({ users: [ADA, GRACE] }) expect(noDefault?.account.id).toBe('user-uuid') diff --git a/src/__tests__/fetch-with-retry.test.ts b/src/transport/fetch-with-retry.test.ts similarity index 91% rename from src/__tests__/fetch-with-retry.test.ts rename to src/transport/fetch-with-retry.test.ts index c82ce0e..83f24f2 100644 --- a/src/__tests__/fetch-with-retry.test.ts +++ b/src/transport/fetch-with-retry.test.ts @@ -36,7 +36,7 @@ describe('fetchWithRetry', () => { }) afterEach(async () => { - const { resetDefaultDispatcherForTests } = await import('../transport/http-dispatcher.js') + const { resetDefaultDispatcherForTests } = await import('./http-dispatcher.js') await resetDefaultDispatcherForTests() restoreProxyEnv() vi.useRealTimers() @@ -55,8 +55,8 @@ describe('fetchWithRetry', () => { ) vi.stubGlobal('fetch', fetchMock) - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') - const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') + const { fetchWithRetry } = await import('./fetch-with-retry.js') const response = await fetchWithRetry({ url: 'https://test.outline.com/api/documents.info', options: { method: 'POST' }, @@ -83,7 +83,7 @@ describe('fetchWithRetry', () => { ) vi.stubGlobal('fetch', fetchMock) - const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') + const { fetchWithRetry } = await import('./fetch-with-retry.js') const response = await fetchWithRetry({ url: 'https://test.outline.com/api/documents.info', retryConfig: { @@ -126,7 +126,7 @@ describe('fetchWithRetry', () => { ) vi.stubGlobal('fetch', fetchMock) - const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') + const { fetchWithRetry } = await import('./fetch-with-retry.js') const requestPromise = fetchWithRetry({ url: 'https://test.outline.com/api/documents.info', options: { @@ -169,8 +169,8 @@ describe('fetchWithRetry', () => { ) vi.stubGlobal('fetch', fetchMock) - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') - const { fetchWithRetry } = await import('../transport/fetch-with-retry.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') + const { fetchWithRetry } = await import('./fetch-with-retry.js') const requestPromise = fetchWithRetry({ url: 'https://test.outline.com/api/documents.info', diff --git a/src/__tests__/http-dispatcher.test.ts b/src/transport/http-dispatcher.test.ts similarity index 91% rename from src/__tests__/http-dispatcher.test.ts rename to src/transport/http-dispatcher.test.ts index a53c7b0..8274884 100644 --- a/src/__tests__/http-dispatcher.test.ts +++ b/src/transport/http-dispatcher.test.ts @@ -40,7 +40,7 @@ describe('http-dispatcher', () => { }) afterEach(async () => { - const { resetDefaultDispatcherForTests } = await import('../transport/http-dispatcher.js') + const { resetDefaultDispatcherForTests } = await import('./http-dispatcher.js') await resetDefaultDispatcherForTests() restoreProxyEnv() vi.restoreAllMocks() @@ -48,27 +48,27 @@ describe('http-dispatcher', () => { }) it('returns a direct Agent when no proxy env vars are set', async () => { - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') expect(getDefaultDispatcher()).toBeInstanceOf(Agent) }) it('returns an EnvHttpProxyAgent when proxy env vars are set', async () => { process.env.HTTPS_PROXY = 'http://proxy.local:8080' - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') expect(getDefaultDispatcher()).toBeInstanceOf(EnvHttpProxyAgent) }) it('caches the dispatcher instance', async () => { - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') expect(getDefaultDispatcher()).toBe(getDefaultDispatcher()) }) it('reset lets tests re-evaluate env-dependent transport selection', async () => { const { getDefaultDispatcher, resetDefaultDispatcherForTests } = - await import('../transport/http-dispatcher.js') + await import('./http-dispatcher.js') const directDispatcher = getDefaultDispatcher() process.env.HTTPS_PROXY = 'http://proxy.local:8080' @@ -98,7 +98,7 @@ describe('http-dispatcher', () => { try { const { port } = httpServer.address() as AddressInfo - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') const dispatcher = getDefaultDispatcher() const response = await fetch(`http://127.0.0.1:${port}/`, { // @ts-expect-error - dispatcher is a valid Node fetch option not in TS lib types @@ -117,7 +117,7 @@ describe('http-dispatcher', () => { describe('suppressExperimentalWarningsSync', () => { it('swallows ExperimentalWarning emissions during the synchronous call', async () => { - const { suppressExperimentalWarningsSync } = await import('../transport/http-dispatcher.js') + const { suppressExperimentalWarningsSync } = await import('./http-dispatcher.js') const calls: unknown[][] = [] const originalEmit = process.emitWarning @@ -142,7 +142,7 @@ describe('suppressExperimentalWarningsSync', () => { }) it('restores the original emitWarning even if the callback throws', async () => { - const { suppressExperimentalWarningsSync } = await import('../transport/http-dispatcher.js') + const { suppressExperimentalWarningsSync } = await import('./http-dispatcher.js') const originalEmit = process.emitWarning const placeholder = (() => {}) as typeof process.emitWarning @@ -161,14 +161,14 @@ describe('suppressExperimentalWarningsSync', () => { }) it('returns the callback result', async () => { - const { suppressExperimentalWarningsSync } = await import('../transport/http-dispatcher.js') + const { suppressExperimentalWarningsSync } = await import('./http-dispatcher.js') const result = suppressExperimentalWarningsSync(() => 42) expect(result).toBe(42) }) it('throws if the callback returns a thenable (sync-only contract)', async () => { - const { suppressExperimentalWarningsSync } = await import('../transport/http-dispatcher.js') + const { suppressExperimentalWarningsSync } = await import('./http-dispatcher.js') // Cast through `unknown` — the public type rejects async callbacks at // compile time; this exercises the runtime defence-in-depth. @@ -180,7 +180,7 @@ describe('suppressExperimentalWarningsSync', () => { describe('http-dispatcher integration with decompress interceptor', () => { afterEach(async () => { - const { resetDefaultDispatcherForTests } = await import('../transport/http-dispatcher.js') + const { resetDefaultDispatcherForTests } = await import('./http-dispatcher.js') await resetDefaultDispatcherForTests() vi.doUnmock('undici') vi.resetModules() @@ -209,7 +209,7 @@ describe('http-dispatcher integration with decompress interceptor', () => { } }) - const { getDefaultDispatcher } = await import('../transport/http-dispatcher.js') + const { getDefaultDispatcher } = await import('./http-dispatcher.js') const dispatcher = getDefaultDispatcher() expect(dispatcher).toBeDefined() diff --git a/tsconfig.json b/tsconfig.json index a5c7b8f..364ab62 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "sourceMap": true }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "src/__tests__"] + "exclude": ["node_modules", "dist", "src/**/*.test.ts", "src/_fixtures"] }