Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions src/commands/branch/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Command } from 'commander';
import { registerBranchCreateCommand } from './create.js';

Expand Down Expand Up @@ -70,18 +70,18 @@ describe('branch create', () => {

it('rejects when no project linked', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue(null);
(getProjectConfig as Mock).mockReturnValue(null);
const program = new Command().exitOverride();
program.option('--json').option('--api-url <url>').option('-y, --yes');
registerBranchCreateCommand(program);
let exitCode: number | undefined;
const origExit = process.exit;
(process.exit as any) = (code?: number) => {
process.exit = ((code?: number) => {
exitCode = code;
throw new Error('__exit__');
};
}) as typeof process.exit;
const origStderr = process.stderr.write.bind(process.stderr);
process.stderr.write = (() => true) as any;
process.stderr.write = (() => true) as typeof process.stderr.write;
try {
await program
.parseAsync(['create', 'feat-x', '--mode', 'schema-only', '--no-switch', '--json'], {
Expand All @@ -97,7 +97,7 @@ describe('branch create', () => {

it('rejects an invalid --mode value before any API call', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -107,12 +107,12 @@ describe('branch create', () => {
registerBranchCreateCommand(program);
let exitCode: number | undefined;
const origExit = process.exit;
(process.exit as any) = (code?: number) => {
process.exit = ((code?: number) => {
exitCode = code;
throw new Error('__exit__');
};
}) as typeof process.exit;
const origStderr = process.stderr.write.bind(process.stderr);
process.stderr.write = (() => true) as any;
process.stderr.write = (() => true) as typeof process.stderr.write;
try {
await program
.parseAsync(['create', 'feat-x', '--mode', 'bogus', '--no-switch', '--json'], {
Expand All @@ -130,7 +130,7 @@ describe('branch create', () => {

it('happy path with --json: posts then prints branch payload', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand Down Expand Up @@ -168,7 +168,7 @@ describe('branch create', () => {

it('happy path without --no-switch invokes runBranchSwitch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand Down Expand Up @@ -205,7 +205,7 @@ describe('branch create', () => {

it('switch failure after a successful create reports "switch failed", not "creation failed"', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -215,19 +215,19 @@ describe('branch create', () => {
oss_host: 'https://p1ky.us-east.insforge.app',
});
const { runBranchSwitch } = await import('./switch.js');
(runBranchSwitch as any).mockRejectedValueOnce(new Error('network down'));
(runBranchSwitch as Mock).mockRejectedValueOnce(new Error('network down'));

const program = new Command().exitOverride();
program.option('--json').option('--api-url <url>').option('-y, --yes');
registerBranchCreateCommand(program);
let exitCode: number | undefined;
const origExit = process.exit;
(process.exit as any) = (code?: number) => {
process.exit = ((code?: number) => {
exitCode = code;
throw new Error('__exit__');
};
}) as typeof process.exit;
const origStderr = process.stderr.write.bind(process.stderr);
process.stderr.write = (() => true) as any;
process.stderr.write = (() => true) as typeof process.stderr.write;
try {
await program
.parseAsync(['create', 'feat-x', '--mode', 'full'], { from: 'user' })
Expand All @@ -249,7 +249,7 @@ describe('branch create', () => {

it('non-JSON path drives the spinner and keeps it active through switch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand Down
16 changes: 8 additions & 8 deletions src/commands/branch/delete.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Command } from 'commander';
import { registerBranchDeleteCommand } from './delete.js';

Expand Down Expand Up @@ -59,7 +59,7 @@ describe('branch delete', () => {

it('happy path with --yes calls deleteBranchApi and captures analytics', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -75,7 +75,7 @@ describe('branch delete', () => {

it('errors when the named branch does not exist', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -90,7 +90,7 @@ describe('branch delete', () => {

it('auto-switches back to parent when deleting the currently active branch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand All @@ -112,7 +112,7 @@ describe('branch delete', () => {

it('does not auto-switch when the deleted branch is not the currently active one', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -125,14 +125,14 @@ describe('branch delete', () => {

it('does not abort the delete command when post-delete switch-back fails', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
branched_from: { project_id: 'p1', project_name: 'parent' },
});
const { runBranchSwitch } = await import('./switch.js');
(runBranchSwitch as any).mockRejectedValueOnce(new Error('no parent backup'));
(runBranchSwitch as Mock).mockRejectedValueOnce(new Error('no parent backup'));
const program = makeProgram();
await runSilently(program, ['delete', 'feat-x', '--yes', '--json']);
// Delete still went through despite the switch-back failure.
Expand All @@ -142,7 +142,7 @@ describe('branch delete', () => {

it('json mode reports switched_back=true when the active branch was deleted', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand Down
16 changes: 8 additions & 8 deletions src/commands/branch/list.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Command } from 'commander';
import { registerBranchListCommand } from './list.js';

Expand Down Expand Up @@ -69,7 +69,7 @@ describe('branch list', () => {

it('lists siblings against project_id when not on a branch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -86,7 +86,7 @@ describe('branch list', () => {

it('lists siblings against branched_from.project_id when currently switched onto a branch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand All @@ -104,7 +104,7 @@ describe('branch list', () => {

it('json mode emits a single JSON document with the branches array', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -119,7 +119,7 @@ describe('branch list', () => {

it('table mode marks the current branch with `*`', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand All @@ -143,7 +143,7 @@ describe('branch list', () => {

it('does not mark any branch when currently on the parent', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -159,13 +159,13 @@ describe('branch list', () => {

it('prints "No branches." when the API returns an empty list', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
});
const { listBranchesApi } = await import('../../lib/api/platform.js');
(listBranchesApi as any).mockResolvedValueOnce([]);
(listBranchesApi as Mock).mockResolvedValueOnce([]);
const program = makeProgram();
const logs = await runWithCapturedLog(program, ['list']);
expect(logs.join('\n')).toContain('No branches.');
Expand Down
10 changes: 5 additions & 5 deletions src/commands/branch/merge.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Command } from 'commander';
import { registerBranchMergeCommand } from './merge.js';

Expand Down Expand Up @@ -143,7 +143,7 @@ describe('branch merge', () => {

it('conflict path exits with code 2 and prints per-conflict summary', async () => {
const { mergeBranchDryRunApi } = await import('../../lib/api/platform.js');
(mergeBranchDryRunApi as any).mockResolvedValueOnce({
(mergeBranchDryRunApi as Mock).mockResolvedValueOnce({
summary: { added: 0, modified: 0, conflicts: 1 },
rendered_sql: '-- ⚠️ MERGE BLOCKED: 1 conflict(s) detected.',
changes: [],
Expand All @@ -167,14 +167,14 @@ describe('branch merge', () => {
// overwrite the meaningful first exit. The first call is what the user sees.
let exitCode: number | undefined;
const origExit = process.exit;
(process.exit as any) = (code?: number) => {
process.exit = ((code?: number) => {
if (exitCode === undefined) exitCode = code;
throw new Error('__exit__');
};
}) as typeof process.exit;
const origLog = console.log;
console.log = () => {};
const origStderr = process.stderr.write.bind(process.stderr);
process.stderr.write = (() => true) as any;
process.stderr.write = (() => true) as typeof process.stderr.write;
try {
await program
.parseAsync(['merge', 'feat-x', '--dry-run'], { from: 'user' })
Expand Down
21 changes: 11 additions & 10 deletions src/commands/branch/switch.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { runBranchSwitch } from './switch.js';
import type { ProjectConfig } from '../../types.js';

const saveCalls: any[] = [];
const saveCalls: ProjectConfig[] = [];

vi.mock('../../lib/config.js', () => ({
getProjectConfig: vi.fn(),
saveProjectConfig: vi.fn((c: any) => saveCalls.push(c)),
saveProjectConfig: vi.fn((c: ProjectConfig) => saveCalls.push(c)),
getProjectConfigFile: () => '/tmp/_test_/.insforge/project.json',
getParentBackupFile: () => '/tmp/_test_/.insforge/project.parent.json',
buildOssHost: (appkey: string, region: string) => `https://${appkey}.${region}.insforge.app`,
Expand Down Expand Up @@ -59,7 +60,7 @@ describe('runBranchSwitch', () => {

it('switches from parent to a branch and creates parent.json backup', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'p1',
project_name: 'parent',
org_id: 'o1',
Expand All @@ -86,7 +87,7 @@ describe('runBranchSwitch', () => {

it('preserves parent backup when switching branch -> branch', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b0',
project_name: 'feat-y',
org_id: 'o1',
Expand All @@ -107,9 +108,9 @@ describe('runBranchSwitch', () => {

it('refuses to switch to a branch not in ready state', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({ project_id: 'p1', project_name: 'parent', org_id: 'o1' });
(getProjectConfig as Mock).mockReturnValue({ project_id: 'p1', project_name: 'parent', org_id: 'o1' });
const { listBranchesApi } = await import('../../lib/api/platform.js');
(listBranchesApi as any).mockResolvedValueOnce([
(listBranchesApi as Mock).mockResolvedValueOnce([
{
id: 'b1',
name: 'feat-x',
Expand All @@ -129,7 +130,7 @@ describe('runBranchSwitch', () => {

it('--parent restores from backup and removes the backup file', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand All @@ -148,7 +149,7 @@ describe('runBranchSwitch', () => {

it('rejects passing both a branch name and --parent', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({
(getProjectConfig as Mock).mockReturnValue({
project_id: 'b1',
project_name: 'feat-x',
org_id: 'o1',
Expand All @@ -161,7 +162,7 @@ describe('runBranchSwitch', () => {

it('--parent fails clearly when no backup exists', async () => {
const { getProjectConfig } = await import('../../lib/config.js');
(getProjectConfig as any).mockReturnValue({ project_id: 'b1', project_name: 'feat-x', org_id: 'o1' });
(getProjectConfig as Mock).mockReturnValue({ project_id: 'b1', project_name: 'feat-x', org_id: 'o1' });
fsMock.existsSync.mockReturnValueOnce(false);
await expect(
runBranchSwitch({ toParent: true, apiUrl: undefined, json: true }),
Expand Down
Loading
Loading