| /*--------------------------------------------------------- | |
| * Copyright 2025 The Go Authors. All rights reserved. | |
| * Licensed under the MIT License. See LICENSE in the project root for license information. | |
| *--------------------------------------------------------*/ | |
| // vscode.WorkspaceConfiguration.get() returns any type. So MockWorkspaceConfiguration | |
| // need to take a input of a Map containing any type. | |
| import assert from 'assert'; | |
| import * as sinon from 'sinon'; | |
| import * as vscode from 'vscode'; | |
| import { getGoConfig, getGoplsConfig, validateConfig } from '../../src/config'; | |
| import { MockWorkspaceConfiguration } from './mocks/configuration'; | |
| suite('Configuration validation tests', () => { | |
| let sandbox: sinon.SinonSandbox; | |
| const util = require('../../src/stateUtils'); | |
| let showWarningMessageStub: sinon.SinonStub; | |
| setup(() => { | |
| sandbox = sinon.createSandbox(); | |
| showWarningMessageStub = sandbox.stub(vscode.window, 'showWarningMessage'); | |
| }); | |
| teardown(() => { | |
| sandbox.restore(); | |
| }); | |
| interface TestCase { | |
| name: string; | |
| // Some configuration validation send one time suggestions to user. | |
| // This will be the return value for getGlobalState indicating whether | |
| // the suggestion have been made already. | |
| suggested?: boolean; | |
| // go and gopls configuration. | |
| goConfig?: Map<string, any>; | |
| goplsConfig?: Map<string, any>; | |
| expectWarning: boolean; | |
| warningMessage?: string; | |
| } | |
| const testCases: TestCase[] = [ | |
| // Lint tool setting tests. | |
| { | |
| name: 'Conflict: lintTool is staticcheck and gopls staticcheck true (fully enabled)', | |
| goConfig: new Map<string, any>([ | |
| ['lintTool', 'staticcheck'], | |
| ['useLanguageServer', true] | |
| ]), | |
| goplsConfig: new Map<string, any>([['ui.diagnostic.staticcheck', true]]), | |
| expectWarning: true, | |
| warningMessage: | |
| 'Warning: staticcheck is configured to run both client side (go.lintTool=staticcheck) and server side (gopls.ui.diagnostic.staticcheck=true)' | |
| }, | |
| { | |
| name: 'Conflict: lintTool is staticcheck and gopls staticcheck unset (partially enabled)', | |
| goConfig: new Map<string, any>([ | |
| ['lintTool', 'staticcheck'], | |
| ['useLanguageServer', true] | |
| ]), | |
| goplsConfig: new Map<string, any>(), | |
| expectWarning: true, | |
| warningMessage: | |
| 'Warning: staticcheck is configured to run both client side (go.lintTool=staticcheck) and server side (gopls.ui.diagnostic.staticcheck=true)' | |
| }, | |
| { | |
| name: 'Happy Path: lintTool is staticcheck and gopls staticcheck disabled', | |
| goConfig: new Map<string, any>([ | |
| ['lintTool', 'staticcheck'], | |
| ['useLanguageServer', true] | |
| ]), | |
| goplsConfig: new Map<string, any>([['ui.diagnostic.staticcheck', false]]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy path: lintTool is golangci-lint but gopls staticheck enable', | |
| goConfig: new Map<string, any>([ | |
| ['lintTool', 'golangci-lint'], | |
| ['useLanguageServer', true] | |
| ]), | |
| goplsConfig: new Map<string, any>([['ui.diagnostic.staticcheck', true]]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy Path: lintTool is unset and gopls staticcheck true (fully enabled)', | |
| goConfig: new Map<string, any>([['useLanguageServer', true]]), | |
| goplsConfig: new Map<string, any>([['ui.diagnostic.staticcheck', true]]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy Path: lintTool is unset and gopls staticcheck unset (partially enabled)', | |
| goConfig: new Map<string, any>([['useLanguageServer', true]]), | |
| goplsConfig: new Map<string, any>(), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy path: lintTool is unset but gopls staticheck disabled', | |
| goConfig: new Map<string, any>([['useLanguageServer', true]]), | |
| goplsConfig: new Map<string, any>([['ui.diagnostic.staticcheck', false]]), | |
| expectWarning: false | |
| }, | |
| // Format tool setting tests. | |
| { | |
| name: 'Error: formatTool is custom but path not provided', | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'custom'] | |
| ]), | |
| expectWarning: true, | |
| warningMessage: | |
| "Error: formatter is configured to custom (go.formatTool=custom) but custom formatter path is not provided in go.alternateTools['customFormatter']" | |
| }, | |
| { | |
| name: 'Happy path: formatTool is custom and path is provided', | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'custom'], | |
| ['alternateTools', { customFormatter: 'foo' }] | |
| ]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Conflict: formatTool is gofumpt and gopls gofumpt setting is true', | |
| suggested: true, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofumpt'] | |
| ]), | |
| goplsConfig: new Map<string, any>([['formatting.gofumpt', true]]), | |
| expectWarning: true, | |
| warningMessage: | |
| 'Warning: formatter is configured to run from both client side (go.formatTool=gofumpt) and server side (gopls.formatting.gofumpt=true).' | |
| }, | |
| { | |
| name: 'Conflict: formatTool is gofmt and gopls gofumpt setting is true', | |
| suggested: true, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofmt'] | |
| ]), | |
| goplsConfig: new Map<string, any>([['formatting.gofumpt', true]]), | |
| expectWarning: true, | |
| warningMessage: | |
| 'Warning: formatter is configured to run from both client side (go.formatTool=gofmt) and server side (gopls.formatting.gofumpt=true).' | |
| }, | |
| { | |
| name: 'Suggestion: formatTool is gofumpt', | |
| suggested: false, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofumpt'] | |
| ]), | |
| goplsConfig: new Map<string, any>([['formatting.gofumpt', true]]), | |
| expectWarning: true, | |
| warningMessage: 'Recommendation: the format tool gofumpt specified is available in gopls.' | |
| }, | |
| { | |
| name: 'Suggestion: formatTool is gofmt', | |
| suggested: false, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofmt'] | |
| ]), | |
| expectWarning: true, | |
| warningMessage: 'Recommendation: the format tool gofmt specified is available in gopls.' | |
| }, | |
| { | |
| name: 'Suggestion skipped: formatTool is gofumpt and has been suggested', | |
| suggested: true, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofumpt'] | |
| ]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Suggestion skipped: formatTool is gofmt and has been suggested', | |
| suggested: true, | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'gofmt'] | |
| ]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy path: formatTool is default, by default gopls will use gofmt', | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'default'] | |
| ]), | |
| expectWarning: false | |
| }, | |
| { | |
| name: 'Happy path: formatTool is default, set gopls.formatting.gofumpt', | |
| goConfig: new Map<string, any>([ | |
| ['useLanguageServer', true], | |
| ['formatTool', 'default'] | |
| ]), | |
| goplsConfig: new Map<string, any>([['formatting.gofumpt', true]]), | |
| expectWarning: false | |
| } | |
| ]; | |
| testCases.forEach((tc) => { | |
| test(tc.name, async () => { | |
| const goConfig = new MockWorkspaceConfiguration(getGoConfig(), tc.goConfig); | |
| const goplsConfig = new MockWorkspaceConfiguration(getGoplsConfig(), tc.goplsConfig); | |
| if (tc.suggested) { | |
| sandbox.stub(util, 'getFromGlobalState').returns(tc.suggested); | |
| } | |
| await validateConfig(goConfig, goplsConfig); | |
| if (tc.expectWarning) { | |
| assert( | |
| showWarningMessageStub.calledWith( | |
| sinon.match((msg: string) => msg.startsWith(tc.warningMessage!)), | |
| sinon.match('Open Settings') | |
| ), | |
| `showWarningMessage was not called with a message starting with "${tc.warningMessage}"` | |
| ); | |
| } else { | |
| assert(showWarningMessageStub.notCalled, 'showWarningMessage was called unexpectedly'); | |
| } | |
| }); | |
| }); | |
| }); |