From 622dbb350269a1b06b67829503b06d67eab3a3df Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 17 Feb 2023 09:16:52 +0000 Subject: [PATCH] Remove isVariantAnalysisReposPanelEnabled feature flag and old flows --- extensions/ql-vscode/src/config.ts | 4 - .../ql-vscode/src/databases/db-module.ts | 4 +- extensions/ql-vscode/src/extension.ts | 2 +- .../variant-analysis/repository-selection.ts | 263 +-------- .../src/variant-analysis/run-remote-query.ts | 2 +- .../variant-analysis-manager.ts | 31 +- .../variant-analysis-manager.test.ts | 162 ++---- .../repository-selection.test.ts | 514 +++--------------- 8 files changed, 158 insertions(+), 824 deletions(-) diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 0ebe2291cf6..849f3aa1833 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -579,10 +579,6 @@ export function isVariantAnalysisLiveResultsEnabled(): boolean { return true; } -export function isVariantAnalysisReposPanelEnabled(): boolean { - return true; -} - // Settings for mocking the GitHub API. const MOCK_GH_API_SERVER = new Setting("mockGitHubApiServer", ROOT_SETTING); diff --git a/extensions/ql-vscode/src/databases/db-module.ts b/extensions/ql-vscode/src/databases/db-module.ts index 8f71528ce93..f81b42d25de 100644 --- a/extensions/ql-vscode/src/databases/db-module.ts +++ b/extensions/ql-vscode/src/databases/db-module.ts @@ -6,7 +6,7 @@ import { DbConfigStore } from "./config/db-config-store"; import { DbManager } from "./db-manager"; import { DbPanel } from "./ui/db-panel"; import { DbSelectionDecorationProvider } from "./ui/db-selection-decoration-provider"; -import { isCanary, isVariantAnalysisReposPanelEnabled } from "../config"; +import { isCanary } from "../config"; export class DbModule extends DisposableObject { public readonly dbManager: DbManager; @@ -36,7 +36,7 @@ export class DbModule extends DisposableObject { return true; } - return isCanary() && isVariantAnalysisReposPanelEnabled(); + return isCanary(); } private async initialize(app: App): Promise { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index d668a9055b0..c9d5d700996 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -638,7 +638,7 @@ async function activateWithInstalledDistribution( cliServer, variantAnalysisStorageDir, variantAnalysisResultsManager, - dbModule?.dbManager, // the dbModule is only needed when variantAnalysisReposPanel is enabled + dbModule?.dbManager, ); ctx.subscriptions.push(variantAnalysisManager); ctx.subscriptions.push(variantAnalysisResultsManager); diff --git a/extensions/ql-vscode/src/variant-analysis/repository-selection.ts b/extensions/ql-vscode/src/variant-analysis/repository-selection.ts index 924971fe4ce..c289067de9e 100644 --- a/extensions/ql-vscode/src/variant-analysis/repository-selection.ts +++ b/extensions/ql-vscode/src/variant-analysis/repository-selection.ts @@ -1,12 +1,3 @@ -import { pathExists as fs_pathExists, stat, readFile } from "fs-extra"; -import { QuickPickItem, window } from "vscode"; -import { extLogger } from "../common"; -import { - getRemoteRepositoryLists, - getRemoteRepositoryListsPath, - isVariantAnalysisReposPanelEnabled, -} from "../config"; -import { OWNER_REGEX, REPO_REGEX } from "../pure/helpers-pure"; import { UserCancellationException } from "../commandRunner"; import { DbManager } from "../databases/db-manager"; import { DbItemKind } from "../databases/db-item"; @@ -17,18 +8,6 @@ export interface RepositorySelection { owners?: string[]; } -interface RepoListQuickPickItem extends QuickPickItem { - repositories?: string[]; - repositoryList?: string; - useCustomRepo?: boolean; - useAllReposOfOwner?: boolean; -} - -interface RepoList { - label: string; - repositories: string[]; -} - /** * Gets the repositories or repository lists to run the query against. * @returns The user selection. @@ -36,101 +15,35 @@ interface RepoList { export async function getRepositorySelection( dbManager?: DbManager, ): Promise { - if (isVariantAnalysisReposPanelEnabled()) { - const selectedDbItem = dbManager?.getSelectedDbItem(); - if (selectedDbItem) { - switch (selectedDbItem.kind) { - case DbItemKind.LocalDatabase || DbItemKind.LocalList: + const selectedDbItem = dbManager?.getSelectedDbItem(); + if (selectedDbItem) { + switch (selectedDbItem.kind) { + case DbItemKind.LocalDatabase || DbItemKind.LocalList: + throw new UserCancellationException( + "Local databases and lists are not supported yet.", + ); + case DbItemKind.RemoteSystemDefinedList: + return { repositoryLists: [selectedDbItem.listName] }; + case DbItemKind.RemoteUserDefinedList: + if (selectedDbItem.repos.length === 0) { throw new UserCancellationException( - "Local databases and lists are not supported yet.", + "The selected repository list is empty. Please add repositories to it before running a variant analysis.", ); - case DbItemKind.RemoteSystemDefinedList: - return { repositoryLists: [selectedDbItem.listName] }; - case DbItemKind.RemoteUserDefinedList: - if (selectedDbItem.repos.length === 0) { - throw new UserCancellationException( - "The selected repository list is empty. Please add repositories to it before running a variant analysis.", - ); - } else { - return { - repositories: selectedDbItem.repos.map( - (repo) => repo.repoFullName, - ), - }; - } - case DbItemKind.RemoteOwner: - return { owners: [selectedDbItem.ownerName] }; - case DbItemKind.RemoteRepo: - return { repositories: [selectedDbItem.repoFullName] }; - } - } else { - throw new UserCancellationException( - "Please select a remote database to run the query against.", - ); + } else { + return { + repositories: selectedDbItem.repos.map((repo) => repo.repoFullName), + }; + } + case DbItemKind.RemoteOwner: + return { owners: [selectedDbItem.ownerName] }; + case DbItemKind.RemoteRepo: + return { repositories: [selectedDbItem.repoFullName] }; } } - const quickPickItems = [ - createCustomRepoQuickPickItem(), - createAllReposOfOwnerQuickPickItem(), - ...createSystemDefinedRepoListsQuickPickItems(), - ...(await createUserDefinedRepoListsQuickPickItems()), - ]; - - const options = { - placeHolder: - "Select a repository list. You can define repository lists in the `codeQL.variantAnalysis.repositoryLists` setting.", - ignoreFocusOut: true, - }; - - const quickpick = await window.showQuickPick( - quickPickItems, - options, + throw new UserCancellationException( + "Please select a remote database to run the query against.", ); - - if (!quickpick) { - // We don't need to display a warning pop-up in this case, since the user just escaped out of the operation. - // We set 'true' to make this a silent exception. - throw new UserCancellationException("No repositories selected", true); - } - - if (quickpick.repositories?.length) { - void extLogger.log( - `Selected repositories: ${quickpick.repositories.join(", ")}`, - ); - return { repositories: quickpick.repositories }; - } else if (quickpick.repositoryList) { - void extLogger.log(`Selected repository list: ${quickpick.repositoryList}`); - return { repositoryLists: [quickpick.repositoryList] }; - } else if (quickpick.useCustomRepo) { - const customRepo = await getCustomRepo(); - if (customRepo === undefined) { - // The user cancelled, do nothing. - throw new UserCancellationException("No repositories selected", true); - } - if (!customRepo || !REPO_REGEX.test(customRepo)) { - throw new UserCancellationException( - "Invalid repository format. Please enter a valid repository in the format / (e.g. github/codeql)", - ); - } - void extLogger.log(`Entered repository: ${customRepo}`); - return { repositories: [customRepo] }; - } else if (quickpick.useAllReposOfOwner) { - const owner = await getOwner(); - if (owner === undefined) { - // The user cancelled, do nothing. - throw new UserCancellationException("No repositories selected", true); - } - if (!owner || !OWNER_REGEX.test(owner)) { - throw new Error(`Invalid user or organization: ${owner}`); - } - void extLogger.log(`Entered owner: ${owner}`); - return { owners: [owner] }; - } else { - // This means the user has selected something, but there is nothing actually linked to this item. We want to show - // this to the user. - throw new UserCancellationException("No repositories selected", false); - } } /** @@ -147,133 +60,3 @@ export function isValidSelection(repoSelection: RepositorySelection): boolean { repositories.length > 0 || repositoryLists.length > 0 || owners.length > 0 ); } - -function createSystemDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { - const topNs = [10, 100, 1000]; - - return topNs.map( - (n) => - ({ - label: `$(star) Top ${n}`, - repositoryList: `top_${n}`, - alwaysShow: true, - } as RepoListQuickPickItem), - ); -} - -async function readExternalRepoLists(): Promise { - const repoLists: RepoList[] = []; - - const path = getRemoteRepositoryListsPath(); - if (!path) { - return repoLists; - } - - await validateExternalRepoListsFile(path); - const json = await readExternalRepoListsJson(path); - - for (const [repoListName, repositories] of Object.entries(json)) { - if (!Array.isArray(repositories)) { - throw Error( - "Invalid repository lists file. It should contain an array of repositories for each list.", - ); - } - - repoLists.push({ - label: repoListName, - repositories, - }); - } - - return repoLists; -} - -async function validateExternalRepoListsFile(path: string): Promise { - const pathExists = await fs_pathExists(path); - if (!pathExists) { - throw Error(`External repository lists file does not exist at ${path}`); - } - - const pathStat = await stat(path); - if (pathStat.isDirectory()) { - throw Error( - "External repository lists path should not point to a directory", - ); - } -} - -async function readExternalRepoListsJson( - path: string, -): Promise> { - let json; - - try { - const fileContents = await readFile(path, "utf8"); - json = await JSON.parse(fileContents); - } catch (error) { - throw Error("Invalid repository lists file. It should contain valid JSON."); - } - - if (Array.isArray(json)) { - throw Error( - "Invalid repository lists file. It should be an object mapping names to a list of repositories.", - ); - } - - return json; -} - -function readRepoListsFromSettings(): RepoList[] { - const repoLists = getRemoteRepositoryLists(); - if (!repoLists) { - return []; - } - - return Object.entries(repoLists).map(([label, repositories]) => ({ - label, - repositories, - })); -} - -async function createUserDefinedRepoListsQuickPickItems(): Promise< - RepoListQuickPickItem[] -> { - const repoListsFromSetings = readRepoListsFromSettings(); - const repoListsFromExternalFile = await readExternalRepoLists(); - - return [...repoListsFromSetings, ...repoListsFromExternalFile]; -} - -function createCustomRepoQuickPickItem(): RepoListQuickPickItem { - return { - label: "$(edit) Enter a GitHub repository", - useCustomRepo: true, - alwaysShow: true, - }; -} - -function createAllReposOfOwnerQuickPickItem(): RepoListQuickPickItem { - return { - label: "$(edit) Enter a GitHub user or organization", - useAllReposOfOwner: true, - alwaysShow: true, - }; -} - -async function getCustomRepo(): Promise { - return await window.showInputBox({ - title: - "Enter a GitHub repository in the format / (e.g. github/codeql)", - placeHolder: "/", - prompt: - "Tip: you can save frequently used repositories in the `codeQL.variantAnalysis.repositoryLists` setting", - ignoreFocusOut: true, - }); -} - -async function getOwner(): Promise { - return await window.showInputBox({ - title: "Enter a GitHub user or organization", - ignoreFocusOut: true, - }); -} diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index b5a885b7f98..aac3e4f0b36 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -223,7 +223,7 @@ export async function prepareRemoteQueryRun( uri: Uri | undefined, progress: ProgressCallback, token: CancellationToken, - dbManager?: DbManager, // the dbManager is only needed when variantAnalysisReposPanel is enabled + dbManager?: DbManager, ): Promise { if (!uri?.fsPath.endsWith(".ql")) { throw new UserCancellationException("Not a CodeQL query file."); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 9bc6fbf573c..9156617f4d0 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -60,7 +60,6 @@ import { } from "../pure/variant-analysis-filter-sort"; import { URLSearchParams } from "url"; import { DbManager } from "../databases/db-manager"; -import { isVariantAnalysisReposPanelEnabled } from "../config"; import { App } from "../common/app"; import { redactableError } from "../pure/errors"; @@ -106,7 +105,7 @@ export class VariantAnalysisManager private readonly cliServer: CodeQLCliServer, private readonly storagePath: string, private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager, - private readonly dbManager?: DbManager, // the dbManager is only needed when variantAnalysisReposPanel is enabled + private readonly dbManager?: DbManager, ) { super(); this.variantAnalysisMonitor = this.push( @@ -635,25 +634,15 @@ export class VariantAnalysisManager return; } - let text: string[]; - if (isVariantAnalysisReposPanelEnabled()) { - text = [ - "{", - ` "name": "new-repo-list",`, - ` "repositories": [`, - ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), - ` "${fullNames[fullNames.length - 1]}"`, - ` ]`, - "}", - ]; - } else { - text = [ - '"new-repo-list": [', - ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), - ` "${fullNames[fullNames.length - 1]}"`, - "]", - ]; - } + const text = [ + "{", + ` "name": "new-repo-list",`, + ` "repositories": [`, + ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), + ` "${fullNames[fullNames.length - 1]}"`, + ` ]`, + "}", + ]; await env.clipboard.writeText(text.join(EOL)); } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 6af75f00842..b97b62d5342 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -12,7 +12,6 @@ import { } from "vscode"; import { CodeQLExtensionInterface } from "../../../../src/extension"; import { extLogger } from "../../../../src/common"; -import * as config from "../../../../src/config"; import { setRemoteControllerRepo, setRemoteRepositoryLists, @@ -915,137 +914,64 @@ describe("Variant Analysis Manager", () => { expect(writeTextStub).toBeCalledTimes(1); }); - describe("variantAnalysisReposPanel true", () => { - beforeEach(() => { - jest - .spyOn(config, "isVariantAnalysisReposPanelEnabled") - .mockReturnValue(true); - }); - - it("should be valid JSON when put in object", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - ); - - const text = writeTextStub.mock.calls[0][0]; - - const parsed = JSON.parse(`${text}`); - - expect(parsed).toEqual({ - name: "new-repo-list", - repositories: [ - scannedRepos[4].repository.fullName, - scannedRepos[2].repository.fullName, - scannedRepos[0].repository.fullName, - ], - }); - }); - - it("should use the sort key", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - { - ...defaultFilterSortState, - sortKey: SortKey.ResultsCount, - }, - ); - - const text = writeTextStub.mock.calls[0][0]; - - const parsed = JSON.parse(`${text}`); - - expect(parsed).toEqual({ - name: "new-repo-list", - repositories: [ - scannedRepos[2].repository.fullName, - scannedRepos[0].repository.fullName, - scannedRepos[4].repository.fullName, - ], - }); - }); - - it("should use the search value", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - { - ...defaultFilterSortState, - searchValue: "ban", - }, - ); + it("should be valid JSON when put in object", async () => { + await variantAnalysisManager.copyRepoListToClipboard( + variantAnalysis.id, + ); - const text = writeTextStub.mock.calls[0][0]; + const text = writeTextStub.mock.calls[0][0]; - const parsed = JSON.parse(`${text}`); + const parsed = JSON.parse(`${text}`); - expect(parsed).toEqual({ - name: "new-repo-list", - repositories: [scannedRepos[4].repository.fullName], - }); + expect(parsed).toEqual({ + name: "new-repo-list", + repositories: [ + scannedRepos[4].repository.fullName, + scannedRepos[2].repository.fullName, + scannedRepos[0].repository.fullName, + ], }); }); - describe("variantAnalysisReposPanel false", () => { - beforeEach(() => { - jest - .spyOn(config, "isVariantAnalysisReposPanelEnabled") - .mockReturnValue(false); - }); - - it("should be valid JSON when put in object", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - ); - - const text = writeTextStub.mock.calls[0][0]; - const parsed = JSON.parse(`{${text}}`); - - expect(parsed).toEqual({ - "new-repo-list": [ - scannedRepos[4].repository.fullName, - scannedRepos[2].repository.fullName, - scannedRepos[0].repository.fullName, - ], - }); - }); - - it("should use the sort key", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - { - ...defaultFilterSortState, - sortKey: SortKey.ResultsCount, - }, - ); + it("should use the sort key", async () => { + await variantAnalysisManager.copyRepoListToClipboard( + variantAnalysis.id, + { + ...defaultFilterSortState, + sortKey: SortKey.ResultsCount, + }, + ); - const text = writeTextStub.mock.calls[0][0]; + const text = writeTextStub.mock.calls[0][0]; - const parsed = JSON.parse(`{${text}}`); + const parsed = JSON.parse(`${text}`); - expect(parsed).toEqual({ - "new-repo-list": [ - scannedRepos[2].repository.fullName, - scannedRepos[0].repository.fullName, - scannedRepos[4].repository.fullName, - ], - }); + expect(parsed).toEqual({ + name: "new-repo-list", + repositories: [ + scannedRepos[2].repository.fullName, + scannedRepos[0].repository.fullName, + scannedRepos[4].repository.fullName, + ], }); + }); - it("should use the search value", async () => { - await variantAnalysisManager.copyRepoListToClipboard( - variantAnalysis.id, - { - ...defaultFilterSortState, - searchValue: "ban", - }, - ); + it("should use the search value", async () => { + await variantAnalysisManager.copyRepoListToClipboard( + variantAnalysis.id, + { + ...defaultFilterSortState, + searchValue: "ban", + }, + ); - const text = writeTextStub.mock.calls[0][0]; + const text = writeTextStub.mock.calls[0][0]; - const parsed = JSON.parse(`{${text}}`); + const parsed = JSON.parse(`${text}`); - expect(parsed).toEqual({ - "new-repo-list": [scannedRepos[4].repository.fullName], - }); + expect(parsed).toEqual({ + name: "new-repo-list", + repositories: [scannedRepos[4].repository.fullName], }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/variant-analysis/repository-selection.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/variant-analysis/repository-selection.test.ts index 8a623f506f2..4a68f7b66d8 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/variant-analysis/repository-selection.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/variant-analysis/repository-selection.test.ts @@ -1,11 +1,3 @@ -import { QuickPickItem, window } from "vscode"; -import { join } from "path"; -import { DirectoryResult } from "tmp-promise"; -import * as tmp from "tmp-promise"; -import { ensureDir, writeFile, writeJson } from "fs-extra"; -import { UserCancellationException } from "../../../../src/commandRunner"; - -import * as config from "../../../../src/config"; import { getRepositorySelection } from "../../../../src/variant-analysis/repository-selection"; import { DbManager } from "../../../../src/databases/db-manager"; import { @@ -15,450 +7,98 @@ import { } from "../../../../src/databases/db-item"; describe("repository selection", () => { - describe("variantAnalysisReposPanel true", () => { - beforeEach(() => { - jest - .spyOn(config, "isVariantAnalysisReposPanelEnabled") - .mockReturnValue(true); - }); - - it("should throw error when no database item is selected", async () => { - const dbManager = setUpDbManager(undefined); - - await expect(getRepositorySelection(dbManager)).rejects.toThrow( - "Please select a remote database to run the query against.", - ); - }); - - it("should log error when local database item is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.LocalDatabase, - } as DbItem); - - await expect(getRepositorySelection(dbManager)).rejects.toThrow( - "Local databases and lists are not supported yet.", - ); - }); - - it("should log an error when an empty remote user defined list is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.RemoteUserDefinedList, - repos: [] as RemoteRepoDbItem[], - } as DbItem); - - await expect(getRepositorySelection(dbManager)).rejects.toThrow( - "The selected repository list is empty. Please add repositories to it before running a variant analysis.", - ); - }); - - it("should return correct selection when remote system defined list is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.RemoteSystemDefinedList, - listName: "top_10", - } as DbItem); - - const repoSelection = await getRepositorySelection(dbManager); - - expect(repoSelection.repositoryLists).toEqual(["top_10"]); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toBeUndefined(); - }); - - it("should return correct selection when remote user defined list is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.RemoteUserDefinedList, - repos: [ - { repoFullName: "owner1/repo1" }, - { repoFullName: "owner1/repo2" }, - ], - } as DbItem); - - const repoSelection = await getRepositorySelection(dbManager); - - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toEqual([ - "owner1/repo1", - "owner1/repo2", - ]); - }); - - it("should return correct selection when remote owner is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.RemoteOwner, - ownerName: "owner2", - } as DbItem); - - const repoSelection = await getRepositorySelection(dbManager); + it("should throw error when no database item is selected", async () => { + const dbManager = setUpDbManager(undefined); - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toEqual(["owner2"]); - expect(repoSelection.repositories).toBeUndefined(); - }); - - it("should return correct selection when remote repo is selected", async () => { - const dbManager = setUpDbManager({ - kind: DbItemKind.RemoteRepo, - repoFullName: "owner1/repo2", - } as DbItem); - - const repoSelection = await getRepositorySelection(dbManager); - - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toEqual(["owner1/repo2"]); - }); - - function setUpDbManager(response: DbItem | undefined): DbManager { - return { - getSelectedDbItem: jest.fn(() => { - return response; - }), - } as any as DbManager; - } + await expect(getRepositorySelection(dbManager)).rejects.toThrow( + "Please select a remote database to run the query against.", + ); }); - describe("variantAnalysisReposPanel false", () => { - let quickPickSpy: jest.SpiedFunction; - let showInputBoxSpy: jest.SpiedFunction; - - let getRemoteRepositoryListsSpy: jest.SpiedFunction< - typeof config.getRemoteRepositoryLists - >; - let getRemoteRepositoryListsPathSpy: jest.SpiedFunction< - typeof config.getRemoteRepositoryListsPath - >; - - beforeEach(() => { - quickPickSpy = jest - .spyOn(window, "showQuickPick") - .mockResolvedValue(undefined); - showInputBoxSpy = jest - .spyOn(window, "showInputBox") - .mockResolvedValue(undefined); - - getRemoteRepositoryListsSpy = jest - .spyOn(config, "getRemoteRepositoryLists") - .mockReturnValue(undefined); - getRemoteRepositoryListsPathSpy = jest - .spyOn(config, "getRemoteRepositoryListsPath") - .mockReturnValue(undefined); - - jest - .spyOn(config, "isVariantAnalysisReposPanelEnabled") - .mockReturnValue(false); - }); - - describe("repo lists from settings", () => { - it("should allow selection from repo lists from your pre-defined config", async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - repositories: ["foo/bar", "foo/baz"], - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({ - list1: ["foo/bar", "foo/baz"], - list2: [], - }); - - // Make the function call - const repoSelection = await getRepositorySelection(); - - // Check that the return value is correct - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toEqual(["foo/bar", "foo/baz"]); - }); - - it("should return an error for an empty repository list", async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - repositories: [], - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({ - list1: ["foo/bar", "foo/baz"], - list2: [], - }); - - await expect(getRepositorySelection()).rejects.toThrow( - "No repositories selected", - ); - await expect(getRepositorySelection()).rejects.toThrow( - UserCancellationException, - ); - await expect(getRepositorySelection()).rejects.toHaveProperty( - "silent", - false, - ); - }); - }); - - describe("system level repo lists", () => { - it("should allow selection from repo lists defined at the system level", async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - repositoryList: "top_100", - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({ - list1: ["foo/bar", "foo/baz"], - list2: [], - }); - - // Make the function call - const repoSelection = await getRepositorySelection(); + it("should log error when local database item is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.LocalDatabase, + } as DbItem); - // Check that the return value is correct - expect(repoSelection.repositories).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositoryLists).toEqual(["top_100"]); - }); - }); - - describe("custom owner", () => { - // Test the owner regex in various "good" cases - const goodOwners = [ - "owner", - "owner-with-hyphens", - "ownerWithNumbers58", - "owner_with_underscores", - "owner.with.periods.", - ]; - goodOwners.forEach((owner) => { - it(`should run on a valid owner that you enter in the text box: ${owner}`, async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - useAllReposOfOwner: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists - showInputBoxSpy.mockResolvedValue(owner); - - // Make the function call - const repoSelection = await getRepositorySelection(); - - // Check that the return value is correct - expect(repoSelection.repositories).toBeUndefined(); - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toEqual([owner]); - }); - }); - - // Test the owner regex in various "bad" cases - const badOwners = ["invalid&owner", "owner-with-repo/repo"]; - badOwners.forEach((owner) => { - it(`should show an error message if you enter an invalid owner in the text box: ${owner}`, async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - useAllReposOfOwner: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists - showInputBoxSpy.mockResolvedValue(owner); - - // Function call should throw a UserCancellationException - await expect(getRepositorySelection()).rejects.toThrow( - `Invalid user or organization: ${owner}`, - ); - }); - }); - - it("should be ok for the user to change their mind", async () => { - quickPickSpy.mockResolvedValue({ - useAllReposOfOwner: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); - - // The user pressed escape to cancel the operation - showInputBoxSpy.mockResolvedValue(undefined); - - await expect(getRepositorySelection()).rejects.toThrow( - "No repositories selected", - ); - await expect(getRepositorySelection()).rejects.toThrow( - UserCancellationException, - ); - await expect(getRepositorySelection()).rejects.toHaveProperty( - "silent", - true, - ); - }); - }); - - describe("custom repo", () => { - // Test the repo regex in various "good" cases - const goodRepos = [ - "owner/repo", - "owner_with.symbols-/repo.with-symbols_", - "ownerWithNumbers58/repoWithNumbers37", - ]; - goodRepos.forEach((repo) => { - it(`should run on a valid repo that you enter in the text box: ${repo}`, async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - useCustomRepo: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists - showInputBoxSpy.mockResolvedValue(repo); - - // Make the function call - const repoSelection = await getRepositorySelection(); - - // Check that the return value is correct - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toEqual([repo]); - }); - }); - - // Test the repo regex in various "bad" cases - const badRepos = [ - "invalid*owner/repo", - "owner/repo+some&invalid&stuff", - "owner-with-no-repo/", - "/repo-with-no-owner", - ]; - badRepos.forEach((repo) => { - it(`should show an error message if you enter an invalid repo in the text box: ${repo}`, async () => { - // Fake return values - quickPickSpy.mockResolvedValue({ - useCustomRepo: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists - showInputBoxSpy.mockResolvedValue(repo); - - // Function call should throw a UserCancellationException - await expect(getRepositorySelection()).rejects.toThrow( - "Invalid repository format", - ); - await expect(getRepositorySelection()).rejects.toThrow( - UserCancellationException, - ); - }); - }); - - it("should be ok for the user to change their mind", async () => { - quickPickSpy.mockResolvedValue({ - useCustomRepo: true, - } as unknown as QuickPickItem); - getRemoteRepositoryListsSpy.mockReturnValue({}); - - // The user pressed escape to cancel the operation - showInputBoxSpy.mockResolvedValue(undefined); - - await expect(getRepositorySelection()).rejects.toThrow( - "No repositories selected", - ); - await expect(getRepositorySelection()).rejects.toThrow( - UserCancellationException, - ); - await expect(getRepositorySelection()).rejects.toHaveProperty( - "silent", - true, - ); - }); - }); - - describe("external repository lists file", () => { - let directory: DirectoryResult; - - beforeEach(async () => { - directory = await tmp.dir({ - unsafeCleanup: true, - }); - }); - - afterEach(async () => { - await directory.cleanup(); - }); - - it("should fail if path does not exist", async () => { - const nonExistingFile = join(directory.path, "non-existing-file.json"); - getRemoteRepositoryListsPathSpy.mockReturnValue(nonExistingFile); - - await expect(getRepositorySelection()).rejects.toThrow( - `External repository lists file does not exist at ${nonExistingFile}`, - ); - }); - - it("should fail if path points to directory", async () => { - const existingDirectory = join(directory.path, "directory"); - await ensureDir(existingDirectory); - getRemoteRepositoryListsPathSpy.mockReturnValue(existingDirectory); - - await expect(getRepositorySelection()).rejects.toThrow( - "External repository lists path should not point to a directory", - ); - }); + await expect(getRepositorySelection(dbManager)).rejects.toThrow( + "Local databases and lists are not supported yet.", + ); + }); - it("should fail if file does not have valid JSON", async () => { - const existingFile = join(directory.path, "repository-lists.json"); - await writeFile(existingFile, "not-json"); - getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); + it("should log an error when an empty remote user defined list is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.RemoteUserDefinedList, + repos: [] as RemoteRepoDbItem[], + } as DbItem); - await expect(getRepositorySelection()).rejects.toThrow( - "Invalid repository lists file. It should contain valid JSON.", - ); - }); + await expect(getRepositorySelection(dbManager)).rejects.toThrow( + "The selected repository list is empty. Please add repositories to it before running a variant analysis.", + ); + }); - it("should fail if file contains array", async () => { - const existingFile = join(directory.path, "repository-lists.json"); - await writeJson(existingFile, []); - getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); + it("should return correct selection when remote system defined list is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.RemoteSystemDefinedList, + listName: "top_10", + } as DbItem); - await expect(getRepositorySelection()).rejects.toThrow( - "Invalid repository lists file. It should be an object mapping names to a list of repositories.", - ); - }); + const repoSelection = await getRepositorySelection(dbManager); - it("should fail if file does not contain repo lists in the right format", async () => { - const existingFile = join(directory.path, "repository-lists.json"); - const repoLists = { - list1: "owner1/repo1", - }; - await writeJson(existingFile, repoLists); - getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); + expect(repoSelection.repositoryLists).toEqual(["top_10"]); + expect(repoSelection.owners).toBeUndefined(); + expect(repoSelection.repositories).toBeUndefined(); + }); - await expect(getRepositorySelection()).rejects.toThrow( - "Invalid repository lists file. It should contain an array of repositories for each list.", - ); - }); + it("should return correct selection when remote user defined list is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.RemoteUserDefinedList, + repos: [ + { repoFullName: "owner1/repo1" }, + { repoFullName: "owner1/repo2" }, + ], + } as DbItem); + + const repoSelection = await getRepositorySelection(dbManager); + + expect(repoSelection.repositoryLists).toBeUndefined(); + expect(repoSelection.owners).toBeUndefined(); + expect(repoSelection.repositories).toEqual([ + "owner1/repo1", + "owner1/repo2", + ]); + }); - it("should get repo lists from file", async () => { - const existingFile = join(directory.path, "repository-lists.json"); - const repoLists = { - list1: ["owner1/repo1", "owner2/repo2"], - list2: ["owner3/repo3"], - }; - await writeJson(existingFile, repoLists); - getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); - getRemoteRepositoryListsSpy.mockReturnValue({ - list3: ["onwer4/repo4"], - list4: [], - }); + it("should return correct selection when remote owner is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.RemoteOwner, + ownerName: "owner2", + } as DbItem); - quickPickSpy.mockResolvedValue({ - repositories: ["owner3/repo3"], - } as unknown as QuickPickItem); + const repoSelection = await getRepositorySelection(dbManager); - const repoSelection = await getRepositorySelection(); + expect(repoSelection.repositoryLists).toBeUndefined(); + expect(repoSelection.owners).toEqual(["owner2"]); + expect(repoSelection.repositories).toBeUndefined(); + }); - expect(repoSelection.repositoryLists).toBeUndefined(); - expect(repoSelection.owners).toBeUndefined(); - expect(repoSelection.repositories).toEqual(["owner3/repo3"]); - }); - }); + it("should return correct selection when remote repo is selected", async () => { + const dbManager = setUpDbManager({ + kind: DbItemKind.RemoteRepo, + repoFullName: "owner1/repo2", + } as DbItem); - it("should allow the user to cancel", async () => { - // Fake return values - quickPickSpy.mockResolvedValue(undefined); + const repoSelection = await getRepositorySelection(dbManager); - await expect(getRepositorySelection()).rejects.toThrow( - "No repositories selected", - ); - await expect(getRepositorySelection()).rejects.toThrow( - UserCancellationException, - ); - await expect(getRepositorySelection()).rejects.toHaveProperty( - "silent", - true, - ); - }); + expect(repoSelection.repositoryLists).toBeUndefined(); + expect(repoSelection.owners).toBeUndefined(); + expect(repoSelection.repositories).toEqual(["owner1/repo2"]); }); + + function setUpDbManager(response: DbItem | undefined): DbManager { + return { + getSelectedDbItem: jest.fn(() => { + return response; + }), + } as any as DbManager; + } });