/** * Shared fixture for the dev() startup tests: one copy of the "mock * everything dev() touches" scaffold, typed against the real services so a * missing or renamed mock member fails typecheck instead of throwing a * TypeError that one of dev()'s best-effort catches (e.g. the git bootstrap * catch around the startup git phase) swallows while the suite stays green. * * vi.mock calls are hoisted per test file and cannot live here as plain * calls, so this module exports FACTORIES that each test file passes to its * own one-liner vi.mock calls: * * vi.mock("node:child_process", () => mockChildProcessModule()); * * That is safe under vitest hoisting because none of the factory-mocked * modules is statically imported by the test files: they all load lazily * during `await import("./dev.mjs")` inside the tests, long after this * module (a plain static import) has been evaluated. The "@opentelemetry/api" * mock stays INLINE and closure-free in each test file anyway: if a test * file ever gained a static import evaluated before this module that pulls * "@opentelemetry/api" in, a factory living here would run before this * module's bindings initialize. */ import * as http from "node:http"; import { vi, type Mock } from "vitest"; import type { LockInfo } from "@superblocksteam/shared"; import type * as npmRegistry from "@superblocksteam/vite-plugin-file-sync/npm-registry"; import type { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service"; import type { dev, DevServerAutoUpgradeMode } from "./dev.mjs"; /** * Pins a mock's members by NAME against the real service, so a member that * is renamed or removed on the real surface fails typecheck here instead of * silently TypeError-ing at runtime. Used where the mocks deliberately * return trimmed shapes; the LockService mocks go further and pin full * signatures with `satisfies Pick<...>`. */ type ServiceMembers = Record; /** * Matches @vitest/spy's internal `Procedure` so bare `vi.fn()` members keep * their permissive call surface. The alias exists because the inferred * `Mock` type is not nameable in this package's declaration * output (TS2883). */ export type AnyProcedure = (...args: any[]) => any; export declare const execMock: Mock; export declare const mockLogger: { info: Mock; warn: Mock; error: Mock; debug: Mock; }; export declare const startupOpLog: readonly string[]; export declare function resetStartupOpLog(): void; /** Recording point for suite-local mock overrides; tests themselves only read. */ export declare function pushStartupOp(op: string): void; export declare const mockCheckVersions: Mock<() => Promise<{ cliUpdated: boolean; upgradePromises: Promise[]; }>>; export declare const mockCreateDevServer: Mock<() => Promise>>; /** Default per-org npm registry client handed out by the AiService mock. */ export declare function defaultNpmRegistryClient(): { getConfig: ReturnType; organizationId: string; }; export declare function createMockGetNpmRegistryClient(): Mock<() => { getConfig: ReturnType; organizationId: string; } | undefined>; export declare function mockChildProcessModule(): { default: { exec: Mock; execFile: Mock; }; exec: Mock; execFile: Mock; }; export declare function mockReadlineModule(): { default: { createInterface: Mock<() => { question: Mock; close: Mock; }>; }; createInterface: Mock<() => { question: Mock; close: Mock; }>; }; export declare function mockFsExtraModule(): { default: { existsSync: Mock<() => boolean>; readFileSync: Mock<() => string>; }; }; export declare function mockPackageManagerDetectorDetectModule(): { detect: Mock<() => Promise<{ name: string; agent: string; version: string; }>>; }; export declare function mockPackageManagerDetectorModule(): { resolveCommand: Mock<() => { command: string; args: string[]; }>; }; export declare function mockReadPkgModule(): { readPackage: Mock<() => Promise<{ name: string; dependencies: { "@superblocksteam/library": string; }; }>>; }; export declare function mockLoggingModule(options?: { errorMeta?: () => unknown; }): { getLogger: () => { info: Mock; warn: Mock; error: Mock; debug: Mock; }; getErrorMeta: Mock<() => unknown>; }; /** * Populated by `mockTelemetryModule({ recordSpanStatuses: true })` so tests * can check the fatal path's APM signal: a crashed boot must not leave an OK * devServerStartup span. Reset with `resetSpanStatuses()` in beforeEach. */ export declare const spanStatusesBySpanName: Record>; export declare function resetSpanStatuses(): void; /** * Populated by `mockTelemetryModule({ recordSpanAttributes: true })`: the * attributes each span name was given, merged across calls. Reset with * `resetSpanAttributes()` in beforeEach. */ export declare const spanAttributesBySpanName: Record>; export declare function resetSpanAttributes(): void; export declare function mockTelemetryModule(options?: { recordSpanStatuses?: boolean; recordSpanAttributes?: boolean; }): { getTracer: () => { startActiveSpan: Mock<(name: string, fn: (span: { end: () => void; setStatus: (status: { code: number; }) => void; setAttribute: (key: string, value: unknown) => void; }) => Promise) => Promise>; }; isCloudPrem: boolean; shutdownTelemetry: Mock<() => Promise>; }; export declare function mockColoretteModule(): { green: (s: string) => string; }; export declare function mockSharedModule(importOriginal: () => Promise): Promise<{ buildGithubSuperblocksSyncWorkflow: Mock; buildGithubSuperblocksSyncWorkflowFromBaseUrl: Mock; ConflictError: { new (message?: string): { name: string; message: string; stack?: string; cause?: unknown; }; new (message?: string, options?: ErrorOptions): { name: string; message: string; stack?: string; cause?: unknown; }; captureStackTrace(targetObject: object, constructorOpt?: Function): void; prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any; stackTraceLimit: number; }; DEFAULT_NON_GIT_BRANCH: string; isGitHubRemoteUrl: Mock<() => boolean>; NotFoundError: { new (message?: string): { name: string; message: string; stack?: string; cause?: unknown; }; new (message?: string, options?: ErrorOptions): { name: string; message: string; stack?: string; cause?: unknown; }; captureStackTrace(targetObject: object, constructorOpt?: Function): void; prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any; stackTraceLimit: number; }; SUPERBLOCKS_LIVE_GIT_BRANCH: string; }>; export declare function mockUtilModule(): { maskUnixSignals: (fn: () => Promise) => Promise; }; export declare function mockAiServiceModule(options: { /** * dev.mts reads this at startup for the home `~/.npmrc` writer and the * per-org install-scripts policy. Returning undefined keeps the install * path on the "no policy resolved" branch and is also valid input for the * mocked-out `syncHomeNpmrc`. */ getNpmRegistryClient: () => unknown; /** Shared handle for tests that reconfigure startup remediation. */ remediateDeclaredDependencies?: Mock; }): { AiService: { new (): { initialize: Mock<() => Promise>; setSyncContextProvider: Mock; notifyMigrationAppArtifactsReady: Mock; removeIntegrationCache: Mock<() => Promise>; remediateDeclaredDependencies: Mock; chatSessionStore: { invalidateCache: Mock; }; getNpmRegistryClient: () => unknown; }; }; AiServiceFeatureFlags: { new (): {}; create: Mock<() => {}>; }; SnapshotManager: { new (): { executePendingRestore: Mock<() => Promise>; }; }; hasEditLifecycleCapability: Mock<() => boolean>; isSdkApiTemplate: Mock<() => boolean>; stripResolvedFromLockfile: Mock<() => Promise>; }; export declare function mockNpmRegistryModule(): { PNPM_ENABLED_ENV: string; detectAppPackageManager: Mock<() => Promise>; NpmRegistryClient: { new (deps: npmRegistry.NpmRegistryClientDeps): { readonly deps: npmRegistry.NpmRegistryClientDeps; getConfig: Mock<() => Promise<{ source: "not-configured"; config: { configured: boolean; }; }>>; get organizationId(): string; }; }; createDiskRegistryPolicyStore: Mock<() => { markConfigured: Mock<() => Promise>; clearConfigured: Mock<() => Promise>; hasEverBeenConfigured: Mock<() => Promise>; }>; isPnpmEnabled: Mock<() => boolean>; shouldIgnoreInstallScripts: Mock<() => boolean>; maybeWriteNpmrcForDir: Mock<() => Promise>; }; /** * Variant whose project-`.npmrc` reconcile records its completion on the * startup op log, for the tests that pin home-npmrc -> project-npmrc -> * install ordering. */ export declare function mockNpmRegistryModuleLoggingProjectNpmrc(): { shouldIgnoreInstallScripts: Mock<() => boolean>; maybeWriteNpmrcForDir: Mock<() => Promise>; PNPM_ENABLED_ENV: string; detectAppPackageManager: Mock<() => Promise>; NpmRegistryClient: { new (deps: npmRegistry.NpmRegistryClientDeps): { readonly deps: npmRegistry.NpmRegistryClientDeps; getConfig: Mock<() => Promise<{ source: "not-configured"; config: { configured: boolean; }; }>>; get organizationId(): string; }; }; createDiskRegistryPolicyStore: Mock<() => { markConfigured: Mock<() => Promise>; clearConfigured: Mock<() => Promise>; hasEverBeenConfigured: Mock<() => Promise>; }>; isPnpmEnabled: Mock<() => boolean>; }; /** * Variant for the S3-restore suite: `NpmInstallBlocked` stays real (the * startup remediation gate rejects with it and dev.mts routes it by * `instanceof`, which only holds if the test and the module under test * share one class), and the dependency-install-classifier exports reachable * from installPackages' catch path are stubbed so a failing install * surfaces as an InitialInstallFailed instead of vitest's mock-guard error * ("No 'X' export is defined on the mock"). */ export declare function mockNpmRegistryModuleWithClassifier(): Promise<{ NpmInstallBlocked: typeof npmRegistry.NpmInstallBlocked; PNPM_ENABLED_ENV: string; detectAppPackageManager: typeof npmRegistry.detectAppPackageManager; isPnpmEnabled: typeof npmRegistry.isPnpmEnabled; shouldIgnoreInstallScripts: Mock<() => boolean>; maybeWriteNpmrcForDir: Mock<() => Promise>; scrubSecrets: Mock<(value: string) => string>; parseNpmJsonDiagnostic: Mock<() => string>; parseNpmJsonError: Mock<() => null>; classifyNpmExecStderr: Mock<() => null>; NpmRegistryClient: { new (deps: npmRegistry.NpmRegistryClientDeps): { readonly deps: npmRegistry.NpmRegistryClientDeps; getConfig: Mock<() => Promise<{ source: "not-configured"; config: { configured: boolean; }; }>>; get organizationId(): string; }; }; createDiskRegistryPolicyStore: Mock<() => { markConfigured: Mock<() => Promise>; clearConfigured: Mock<() => Promise>; hasEverBeenConfigured: Mock<() => Promise>; }>; }>; /** Git-service module whose factory resolves to nothing (S3-restore suite). */ export declare function mockGitServiceModuleNoop(): { createGitService: Mock<() => Promise>; }; /** * Git-service module that records every git op on the startup op log. The * mocked `raw` answers every revparse with a bare commit hash, which sends * `fetchAndEnsureLiveBranch` down its checkoutOrCreate path; the ls-remote * and --show-current answers serve `ensureGitRepo`'s fresh-init path and its * push step. Note this does NOT drive dev()'s boot-time * `ensureRuntimeDbfsBranchConsistency` into a switch: with the ordering * SDK's single-shot git config the boot and git-phase branch resolutions * agree, so the reconcile early-returns. A test that wants the reconcile to * actually switch makes the FIRST `getApplicationGitConfig` call report no * remote (two-phase pattern). */ export declare function mockGitServiceModuleWithOpLog(options?: { completionDelayMs?: number; }): { createGitService: Mock<(cwd: string) => { workDir: string; configure: Mock<() => Promise>; init: Mock<() => Promise>; addRemote: Mock<() => Promise>; fetch: Mock<() => Promise>; raw: Mock<(args: string | string[]) => Promise<"" | "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef\trefs/heads/__live__\n" | "__live__\n">>; push: Mock<() => Promise>; getDefaultBranch: Mock<() => Promise>; getRemotes: Mock<() => Promise<{ name: string; refs: { fetch: string; }; }[]>>; remote: Mock<() => Promise>; revparse: Mock<(ref: string) => Promise>; checkout: Mock<() => Promise>; checkoutOrCreate: Mock<() => Promise>; }>; }; /** * One builder for the whole LockService surface dev() and the startup suites * touch. The mock mirrors the REAL service surface: every member carries its * real signature via `satisfies Pick`, so an omission or a * signature drift fails typecheck here instead of TypeError-ing at runtime * into one of dev()'s swallowed catches. Per-suite scenario differences * (e.g. the lock-acquisition suite's conflict responses) are expressed as * per-test mock overrides, not as trimmed builder variants. */ export declare function buildMockLockService(): { constructedWith: Mock<(config: unknown) => void>; acquireLock: Mock<() => Promise>; relinquishLock: Mock<() => Promise>; notifyClosedByServer: Mock<() => Promise>; shutdownAndExit: Mock<() => Promise>; setIsRestarting: Mock<() => Promise>; setOnLockConflict: Mock<(callback: (error: import("@superblocksteam/shared").ConflictError) => Promise | void) => void>; isLocked: false; connectedUsers: never[]; wasRecentlyActive: false; timeSinceLastActivity: number; isLockedWhenSettled: Mock<() => Promise>; switchBranch: Mock<() => Promise>; forceTakeover: Mock<() => Promise>; inspectLock: Mock<() => Promise>; }; export declare function mockLockServiceModule(mockLockService: ReturnType): { LockService: { new (config: unknown): {}; }; LockType: { CSB: string; LOCAL: string; }; ProcessLifetime: { EXTERNAL: string; OWNED: string; }; }; export declare function buildMockSyncService(): { downloadDirectory: Mock<() => Promise>; uploadDirectory: Mock<() => Promise>; uploadDirectoryNowIfNeeded: Mock<() => Promise>; cancelPendingSync: Mock<() => void>; setBranchName: Mock<(nextBranchName: string) => void>; }; /** Sync service whose DBFS download records on the startup op log. */ export declare function buildOrderingMockSyncService(): { downloadDirectory: Mock<() => Promise>; uploadDirectory: Mock<() => Promise>; uploadDirectoryNowIfNeeded: Mock<() => Promise>; cancelPendingSync: Mock<() => void>; setBranchName: Mock<(nextBranchName: string) => void>; }; export declare function mockSyncServiceModule(mockSyncService: ServiceMembers>): { SyncService: { new (): {}; }; }; export declare function mockOperationQueueModule(): { OperationQueue: { new (): { enqueue: Mock<(fn: () => Promise) => Promise>; }; }; }; export declare function mockServerRpcModule(): { AutoConnectingRpcClient: { new (): {}; }; }; export declare function mockDraftInterfaceModule(): {}; export declare function mockVersionDetectionModule(): { getCurrentCliVersion: Mock<() => Promise<{ version: string; alias: undefined; }>>; }; export declare function mockAutomaticUpgradesModule(): { checkVersionsAndWritePackageJson: () => Promise<{ cliUpdated: boolean; upgradePromises: Promise[]; }>; logLibraryVersionCheck: () => void; buildInstallEnv: (userconfigPath: string) => { NPM_CONFIG_USERCONFIG: string; PNPM_CONFIG_USERCONFIG: string; TZ?: string; }; }; /** * The home `~/.npmrc` writer is a best-effort step that runs before * auto-upgrade. Mocked out so the suites stay focused on their own * startup phase. */ export declare function mockHomeNpmrcModule(): { syncHomeNpmrc: Mock<() => Promise<{ outcome: string; path: string; }>>; superblocksNpmrcPath: Mock<() => string>; superblocksLogsPath: Mock<(appDir: string) => string>; }; /** Variant whose home sync records its completion on the startup op log. */ export declare function mockHomeNpmrcModuleWithOpLog(): { syncHomeNpmrc: Mock<() => Promise<{ outcome: string; path: string; }>>; superblocksNpmrcPath: Mock<() => string>; superblocksLogsPath: Mock<(appDir: string) => string>; }; export declare function mockGitRepoSetupModule(): { ensureRemoteHasDefaultBranch: Mock<() => Promise>; getGitErrorFields: Mock<() => {}>; }; export declare function mockDevServerModule(): { createDevServer: () => Promise>; }; export declare function mockIndexModule(): { AUTO_UPGRADE_EXIT_CODE: number; }; /** The options contract of the function under test. */ type DevOptions = Parameters[0]; /** * Like `Omit`, but requires each omitted key to be a real key of `T`. Plain * `Omit` accepts a `K` that is not in `keyof T` and silently drops * nothing, so an exemption for an option that gets renamed or removed on * dev()'s type would stop covering anything without a typecheck error. This * keeps every exemption honest: an exempted key that stops existing fails * here instead. */ type OmitKnown = Omit; /** SDK whose local and server hashes agree (no restore pressure). */ export declare function buildMockSdk(): { fetchCurrentUser: Mock<() => Promise<{ user: { id: string; name: string; email: string; currentOrganizationId: string; }; organizations: { id: string; pluginExecutionVersions: {}; }[]; flagBootstrap: {}; }>>; getFeatureFlagsForCurrentUser: Mock<() => Promise<{ devServerCloudInactivityTimeoutMinutes: () => 30; devServerLocalInactivityTimeoutMinutes: () => 30; enableSessionRecording: () => false; removeManualCommitsEnabled: () => false; pnpmEnabled: () => false; }>>; getFeatureFlagsForUser: Mock<() => { devServerCloudInactivityTimeoutMinutes: () => 30; devServerLocalInactivityTimeoutMinutes: () => 30; }>; hashLocalDirectory: Mock<() => Promise<{ hash: string; }>>; dbfsGetApplicationDirectoryHash: Mock<() => Promise>; getApplicationGitConfig: Mock<() => Promise>; }; /** * SDK for the ordering suites: the first local hash differs from the server * hash (a Clark-only DBFS change) so the restore download runs, and the * post-download hash matches. Both hashes record on the startup op log. */ export declare function buildOrderingMockSdk(): { fetchCurrentUser: Mock<() => Promise<{ user: { id: string; name: string; email: string; currentOrganizationId: string; }; organizations: { id: string; pluginExecutionVersions: {}; }[]; flagBootstrap: {}; }>>; getFeatureFlagsForCurrentUser: Mock<() => Promise<{ devServerCloudInactivityTimeoutMinutes: () => 30; devServerLocalInactivityTimeoutMinutes: () => 30; enableSessionRecording: () => false; removeManualCommitsEnabled: () => false; pnpmEnabled: () => false; }>>; getFeatureFlagsForUser: Mock<() => { devServerCloudInactivityTimeoutMinutes: () => 30; devServerLocalInactivityTimeoutMinutes: () => 30; }>; hashLocalDirectory: Mock; dbfsGetApplicationDirectoryHash: Mock<() => Promise>; getApplicationGitConfig: Mock<() => Promise<{ gitRemoteUrl: string; hasCredential: boolean; }>>; }; declare function buildDevOptionsCommon(): { downloadFirst: true; applicationConfig: { id: string; branchName: string; }; tokenConfig: { superblocksBaseUrl: string; token: string; }; tokenManager: { updateToken: Mock; getCurrentToken: Mock<() => string>; }; }; /** * The dev() options the builders assemble and the suites override. Every * member carries its real DevOptions type except: * * - `sdk` is the deliberately partial fake, pinned by MockSdkSurface; * - `tokenManager` is the harness fake, pinned to the two members dev() * calls (see buildDevOptionsCommon). The real TokenManager is nominal * (private field), so the fake carries its Pick type here rather than the * full class; buildDevOptionsCommon supplies it and no suite overrides it. * - `autoUpgradeMode` takes the enum's string VALUES, because importing the * enum itself would evaluate dev.mjs before the vi.mocks install. */ type DevOptionsOverrides = Partial> & { sdk?: ReturnType | ReturnType; tokenManager?: ReturnType["tokenManager"]; autoUpgradeMode?: `${DevServerAutoUpgradeMode}`; }; export declare function buildDevOptions(overrides?: DevOptionsOverrides): DevOptions; export declare function buildOrderingDevOptions(cwd: string, overrides?: DevOptionsOverrides): DevOptions; /** * Wires execMock so every child-process call completes successfully. * `logInstallToOpLog` records the completion on the startup op log; * `completionDelayMs` completes on a later tick, like a real child process, * so the log records when the install finished relative to its neighbours. */ export declare function setupExecMock(options?: { logInstallToOpLog?: boolean; completionDelayMs?: number; }): void; export {}; //# sourceMappingURL=dev-startup-test-harness.d.mts.map