Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"lint": "nx run-many -t lint"
},
"devDependencies": {
"nx": "^23.2.0"
"nx": "^23.2.1"
}
}
4 changes: 2 additions & 2 deletions packages/gitlab-mcp-db/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"devDependencies": {
"@structured-world/gitlab-mcp": "workspace:^",
"@types/jest": "^30.0.0",
"@types/node": "^25.9.5",
"jest": "^30.5.1",
"@types/node": "^25.9.8",
"jest": "^30.5.2",
"ts-jest": "^29.4.12",
"typescript": "^6.0.3"
},
Expand Down
26 changes: 13 additions & 13 deletions packages/gitlab-mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -560,39 +560,39 @@
"build:mcpb": "./scripts/build-mcpb.sh"
},
"dependencies": {
"@clack/prompts": "^1.7.0",
"@clack/prompts": "^1.8.1",
"@modelcontextprotocol/sdk": "^1.30.0",
"express": "^5.2.1",
"graphql": "^17.0.2",
"graphql-tag": "^2.12.7",
"open": "^11.0.2",
"open": "^11.0.4",
"picomatch": "^4.0.7",
"pino": "^10.3.1",
"pino-pretty": "^13.1.3",
"transliteration": "^2.6.1",
"undici": "^8.10.2",
"xstate": "^5.32.6",
"yaml": "^2.9.0",
"zod": "^4.5.4"
"xstate": "^5.33.2",
"yaml": "^2.9.1",
"zod": "^4.6.5"
},
"devDependencies": {
"@cloudflare/workers-types": "^5.20260905.1",
"@cloudflare/workers-types": "^5.20260919.1",
"@eslint/js": "^10.0.1",
"@graphql-typed-document-node/core": "^3.2.0",
"@structured-world/vue-privacy": "^1.10.0",
"@types/express": "^5.0.6",
"@types/jest": "^30.0.0",
"@types/node": "^25.9.5",
"@types/node": "^25.9.8",
"@types/picomatch": "^4.0.3",
"@typescript-eslint/eslint-plugin": "^8.69.0",
"@typescript-eslint/parser": "^8.69.0",
"auto-changelog": "^2.6.0",
"@typescript-eslint/eslint-plugin": "^8.70.0",
"@typescript-eslint/parser": "^8.70.0",
"auto-changelog": "^2.6.1",
"cross-env": "^10.1.0",
"dotenv": "^17.4.2",
"eslint": "^10.10.0",
"eslint": "^10.11.0",
"eslint-plugin-prettier": "^5.5.6",
"jest": "^30.5.1",
"prettier": "^3.9.6",
"jest": "^30.5.2",
"prettier": "^3.9.8",
"ts-jest": "^29.4.12",
"ts-node": "^10.9.2",
"typescript": "^6.0.3",
Expand Down
41 changes: 17 additions & 24 deletions packages/gitlab-mcp/tests/unit/cli/docker/docker-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jest.mock('@clack/prompts', () => ({
select: jest.fn(),
isCancel: jest.fn(() => false),
cancel: jest.fn(),
CANCEL_SYMBOL: Symbol('clack:cancel'),
}));

import {
Expand Down Expand Up @@ -589,9 +590,8 @@ describe('docker-command', () => {
dockerRunning: true,
composeInstalled: true,
});
const cancelSymbol = Symbol.for('cancel');
mockP.text.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.text.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await initDocker();

Expand All @@ -605,9 +605,8 @@ describe('docker-command', () => {
composeInstalled: true,
});
mockP.text.mockResolvedValueOnce('3333');
const cancelSymbol = Symbol.for('cancel');
mockP.confirm.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.confirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await initDocker();

Expand Down Expand Up @@ -675,9 +674,8 @@ describe('docker-command', () => {
});
mockP.text.mockResolvedValueOnce('3333');
mockP.confirm.mockResolvedValueOnce(false);
const cancelSymbol = Symbol.for('cancel');
mockP.confirm.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.confirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await initDocker();

Expand Down Expand Up @@ -738,19 +736,17 @@ describe('docker-command', () => {
});

it('should handle host input cancellation', async () => {
const cancelSymbol = Symbol.for('cancel');
mockP.text.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.text.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await dockerAddInstance();

expect(mockP.cancel).toHaveBeenCalledWith('Setup cancelled');
});

it('should handle name input cancellation', async () => {
const cancelSymbol = Symbol.for('cancel');
mockP.text.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.text.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await dockerAddInstance('gitlab.company.com');

Expand All @@ -759,9 +755,8 @@ describe('docker-command', () => {

it('should handle OAuth confirmation cancellation', async () => {
mockP.text.mockResolvedValueOnce('Name');
const cancelSymbol = Symbol.for('cancel');
mockP.confirm.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.confirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await dockerAddInstance('gitlab.company.com');

Expand Down Expand Up @@ -789,9 +784,8 @@ describe('docker-command', () => {
it('should handle OAuth client ID cancellation', async () => {
mockP.text.mockResolvedValueOnce('Name');
mockP.confirm.mockResolvedValueOnce(true);
const cancelSymbol = Symbol.for('cancel');
mockP.text.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.text.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await dockerAddInstance('gitlab.company.com');

Expand All @@ -801,9 +795,8 @@ describe('docker-command', () => {
it('should handle preset selection cancellation', async () => {
mockP.text.mockResolvedValueOnce('Name');
mockP.confirm.mockResolvedValueOnce(false);
const cancelSymbol = Symbol.for('cancel');
mockP.select.mockResolvedValueOnce(cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.select.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

await dockerAddInstance('gitlab.company.com');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jest.mock('@clack/prompts', () => ({
confirm: jest.fn(),
cancel: jest.fn(),
isCancel: jest.fn(() => false),
CANCEL_SYMBOL: Symbol('clack:cancel'),
}));

// Mock detector module
Expand Down Expand Up @@ -421,7 +422,7 @@ describe('install-command', () => {
});

it('should handle user cancellation during client selection', async () => {
mockP.multiselect.mockResolvedValueOnce(Symbol.for('cancel'));
mockP.multiselect.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockP.isCancel.mockReturnValueOnce(true);

const results = await runInstallWizard(mockServerConfig, {});
Expand All @@ -448,10 +449,9 @@ describe('install-command', () => {
detectAllClients.mockReturnValueOnce([
{ client: 'claude-desktop', detected: true, alreadyConfigured: true },
]);
const cancelSymbol = Symbol.for('cancel');
mockP.confirm.mockResolvedValueOnce(cancelSymbol);
mockP.confirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
// isCancel is called after confirm returns
mockP.isCancel.mockImplementation((val) => val === cancelSymbol);
mockP.isCancel.mockImplementation((val) => val === p.CANCEL_SYMBOL);

const results = await runInstallWizard(mockServerConfig, { claudeDesktop: true });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jest.mock('@clack/prompts', () => ({
step: jest.fn(),
},
isCancel: jest.fn().mockReturnValue(false),
CANCEL_SYMBOL: Symbol('clack:cancel'),
}));

jest.mock('../../../../../src/cli/install/installers', () => ({
Expand Down Expand Up @@ -103,7 +104,7 @@ describe('flows/configure-existing', () => {

it('should return cancelled when action is cancelled', async () => {
mockIsCancel.mockReturnValueOnce(true);
mockSelect.mockResolvedValueOnce(Symbol('cancel'));
mockSelect.mockResolvedValueOnce(p.CANCEL_SYMBOL);

const result = await runConfigureExistingFlow(discoveryWithUnconfigured);

Expand Down Expand Up @@ -153,7 +154,7 @@ describe('flows/configure-existing', () => {
});
mockSelect.mockResolvedValueOnce('add-clients');
mockMultiselect.mockResolvedValueOnce(['claude-code']);
mockPassword.mockResolvedValueOnce(Symbol('cancel'));
mockPassword.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel
.mockReturnValueOnce(false) // action
.mockReturnValueOnce(false) // multiselect
Expand All @@ -167,7 +168,7 @@ describe('flows/configure-existing', () => {

it('should return cancelled when client selection is cancelled during add', async () => {
mockSelect.mockResolvedValueOnce('add-clients');
mockMultiselect.mockResolvedValueOnce(Symbol('cancel'));
mockMultiselect.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel.mockReturnValueOnce(false).mockReturnValueOnce(true);

const result = await runConfigureExistingFlow(discoveryWithUnconfigured);
Expand Down Expand Up @@ -206,7 +207,7 @@ describe('flows/configure-existing', () => {

it('should return cancelled when client selection is cancelled during update', async () => {
mockSelect.mockResolvedValueOnce('update-clients');
mockMultiselect.mockResolvedValueOnce(Symbol('cancel'));
mockMultiselect.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel.mockReturnValueOnce(false).mockReturnValueOnce(true);

const result = await runConfigureExistingFlow(discoveryWithUnconfigured);
Expand Down Expand Up @@ -238,7 +239,7 @@ describe('flows/configure-existing', () => {
});
mockSelect.mockResolvedValueOnce('update-clients');
mockMultiselect.mockResolvedValueOnce(['cursor']);
mockPassword.mockResolvedValueOnce(Symbol('cancel'));
mockPassword.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel
.mockReturnValueOnce(false) // action
.mockReturnValueOnce(false) // multiselect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jest.mock('@clack/prompts', () => ({
step: jest.fn(),
},
isCancel: jest.fn().mockReturnValue(false),
CANCEL_SYMBOL: Symbol('clack:cancel'),
}));

jest.mock('../../../../../src/cli/init/connection', () => ({
Expand Down Expand Up @@ -113,7 +114,7 @@ describe('flows/local-setup', () => {

it('should return cancelled when instance type is cancelled', async () => {
mockIsCancel.mockReturnValueOnce(true);
mockSelect.mockResolvedValueOnce(Symbol('cancel'));
mockSelect.mockResolvedValueOnce(p.CANCEL_SYMBOL);

const result = await runLocalSetupFlow(emptyDiscovery);

Expand Down Expand Up @@ -146,7 +147,7 @@ describe('flows/local-setup', () => {

it('should return cancelled when URL input is cancelled', async () => {
mockSelect.mockResolvedValueOnce('self-hosted');
mockText.mockResolvedValueOnce(Symbol('cancel'));
mockText.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel.mockReturnValueOnce(false).mockReturnValueOnce(true);

const result = await runLocalSetupFlow(emptyDiscovery);
Expand All @@ -157,7 +158,7 @@ describe('flows/local-setup', () => {

it('should return cancelled when has-token confirm is cancelled', async () => {
mockSelect.mockResolvedValueOnce('saas');
mockConfirm.mockResolvedValueOnce(Symbol('cancel'));
mockConfirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel.mockReturnValueOnce(false).mockReturnValueOnce(true);

const result = await runLocalSetupFlow(emptyDiscovery);
Expand All @@ -183,7 +184,7 @@ describe('flows/local-setup', () => {
it('should return cancelled when token input is cancelled', async () => {
mockSelect.mockResolvedValueOnce('saas');
mockConfirm.mockResolvedValueOnce(true); // has token
mockPassword.mockResolvedValueOnce(Symbol('cancel'));
mockPassword.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel
.mockReturnValueOnce(false) // instance
.mockReturnValueOnce(false) // confirm
Expand Down Expand Up @@ -311,7 +312,7 @@ describe('flows/local-setup', () => {
mockSelect.mockResolvedValueOnce('saas');
mockConfirm.mockResolvedValueOnce(true);
mockPassword.mockResolvedValueOnce('glpat-xxxxxxxxxxxxxxxxxxxx');
mockMultiselect.mockResolvedValueOnce(Symbol('cancel'));
mockMultiselect.mockResolvedValueOnce(p.CANCEL_SYMBOL);
// isCancel returns false for all prior prompts, then true for multiselect
mockIsCancel
.mockReturnValueOnce(false) // instance type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jest.mock('@clack/prompts', () => ({
},
note: jest.fn(),
isCancel: jest.fn().mockReturnValue(false),
CANCEL_SYMBOL: Symbol('clack:cancel'),
}));

jest.mock('../../../../../src/cli/docker/docker-utils', () => ({
Expand Down Expand Up @@ -105,7 +106,7 @@ describe('flows/server-setup', () => {

it('should return cancelled when deployment type is cancelled', async () => {
mockIsCancel.mockReturnValueOnce(true);
mockSelect.mockResolvedValueOnce(Symbol('cancel'));
mockSelect.mockResolvedValueOnce(p.CANCEL_SYMBOL);

const result = await runServerSetupFlow(dockerReadyDiscovery);

Expand All @@ -115,7 +116,7 @@ describe('flows/server-setup', () => {

it('should return cancelled when port is cancelled', async () => {
mockSelect.mockResolvedValueOnce('standalone');
mockText.mockResolvedValueOnce(Symbol('cancel'));
mockText.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel.mockReturnValueOnce(false).mockReturnValueOnce(true);

const result = await runServerSetupFlow(dockerReadyDiscovery);
Expand All @@ -127,7 +128,7 @@ describe('flows/server-setup', () => {
it('should return cancelled when OAuth confirm is cancelled', async () => {
mockSelect.mockResolvedValueOnce('standalone');
mockText.mockResolvedValueOnce('3333');
mockConfirm.mockResolvedValueOnce(Symbol('cancel'));
mockConfirm.mockResolvedValueOnce(p.CANCEL_SYMBOL);
mockIsCancel
.mockReturnValueOnce(false) // deployment
.mockReturnValueOnce(false) // port
Expand Down Expand Up @@ -200,7 +201,7 @@ describe('flows/server-setup', () => {

it('should return cancelled when database URL is cancelled', async () => {
mockSelect.mockResolvedValueOnce('external-db');
mockText.mockResolvedValueOnce('3333').mockResolvedValueOnce(Symbol('cancel')); // db url cancel
mockText.mockResolvedValueOnce('3333').mockResolvedValueOnce(p.CANCEL_SYMBOL); // db url cancel
mockConfirm.mockResolvedValueOnce(true); // enable oauth
mockIsCancel
.mockReturnValueOnce(false) // deployment
Expand Down
Loading
Loading