Merge branch 'main' into robertbrignull/fix-token-alerts
This commit is contained in:
693
extensions/ql-vscode/package-lock.json
generated
693
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1995,19 +1995,19 @@
|
||||
"@github/markdownlint-github": "^0.6.0",
|
||||
"@octokit/plugin-throttling": "^8.0.0",
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@storybook/addon-a11y": "^8.0.2",
|
||||
"@storybook/addon-actions": "^8.0.2",
|
||||
"@storybook/addon-essentials": "^8.0.2",
|
||||
"@storybook/addon-interactions": "^8.0.2",
|
||||
"@storybook/addon-links": "^8.0.2",
|
||||
"@storybook/addon-a11y": "^8.0.5",
|
||||
"@storybook/addon-actions": "^8.0.5",
|
||||
"@storybook/addon-essentials": "^8.0.5",
|
||||
"@storybook/addon-interactions": "^8.0.5",
|
||||
"@storybook/addon-links": "^8.0.5",
|
||||
"@storybook/blocks": "^8.0.2",
|
||||
"@storybook/components": "^8.0.2",
|
||||
"@storybook/csf": "^0.1.3",
|
||||
"@storybook/icons": "^1.2.9",
|
||||
"@storybook/manager-api": "^8.0.2",
|
||||
"@storybook/react": "^8.0.2",
|
||||
"@storybook/react-vite": "^8.0.2",
|
||||
"@storybook/theming": "^8.0.2",
|
||||
"@storybook/manager-api": "^8.0.5",
|
||||
"@storybook/react": "^8.0.5",
|
||||
"@storybook/react-vite": "^8.0.5",
|
||||
"@storybook/theming": "^8.0.5",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.2",
|
||||
@@ -2027,7 +2027,7 @@
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/sarif": "^2.1.2",
|
||||
"@types/semver": "^7.2.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/stream-json": "^1.7.1",
|
||||
"@types/styled-components": "^5.1.11",
|
||||
"@types/tar-stream": "^3.1.3",
|
||||
@@ -2065,13 +2065,13 @@
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"jest-runner-vscode": "^3.0.1",
|
||||
"lint-staged": "^15.0.2",
|
||||
"lint-staged": "^15.2.2",
|
||||
"markdownlint-cli2": "^0.12.1",
|
||||
"markdownlint-cli2-formatter-pretty": "^0.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"patch-package": "^8.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
"storybook": "^8.0.2",
|
||||
"storybook": "^8.0.5",
|
||||
"tar-stream": "^3.1.7",
|
||||
"through2": "^4.0.2",
|
||||
"ts-jest": "^29.0.1",
|
||||
|
||||
@@ -605,6 +605,11 @@ interface OpenModelAlertsViewMessage {
|
||||
t: "openModelAlertsView";
|
||||
}
|
||||
|
||||
interface RevealInModelAlertsViewMessage {
|
||||
t: "revealInModelAlertsView";
|
||||
modeledMethod: ModeledMethod;
|
||||
}
|
||||
|
||||
interface ModelDependencyMessage {
|
||||
t: "modelDependency";
|
||||
}
|
||||
@@ -677,7 +682,8 @@ export type FromModelEditorMessage =
|
||||
| SetMultipleModeledMethodsMessage
|
||||
| StartModelEvaluationMessage
|
||||
| StopModelEvaluationMessage
|
||||
| OpenModelAlertsViewMessage;
|
||||
| OpenModelAlertsViewMessage
|
||||
| RevealInModelAlertsViewMessage;
|
||||
|
||||
interface RevealInEditorMessage {
|
||||
t: "revealInModelEditor";
|
||||
@@ -746,13 +752,20 @@ interface StopEvaluationRunMessage {
|
||||
t: "stopEvaluationRun";
|
||||
}
|
||||
|
||||
interface RevealModelMessage {
|
||||
t: "revealModel";
|
||||
modeledMethod: ModeledMethod;
|
||||
}
|
||||
|
||||
export type ToModelAlertsMessage =
|
||||
| SetModelAlertsViewStateMessage
|
||||
| SetVariantAnalysisMessage
|
||||
| SetRepoResultsMessage;
|
||||
| SetRepoResultsMessage
|
||||
| RevealModelMessage;
|
||||
|
||||
export type FromModelAlertsMessage =
|
||||
| CommonFromViewMessages
|
||||
| OpenModelPackMessage
|
||||
| OpenActionsLogsMessage
|
||||
| StopEvaluationRunMessage;
|
||||
| StopEvaluationRunMessage
|
||||
| RevealInEditorMessage;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { appendFile, ensureFile } from "fs-extra";
|
||||
import { ensureFile } from "fs-extra";
|
||||
import { open } from "node:fs/promises";
|
||||
import type { FileHandle } from "node:fs/promises";
|
||||
import { isAbsolute } from "path";
|
||||
import { getErrorMessage } from "../helpers-pure";
|
||||
import type { Logger, LogOptions } from "./logger";
|
||||
import type { Disposable } from "../disposable-object";
|
||||
|
||||
/**
|
||||
* An implementation of {@link Logger} that sends the output both to another {@link Logger}
|
||||
@@ -10,9 +13,10 @@ import type { Logger, LogOptions } from "./logger";
|
||||
* The first time a message is written, an additional banner is written to the underlying logger
|
||||
* pointing the user to the "side log" file.
|
||||
*/
|
||||
export class TeeLogger implements Logger {
|
||||
export class TeeLogger implements Logger, Disposable {
|
||||
private emittedRedirectMessage = false;
|
||||
private error = false;
|
||||
private fileHandle: FileHandle | undefined = undefined;
|
||||
|
||||
public constructor(
|
||||
private readonly logger: Logger,
|
||||
@@ -37,11 +41,15 @@ export class TeeLogger implements Logger {
|
||||
|
||||
if (!this.error) {
|
||||
try {
|
||||
const trailingNewline = options.trailingNewline ?? true;
|
||||
await ensureFile(this.location);
|
||||
if (!this.fileHandle) {
|
||||
await ensureFile(this.location);
|
||||
|
||||
await appendFile(
|
||||
this.location,
|
||||
this.fileHandle = await open(this.location, "a");
|
||||
}
|
||||
|
||||
const trailingNewline = options.trailingNewline ?? true;
|
||||
|
||||
await this.fileHandle.appendFile(
|
||||
message + (trailingNewline ? "\n" : ""),
|
||||
{
|
||||
encoding: "utf8",
|
||||
@@ -50,6 +58,14 @@ export class TeeLogger implements Logger {
|
||||
} catch (e) {
|
||||
// Write an error message to the primary log, and stop trying to write to the side log.
|
||||
this.error = true;
|
||||
try {
|
||||
await this.fileHandle?.close();
|
||||
} catch (e) {
|
||||
void this.logger.log(
|
||||
`Failed to close file handle: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
this.fileHandle = undefined;
|
||||
const errorMessage = getErrorMessage(e);
|
||||
await this.logger.log(
|
||||
`Error writing to additional log file: ${errorMessage}`,
|
||||
@@ -65,4 +81,15 @@ export class TeeLogger implements Logger {
|
||||
show(preserveFocus?: boolean): void {
|
||||
this.logger.show(preserveFocus);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
try {
|
||||
void this.fileHandle?.close();
|
||||
} catch (e) {
|
||||
void this.logger.log(
|
||||
`Failed to close file handle: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
this.fileHandle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,8 @@ import { window } from "vscode";
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
|
||||
import { getLanguageDisplayName } from "../../common/query-language";
|
||||
import { downloadGitHubDatabaseFromUrl } from "../database-fetcher";
|
||||
import type { DatabaseFetcher } from "../database-fetcher";
|
||||
import { withProgress } from "../../common/vscode/progress";
|
||||
import type { DatabaseManager } from "../local-databases";
|
||||
import type { CodeQLCliServer } from "../../codeql-cli/cli";
|
||||
import type { AppCommandManager } from "../../common/commands";
|
||||
import type { GitHubDatabaseConfig } from "../../config";
|
||||
import type { CodeqlDatabase } from "./api";
|
||||
@@ -58,9 +56,7 @@ export async function downloadDatabaseFromGitHub(
|
||||
owner: string,
|
||||
repo: string,
|
||||
databases: CodeqlDatabase[],
|
||||
databaseManager: DatabaseManager,
|
||||
storagePath: string,
|
||||
cliServer: CodeQLCliServer,
|
||||
databaseFetcher: DatabaseFetcher,
|
||||
commandManager: AppCommandManager,
|
||||
): Promise<void> {
|
||||
const selectedDatabases = await promptForDatabases(databases);
|
||||
@@ -72,7 +68,7 @@ export async function downloadDatabaseFromGitHub(
|
||||
selectedDatabases.map((database) =>
|
||||
withProgress(
|
||||
async (progress) => {
|
||||
await downloadGitHubDatabaseFromUrl(
|
||||
await databaseFetcher.downloadGitHubDatabaseFromUrl(
|
||||
database.url,
|
||||
database.id,
|
||||
database.created_at,
|
||||
@@ -81,9 +77,6 @@ export async function downloadDatabaseFromGitHub(
|
||||
repo,
|
||||
octokit,
|
||||
progress,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
} from "./download";
|
||||
import type { GitHubDatabaseConfig } from "../../config";
|
||||
import type { DatabaseManager } from "../local-databases";
|
||||
import type { CodeQLCliServer } from "../../codeql-cli/cli";
|
||||
import type { CodeqlDatabase, ListDatabasesResult } from "./api";
|
||||
import { listDatabases } from "./api";
|
||||
import type { DatabaseUpdate } from "./updates";
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
isNewerDatabaseAvailable,
|
||||
} from "./updates";
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
import type { DatabaseFetcher } from "../database-fetcher";
|
||||
|
||||
export class GitHubDatabasesModule extends DisposableObject {
|
||||
/**
|
||||
@@ -33,8 +33,7 @@ export class GitHubDatabasesModule extends DisposableObject {
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly databaseStoragePath: string,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
private readonly config: GitHubDatabaseConfig,
|
||||
) {
|
||||
super();
|
||||
@@ -43,15 +42,13 @@ export class GitHubDatabasesModule extends DisposableObject {
|
||||
public static async initialize(
|
||||
app: App,
|
||||
databaseManager: DatabaseManager,
|
||||
databaseStoragePath: string,
|
||||
cliServer: CodeQLCliServer,
|
||||
databaseFetcher: DatabaseFetcher,
|
||||
config: GitHubDatabaseConfig,
|
||||
): Promise<GitHubDatabasesModule> {
|
||||
const githubDatabasesModule = new GitHubDatabasesModule(
|
||||
app,
|
||||
databaseManager,
|
||||
databaseStoragePath,
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
config,
|
||||
);
|
||||
app.subscriptions.push(githubDatabasesModule);
|
||||
@@ -185,9 +182,7 @@ export class GitHubDatabasesModule extends DisposableObject {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
this.databaseManager,
|
||||
this.databaseStoragePath,
|
||||
this.cliServer,
|
||||
this.databaseFetcher,
|
||||
this.app.commands,
|
||||
);
|
||||
}
|
||||
@@ -212,8 +207,7 @@ export class GitHubDatabasesModule extends DisposableObject {
|
||||
repo,
|
||||
databaseUpdates,
|
||||
this.databaseManager,
|
||||
this.databaseStoragePath,
|
||||
this.cliServer,
|
||||
this.databaseFetcher,
|
||||
this.app.commands,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { CodeqlDatabase } from "./api";
|
||||
import type { DatabaseItem, DatabaseManager } from "../local-databases";
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
import type { CodeQLCliServer } from "../../codeql-cli/cli";
|
||||
import type { AppCommandManager } from "../../common/commands";
|
||||
import { getLanguageDisplayName } from "../../common/query-language";
|
||||
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
|
||||
import { downloadGitHubDatabaseFromUrl } from "../database-fetcher";
|
||||
import type { DatabaseFetcher } from "../database-fetcher";
|
||||
import { withProgress } from "../../common/vscode/progress";
|
||||
import { window } from "vscode";
|
||||
import type { GitHubDatabaseConfig } from "../../config";
|
||||
@@ -156,8 +155,7 @@ export async function downloadDatabaseUpdateFromGitHub(
|
||||
repo: string,
|
||||
updates: DatabaseUpdate[],
|
||||
databaseManager: DatabaseManager,
|
||||
storagePath: string,
|
||||
cliServer: CodeQLCliServer,
|
||||
databaseFetcher: DatabaseFetcher,
|
||||
commandManager: AppCommandManager,
|
||||
): Promise<void> {
|
||||
const selectedDatabases = await promptForDatabases(
|
||||
@@ -179,21 +177,19 @@ export async function downloadDatabaseUpdateFromGitHub(
|
||||
|
||||
return withProgress(
|
||||
async (progress) => {
|
||||
const newDatabase = await downloadGitHubDatabaseFromUrl(
|
||||
database.url,
|
||||
database.id,
|
||||
database.created_at,
|
||||
database.commit_oid ?? null,
|
||||
owner,
|
||||
repo,
|
||||
octokit,
|
||||
progress,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
databaseManager.currentDatabaseItem === update.databaseItem,
|
||||
update.databaseItem.hasSourceArchiveInExplorer(),
|
||||
);
|
||||
const newDatabase =
|
||||
await databaseFetcher.downloadGitHubDatabaseFromUrl(
|
||||
database.url,
|
||||
database.id,
|
||||
database.created_at,
|
||||
database.commit_oid ?? null,
|
||||
owner,
|
||||
repo,
|
||||
octokit,
|
||||
progress,
|
||||
databaseManager.currentDatabaseItem === update.databaseItem,
|
||||
update.databaseItem.hasSourceArchiveInExplorer(),
|
||||
);
|
||||
if (newDatabase === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,11 +37,7 @@ import {
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogErrorMessage,
|
||||
} from "../common/logging";
|
||||
import {
|
||||
importLocalDatabase,
|
||||
promptImportGithubDatabase,
|
||||
promptImportInternetDatabase,
|
||||
} from "./database-fetcher";
|
||||
import type { DatabaseFetcher } from "./database-fetcher";
|
||||
import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure";
|
||||
import type { QueryRunner } from "../query-server";
|
||||
import type { App } from "../common/app";
|
||||
@@ -248,6 +244,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
public constructor(
|
||||
private app: App,
|
||||
private databaseManager: DatabaseManager,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
languageContext: LanguageContextStore,
|
||||
private readonly queryServer: QueryRunner,
|
||||
private readonly storagePath: string,
|
||||
@@ -535,13 +532,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
private async handleChooseDatabaseInternet(): Promise<void> {
|
||||
return withProgress(
|
||||
async (progress) => {
|
||||
await promptImportInternetDatabase(
|
||||
this.app.commands,
|
||||
this.databaseManager,
|
||||
this.storagePath,
|
||||
progress,
|
||||
this.queryServer.cliServer,
|
||||
);
|
||||
await this.databaseFetcher.promptImportInternetDatabase(progress);
|
||||
},
|
||||
{
|
||||
title: "Adding database from URL",
|
||||
@@ -552,13 +543,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
private async handleChooseDatabaseGithub(): Promise<void> {
|
||||
return withProgress(
|
||||
async (progress) => {
|
||||
await promptImportGithubDatabase(
|
||||
this.app,
|
||||
this.databaseManager,
|
||||
this.storagePath,
|
||||
progress,
|
||||
this.queryServer.cliServer,
|
||||
);
|
||||
await this.databaseFetcher.promptImportGithubDatabase(progress);
|
||||
},
|
||||
{
|
||||
title: "Adding database from GitHub",
|
||||
@@ -705,13 +690,9 @@ export class DatabaseUI extends DisposableObject {
|
||||
try {
|
||||
// Assume user has selected an archive if the file has a .zip extension
|
||||
if (uri.path.endsWith(".zip")) {
|
||||
await importLocalDatabase(
|
||||
this.app.commands,
|
||||
await this.databaseFetcher.importLocalDatabase(
|
||||
uri.toString(true),
|
||||
this.databaseManager,
|
||||
this.storagePath,
|
||||
progress,
|
||||
this.queryServer.cliServer,
|
||||
);
|
||||
} else {
|
||||
await this.databaseManager.openDatabase(uri, {
|
||||
@@ -756,13 +737,9 @@ export class DatabaseUI extends DisposableObject {
|
||||
await this.databaseManager.removeDatabaseItem(existingItem);
|
||||
}
|
||||
|
||||
await importLocalDatabase(
|
||||
this.app.commands,
|
||||
await this.databaseFetcher.importLocalDatabase(
|
||||
uri.toString(true),
|
||||
this.databaseManager,
|
||||
this.storagePath,
|
||||
progress,
|
||||
this.queryServer.cliServer,
|
||||
);
|
||||
|
||||
if (existingItem !== undefined) {
|
||||
@@ -1003,13 +980,9 @@ export class DatabaseUI extends DisposableObject {
|
||||
// we are selecting a database archive or a testproj.
|
||||
// Unzip archives (if an archive) and copy into a workspace-controlled area
|
||||
// before importing.
|
||||
return await importLocalDatabase(
|
||||
this.app.commands,
|
||||
return await this.databaseFetcher.importLocalDatabase(
|
||||
uri.toString(true),
|
||||
this.databaseManager,
|
||||
this.storagePath,
|
||||
progress,
|
||||
this.queryServer.cliServer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { pathExists, remove } from "fs-extra";
|
||||
import { join } from "path";
|
||||
import type { Uri } from "vscode";
|
||||
import { zip } from "zip-a-folder";
|
||||
|
||||
/**
|
||||
* The layout of the database.
|
||||
@@ -28,3 +31,26 @@ export interface DatabaseContents {
|
||||
export interface DatabaseContentsWithDbScheme extends DatabaseContents {
|
||||
dbSchemeUri: Uri; // Always present
|
||||
}
|
||||
|
||||
/**
|
||||
* Databases created by the old odasa tool will not have a zipped
|
||||
* source location. However, this extension works better if sources
|
||||
* are zipped.
|
||||
*
|
||||
* This function ensures that the source location is zipped. If the
|
||||
* `src` folder exists and the `src.zip` file does not, the `src`
|
||||
* folder will be zipped and then deleted.
|
||||
*
|
||||
* @param databasePath The full path to the unzipped database
|
||||
*/
|
||||
export async function ensureZippedSourceLocation(
|
||||
databasePath: string,
|
||||
): Promise<void> {
|
||||
const srcFolderPath = join(databasePath, "src");
|
||||
const srcZipPath = `${srcFolderPath}.zip`;
|
||||
|
||||
if ((await pathExists(srcFolderPath)) && !(await pathExists(srcZipPath))) {
|
||||
await zip(srcFolderPath, srcZipPath);
|
||||
await remove(srcFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ import { DatabaseResolver } from "./database-resolver";
|
||||
import { telemetryListener } from "../../common/vscode/telemetry";
|
||||
import type { LanguageContextStore } from "../../language-context-store";
|
||||
import type { DatabaseOrigin } from "./database-origin";
|
||||
import { ensureZippedSourceLocation } from "../database-fetcher";
|
||||
import { ensureZippedSourceLocation } from "./database-contents";
|
||||
|
||||
/**
|
||||
* The name of the key in the workspaceState dictionary in which we
|
||||
|
||||
@@ -133,6 +133,7 @@ import { OpenReferencedFileCodeLensProvider } from "./local-queries/open-referen
|
||||
import { LanguageContextStore } from "./language-context-store";
|
||||
import { LanguageSelectionPanel } from "./language-selection-panel/language-selection-panel";
|
||||
import { GitHubDatabasesModule } from "./databases/github-databases";
|
||||
import { DatabaseFetcher } from "./databases/database-fetcher";
|
||||
|
||||
/**
|
||||
* extension.ts
|
||||
@@ -799,12 +800,20 @@ async function activateWithInstalledDistribution(
|
||||
// Let this run async.
|
||||
void dbm.loadPersistedState();
|
||||
|
||||
const databaseFetcher = new DatabaseFetcher(
|
||||
app,
|
||||
dbm,
|
||||
getContextStoragePath(ctx),
|
||||
cliServer,
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(dbm);
|
||||
|
||||
void extLogger.log("Initializing database panel.");
|
||||
const databaseUI = new DatabaseUI(
|
||||
app,
|
||||
dbm,
|
||||
databaseFetcher,
|
||||
languageContext,
|
||||
qs,
|
||||
getContextStoragePath(ctx),
|
||||
@@ -881,8 +890,7 @@ async function activateWithInstalledDistribution(
|
||||
await GitHubDatabasesModule.initialize(
|
||||
app,
|
||||
dbm,
|
||||
getContextStoragePath(ctx),
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
githubDatabaseConfigListener,
|
||||
);
|
||||
|
||||
@@ -953,6 +961,7 @@ async function activateWithInstalledDistribution(
|
||||
qs,
|
||||
qhm,
|
||||
dbm,
|
||||
databaseFetcher,
|
||||
cliServer,
|
||||
databaseUI,
|
||||
localQueryResultsView,
|
||||
@@ -977,6 +986,7 @@ async function activateWithInstalledDistribution(
|
||||
const modelEditorModule = await ModelEditorModule.initialize(
|
||||
app,
|
||||
dbm,
|
||||
databaseFetcher,
|
||||
variantAnalysisManager,
|
||||
cliServer,
|
||||
qs,
|
||||
|
||||
@@ -92,11 +92,12 @@ export async function runContextualQuery(
|
||||
void extLogger.log(
|
||||
`Running contextual query ${query}; results will be stored in ${queryRun.outputDir.querySaveDir}`,
|
||||
);
|
||||
const results = await queryRun.evaluate(
|
||||
progress,
|
||||
token,
|
||||
new TeeLogger(qs.logger, queryRun.outputDir.logPath),
|
||||
);
|
||||
await cleanup?.();
|
||||
return results;
|
||||
const teeLogger = new TeeLogger(qs.logger, queryRun.outputDir.logPath);
|
||||
|
||||
try {
|
||||
return await queryRun.evaluate(progress, token, teeLogger);
|
||||
} finally {
|
||||
await cleanup?.();
|
||||
teeLogger.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
|
||||
import { tryGetQueryLanguage } from "../common/query-language";
|
||||
import type { LanguageContextStore } from "../language-context-store";
|
||||
import type { ExtensionApp } from "../common/vscode/vscode-app";
|
||||
import type { DatabaseFetcher } from "../databases/database-fetcher";
|
||||
|
||||
export enum QuickEvalType {
|
||||
None,
|
||||
@@ -69,6 +70,7 @@ export class LocalQueries extends DisposableObject {
|
||||
private readonly queryRunner: QueryRunner,
|
||||
private readonly queryHistoryManager: QueryHistoryManager,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly databaseUI: DatabaseUI,
|
||||
private readonly localQueryResultsView: ResultsView,
|
||||
@@ -319,15 +321,13 @@ export class LocalQueries extends DisposableObject {
|
||||
private async createSkeletonQuery(): Promise<void> {
|
||||
await withProgress(
|
||||
async (progress: ProgressCallback) => {
|
||||
const contextStoragePath =
|
||||
this.app.workspaceStoragePath || this.app.globalStoragePath;
|
||||
const language = this.languageContextStore.selectedLanguage;
|
||||
const skeletonQueryWizard = new SkeletonQueryWizard(
|
||||
this.cliServer,
|
||||
progress,
|
||||
this.app,
|
||||
this.databaseManager,
|
||||
contextStoragePath,
|
||||
this.databaseFetcher,
|
||||
this.selectedQueryTreeViewItems,
|
||||
language,
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ import { redactableError } from "../common/errors";
|
||||
import type { LocalQueries } from "./local-queries";
|
||||
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
||||
import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import type { Disposable } from "../common/disposable-object";
|
||||
|
||||
function formatResultMessage(result: CoreQueryResults): string {
|
||||
switch (result.resultType) {
|
||||
@@ -61,7 +62,11 @@ export class LocalQueryRun {
|
||||
private readonly localQueries: LocalQueries,
|
||||
private readonly queryInfo: LocalQueryInfo,
|
||||
private readonly dbItem: DatabaseItem,
|
||||
public readonly logger: Logger, // Public so that other clients, like the debug adapter, know where to send log output
|
||||
/**
|
||||
* The logger is only available while the query is running and will be disposed of when the
|
||||
* query completes.
|
||||
*/
|
||||
public readonly logger: Logger & Disposable, // Public so that other clients, like the debug adapter, know where to send log output
|
||||
private readonly queryHistoryManager: QueryHistoryManager,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
) {}
|
||||
@@ -92,6 +97,8 @@ export class LocalQueryRun {
|
||||
// Note we must update the query history view after showing results as the
|
||||
// display and sorting might depend on the number of results
|
||||
await this.queryHistoryManager.refreshTreeView();
|
||||
|
||||
this.logger.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,6 +117,8 @@ export class LocalQueryRun {
|
||||
err.message = `Error running query: ${err.message}`;
|
||||
this.queryInfo.failureReason = err.message;
|
||||
await this.queryHistoryManager.refreshTreeView();
|
||||
|
||||
this.logger.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,21 +47,27 @@ export async function runQuery({
|
||||
undefined,
|
||||
);
|
||||
|
||||
const completedQuery = await queryRun.evaluate(
|
||||
progress,
|
||||
token,
|
||||
new TeeLogger(queryRunner.logger, queryRun.outputDir.logPath),
|
||||
const teeLogger = new TeeLogger(
|
||||
queryRunner.logger,
|
||||
queryRun.outputDir.logPath,
|
||||
);
|
||||
|
||||
if (completedQuery.resultType !== QueryResultType.SUCCESS) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
extLogger,
|
||||
telemetryListener,
|
||||
redactableError`Failed to run ${basename(queryPath)} query: ${
|
||||
completedQuery.message ?? "No message"
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
try {
|
||||
const completedQuery = await queryRun.evaluate(progress, token, teeLogger);
|
||||
|
||||
if (completedQuery.resultType !== QueryResultType.SUCCESS) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
extLogger,
|
||||
telemetryListener,
|
||||
redactableError`Failed to run ${basename(queryPath)} query: ${
|
||||
completedQuery.message ?? "No message"
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return completedQuery;
|
||||
} finally {
|
||||
teeLogger.dispose();
|
||||
}
|
||||
return completedQuery;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ import {
|
||||
UserCancellationException,
|
||||
withProgress,
|
||||
} from "../common/vscode/progress";
|
||||
import {
|
||||
askForGitHubRepo,
|
||||
downloadGitHubDatabase,
|
||||
} from "../databases/database-fetcher";
|
||||
import type { DatabaseFetcher } from "../databases/database-fetcher";
|
||||
import {
|
||||
getQlPackLocation,
|
||||
isCodespacesTemplate,
|
||||
@@ -62,7 +59,7 @@ export class SkeletonQueryWizard {
|
||||
private readonly progress: ProgressCallback,
|
||||
private readonly app: App,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly databaseStoragePath: string | undefined,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
private readonly selectedItems: readonly QueryTreeViewItem[],
|
||||
private language: QueryLanguage | undefined = undefined,
|
||||
) {}
|
||||
@@ -363,10 +360,6 @@ export class SkeletonQueryWizard {
|
||||
}
|
||||
|
||||
private async downloadDatabase(progress: ProgressCallback) {
|
||||
if (this.databaseStoragePath === undefined) {
|
||||
throw new Error("Database storage path is undefined");
|
||||
}
|
||||
|
||||
if (this.language === undefined) {
|
||||
throw new Error("Language is undefined");
|
||||
}
|
||||
@@ -378,20 +371,10 @@ export class SkeletonQueryWizard {
|
||||
});
|
||||
|
||||
const githubRepoNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language];
|
||||
const chosenRepo = await askForGitHubRepo(undefined, githubRepoNwo);
|
||||
|
||||
if (!chosenRepo) {
|
||||
throw new UserCancellationException("No GitHub repository provided");
|
||||
}
|
||||
|
||||
await downloadGitHubDatabase(
|
||||
chosenRepo,
|
||||
this.app,
|
||||
this.databaseManager,
|
||||
this.databaseStoragePath,
|
||||
await this.databaseFetcher.promptImportGithubDatabase(
|
||||
progress,
|
||||
this.cliServer,
|
||||
this.language,
|
||||
githubRepoNwo,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import type {
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
} from "../../variant-analysis/shared/variant-analysis";
|
||||
import type { AppEvent, AppEventEmitter } from "../../common/events";
|
||||
import type { ModeledMethod } from "../modeled-method";
|
||||
import type { MethodSignature } from "../method";
|
||||
|
||||
export class ModelAlertsView extends AbstractWebview<
|
||||
ToModelAlertsMessage,
|
||||
@@ -56,7 +58,7 @@ export class ModelAlertsView extends AbstractWebview<
|
||||
|
||||
await this.waitForPanelLoaded();
|
||||
await this.setViewState();
|
||||
await this.updateReposResults(reposResults);
|
||||
await this.setReposResults(reposResults);
|
||||
}
|
||||
|
||||
protected async getPanelConfig(): Promise<WebviewPanelConfig> {
|
||||
@@ -102,6 +104,9 @@ export class ModelAlertsView extends AbstractWebview<
|
||||
case "stopEvaluationRun":
|
||||
await this.stopEvaluationRun();
|
||||
break;
|
||||
case "revealInModelEditor":
|
||||
await this.revealInModelEditor(msg.method);
|
||||
break;
|
||||
default:
|
||||
assertNever(msg);
|
||||
}
|
||||
@@ -116,7 +121,7 @@ export class ModelAlertsView extends AbstractWebview<
|
||||
});
|
||||
}
|
||||
|
||||
public async updateVariantAnalysis(
|
||||
public async setVariantAnalysis(
|
||||
variantAnalysis: VariantAnalysis,
|
||||
): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
@@ -129,20 +134,7 @@ export class ModelAlertsView extends AbstractWebview<
|
||||
});
|
||||
}
|
||||
|
||||
public async updateRepoResults(
|
||||
repositoryResult: VariantAnalysisScannedRepositoryResult,
|
||||
): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.postMessage({
|
||||
t: "setRepoResults",
|
||||
repoResults: [repositoryResult],
|
||||
});
|
||||
}
|
||||
|
||||
public async updateReposResults(
|
||||
public async setReposResults(
|
||||
repoResults: VariantAnalysisScannedRepositoryResult[],
|
||||
): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
@@ -175,9 +167,39 @@ export class ModelAlertsView extends AbstractWebview<
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
this.push(
|
||||
this.modelingEvents.onRevealInModelAlertsView(async (event) => {
|
||||
if (event.dbUri === this.dbItem.databaseUri.toString()) {
|
||||
await this.revealMethod(event.modeledMethod);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async stopEvaluationRun() {
|
||||
this.onEvaluationRunStopClickedEventEmitter.fire();
|
||||
}
|
||||
|
||||
private async revealInModelEditor(method: MethodSignature): Promise<void> {
|
||||
if (!this.dbItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.modelingEvents.fireRevealInModelEditorEvent(
|
||||
this.dbItem.databaseUri.toString(),
|
||||
method,
|
||||
);
|
||||
}
|
||||
|
||||
private async revealMethod(method: ModeledMethod): Promise<void> {
|
||||
const panel = await this.getPanel();
|
||||
|
||||
panel?.reveal();
|
||||
|
||||
await this.postMessage({
|
||||
t: "revealModel",
|
||||
modeledMethod: method,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import { INITIAL_MODE } from "./shared/mode";
|
||||
import { isSupportedLanguage } from "./supported-languages";
|
||||
import { DefaultNotifier, checkConsistency } from "./consistency-check";
|
||||
import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager";
|
||||
import type { DatabaseFetcher } from "../databases/database-fetcher";
|
||||
|
||||
export class ModelEditorModule extends DisposableObject {
|
||||
private readonly queryStorageDir: string;
|
||||
@@ -42,6 +43,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
private constructor(
|
||||
private readonly app: App,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
private readonly variantAnalysisManager: VariantAnalysisManager,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly queryRunner: QueryRunner,
|
||||
@@ -65,6 +67,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
public static async initialize(
|
||||
app: App,
|
||||
databaseManager: DatabaseManager,
|
||||
databaseFetcher: DatabaseFetcher,
|
||||
variantAnalysisManager: VariantAnalysisManager,
|
||||
cliServer: CodeQLCliServer,
|
||||
queryRunner: QueryRunner,
|
||||
@@ -73,6 +76,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
const modelEditorModule = new ModelEditorModule(
|
||||
app,
|
||||
databaseManager,
|
||||
databaseFetcher,
|
||||
variantAnalysisManager,
|
||||
cliServer,
|
||||
queryRunner,
|
||||
@@ -236,6 +240,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
this.modelingEvents,
|
||||
this.modelConfig,
|
||||
this.databaseManager,
|
||||
this.databaseFetcher,
|
||||
this.variantAnalysisManager,
|
||||
this.cliServer,
|
||||
this.queryRunner,
|
||||
|
||||
@@ -29,7 +29,7 @@ import type {
|
||||
} from "../databases/local-databases";
|
||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { asError, assertNever, getErrorMessage } from "../common/helpers-pure";
|
||||
import { promptImportGithubDatabase } from "../databases/database-fetcher";
|
||||
import type { DatabaseFetcher } from "../databases/database-fetcher";
|
||||
import type { App } from "../common/app";
|
||||
import { redactableError } from "../common/errors";
|
||||
import {
|
||||
@@ -86,6 +86,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
private readonly modelingEvents: ModelingEvents,
|
||||
private readonly modelConfig: ModelConfigListener,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly databaseFetcher: DatabaseFetcher,
|
||||
private readonly variantAnalysisManager: VariantAnalysisManager,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly queryRunner: QueryRunner,
|
||||
@@ -384,6 +385,9 @@ export class ModelEditorView extends AbstractWebview<
|
||||
case "openModelAlertsView":
|
||||
await this.modelEvaluator.openModelAlertsView();
|
||||
break;
|
||||
case "revealInModelAlertsView":
|
||||
await this.modelEvaluator.revealInModelAlertsView(msg.modeledMethod);
|
||||
break;
|
||||
case "telemetry":
|
||||
telemetryListener?.sendUIInteraction(msg.action);
|
||||
break;
|
||||
@@ -850,6 +854,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
this.modelingEvents,
|
||||
this.modelConfig,
|
||||
this.databaseManager,
|
||||
this.databaseFetcher,
|
||||
this.variantAnalysisManager,
|
||||
this.cliServer,
|
||||
this.queryRunner,
|
||||
@@ -920,13 +925,10 @@ export class ModelEditorView extends AbstractWebview<
|
||||
// the user to import the library database. We need to have the database
|
||||
// imported to the query server, so we need to register it to our workspace.
|
||||
const makeSelected = false;
|
||||
const addedDatabase = await promptImportGithubDatabase(
|
||||
this.app,
|
||||
this.databaseManager,
|
||||
this.app.workspaceStoragePath ?? this.app.globalStoragePath,
|
||||
const addedDatabase = await this.databaseFetcher.promptImportGithubDatabase(
|
||||
progress,
|
||||
this.cliServer,
|
||||
this.databaseItem.language,
|
||||
undefined,
|
||||
makeSelected,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -21,6 +21,7 @@ import type { QlPackDetails } from "../variant-analysis/ql-pack-details";
|
||||
import type { App } from "../common/app";
|
||||
import { ModelAlertsView } from "./model-alerts/model-alerts-view";
|
||||
import type { ExtensionPack } from "./shared/extension-pack";
|
||||
import type { ModeledMethod } from "./modeled-method";
|
||||
|
||||
export class ModelEvaluator extends DisposableObject {
|
||||
// Cancellation token source to allow cancelling of the current run
|
||||
@@ -154,10 +155,20 @@ export class ModelEvaluator extends DisposableObject {
|
||||
);
|
||||
await this.modelAlertsView.showView(reposResults);
|
||||
|
||||
await this.modelAlertsView.updateVariantAnalysis(variantAnalysis);
|
||||
await this.modelAlertsView.setVariantAnalysis(variantAnalysis);
|
||||
}
|
||||
}
|
||||
|
||||
public async revealInModelAlertsView(modeledMethod: ModeledMethod) {
|
||||
if (!this.modelingStore.isModelAlertsViewOpen(this.dbItem)) {
|
||||
await this.openModelAlertsView();
|
||||
}
|
||||
this.modelingEvents.fireRevealInModelAlertsViewEvent(
|
||||
this.dbItem.databaseUri.toString(),
|
||||
modeledMethod,
|
||||
);
|
||||
}
|
||||
|
||||
private registerToModelingEvents() {
|
||||
this.push(
|
||||
this.modelingEvents.onModelEvaluationRunChanged(async (event) => {
|
||||
@@ -260,7 +271,7 @@ export class ModelEvaluator extends DisposableObject {
|
||||
});
|
||||
|
||||
// Update model alerts view
|
||||
await this.modelAlertsView?.updateVariantAnalysis(variantAnalysis);
|
||||
await this.modelAlertsView?.setVariantAnalysis(variantAnalysis);
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -284,7 +295,7 @@ export class ModelEvaluator extends DisposableObject {
|
||||
this.push(
|
||||
this.variantAnalysisManager.onRepoResultsLoaded(async (e) => {
|
||||
if (e.variantAnalysisId === variantAnalysisId) {
|
||||
await this.modelAlertsView?.updateRepoResults(e);
|
||||
await this.modelAlertsView?.setReposResults([e]);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { assertNever } from "../common/helpers-pure";
|
||||
import type { MethodSignature } from "./method";
|
||||
import type { ModelingStatus } from "./shared/modeling-status";
|
||||
|
||||
@@ -169,3 +170,86 @@ export function calculateNewProvenance(
|
||||
return "manual";
|
||||
}
|
||||
}
|
||||
|
||||
export function createModeledMethodKey(modeledMethod: ModeledMethod): string {
|
||||
const canonicalModeledMethod = canonicalizeModeledMethod(modeledMethod);
|
||||
return JSON.stringify(
|
||||
canonicalModeledMethod,
|
||||
// This ensures the keys are always in the same order
|
||||
Object.keys(canonicalModeledMethod).sort(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will reset any properties which are not used for the specific type of modeled method.
|
||||
*
|
||||
* It will also set the `provenance` to `manual` since multiple modelings of the same method with a
|
||||
* different provenance are not actually different.
|
||||
*
|
||||
* The returned canonical modeled method should only be used for comparisons. It should not be used
|
||||
* for display purposes, saving the model, or any other purpose which requires the original modeled
|
||||
* method to be preserved.
|
||||
*
|
||||
* @param modeledMethod The modeled method to canonicalize
|
||||
*/
|
||||
function canonicalizeModeledMethod(
|
||||
modeledMethod: ModeledMethod,
|
||||
): ModeledMethod {
|
||||
const methodSignature: MethodSignature = {
|
||||
endpointType: modeledMethod.endpointType,
|
||||
signature: modeledMethod.signature,
|
||||
packageName: modeledMethod.packageName,
|
||||
typeName: modeledMethod.typeName,
|
||||
methodName: modeledMethod.methodName,
|
||||
methodParameters: modeledMethod.methodParameters,
|
||||
};
|
||||
|
||||
switch (modeledMethod.type) {
|
||||
case "none":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "none",
|
||||
};
|
||||
case "source":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "source",
|
||||
output: modeledMethod.output,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "sink":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "sink",
|
||||
input: modeledMethod.input,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "summary":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "summary",
|
||||
input: modeledMethod.input,
|
||||
output: modeledMethod.output,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "neutral":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "neutral",
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "type":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "type",
|
||||
relatedTypeName: modeledMethod.relatedTypeName,
|
||||
path: modeledMethod.path,
|
||||
};
|
||||
default:
|
||||
assertNever(modeledMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ interface FocusModelAlertsViewEvent {
|
||||
dbUri: string;
|
||||
}
|
||||
|
||||
interface RevealInModelAlertsViewEvent {
|
||||
dbUri: string;
|
||||
modeledMethod: ModeledMethod;
|
||||
}
|
||||
|
||||
export class ModelingEvents extends DisposableObject {
|
||||
public readonly onActiveDbChanged: AppEvent<void>;
|
||||
public readonly onDbOpened: AppEvent<DatabaseItem>;
|
||||
@@ -84,6 +89,7 @@ export class ModelingEvents extends DisposableObject {
|
||||
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
|
||||
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
|
||||
public readonly onFocusModelAlertsView: AppEvent<FocusModelAlertsViewEvent>;
|
||||
public readonly onRevealInModelAlertsView: AppEvent<RevealInModelAlertsViewEvent>;
|
||||
|
||||
private readonly onActiveDbChangedEventEmitter: AppEventEmitter<void>;
|
||||
private readonly onDbOpenedEventEmitter: AppEventEmitter<DatabaseItem>;
|
||||
@@ -99,6 +105,7 @@ export class ModelingEvents extends DisposableObject {
|
||||
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
|
||||
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
|
||||
private readonly onFocusModelAlertsViewEventEmitter: AppEventEmitter<FocusModelAlertsViewEvent>;
|
||||
private readonly onRevealInModelAlertsViewEventEmitter: AppEventEmitter<RevealInModelAlertsViewEvent>;
|
||||
|
||||
constructor(app: App) {
|
||||
super();
|
||||
@@ -176,6 +183,12 @@ export class ModelingEvents extends DisposableObject {
|
||||
app.createEventEmitter<FocusModelAlertsViewEvent>(),
|
||||
);
|
||||
this.onFocusModelAlertsView = this.onFocusModelAlertsViewEventEmitter.event;
|
||||
|
||||
this.onRevealInModelAlertsViewEventEmitter = this.push(
|
||||
app.createEventEmitter<RevealInModelAlertsViewEvent>(),
|
||||
);
|
||||
this.onRevealInModelAlertsView =
|
||||
this.onRevealInModelAlertsViewEventEmitter.event;
|
||||
}
|
||||
|
||||
public fireActiveDbChangedEvent() {
|
||||
@@ -301,4 +314,11 @@ export class ModelingEvents extends DisposableObject {
|
||||
public fireFocusModelAlertsViewEvent(dbUri: string) {
|
||||
this.onFocusModelAlertsViewEventEmitter.fire({ dbUri });
|
||||
}
|
||||
|
||||
public fireRevealInModelAlertsViewEvent(
|
||||
dbUri: string,
|
||||
modeledMethod: ModeledMethod,
|
||||
) {
|
||||
this.onRevealInModelAlertsViewEventEmitter.fire({ dbUri, modeledMethod });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createModeledMethodKey } from "../modeled-method";
|
||||
import type { ModeledMethod, NeutralModeledMethod } from "../modeled-method";
|
||||
import type { MethodSignature } from "../method";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
|
||||
export type ModeledMethodValidationError = {
|
||||
title: string;
|
||||
@@ -9,80 +8,6 @@ export type ModeledMethodValidationError = {
|
||||
index: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will reset any properties which are not used for the specific type of modeled method.
|
||||
*
|
||||
* It will also set the `provenance` to `manual` since multiple modelings of the same method with a
|
||||
* different provenance are not actually different.
|
||||
*
|
||||
* The returned canonical modeled method should only be used for comparisons. It should not be used
|
||||
* for display purposes, saving the model, or any other purpose which requires the original modeled
|
||||
* method to be preserved.
|
||||
*
|
||||
* @param modeledMethod The modeled method to canonicalize
|
||||
*/
|
||||
function canonicalizeModeledMethod(
|
||||
modeledMethod: ModeledMethod,
|
||||
): ModeledMethod {
|
||||
const methodSignature: MethodSignature = {
|
||||
endpointType: modeledMethod.endpointType,
|
||||
signature: modeledMethod.signature,
|
||||
packageName: modeledMethod.packageName,
|
||||
typeName: modeledMethod.typeName,
|
||||
methodName: modeledMethod.methodName,
|
||||
methodParameters: modeledMethod.methodParameters,
|
||||
};
|
||||
|
||||
switch (modeledMethod.type) {
|
||||
case "none":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "none",
|
||||
};
|
||||
case "source":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "source",
|
||||
output: modeledMethod.output,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "sink":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "sink",
|
||||
input: modeledMethod.input,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "summary":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "summary",
|
||||
input: modeledMethod.input,
|
||||
output: modeledMethod.output,
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "neutral":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "neutral",
|
||||
kind: modeledMethod.kind,
|
||||
provenance: "manual",
|
||||
};
|
||||
case "type":
|
||||
return {
|
||||
...methodSignature,
|
||||
type: "type",
|
||||
relatedTypeName: modeledMethod.relatedTypeName,
|
||||
path: modeledMethod.path,
|
||||
};
|
||||
default:
|
||||
assertNever(modeledMethod);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateModeledMethods(
|
||||
modeledMethods: ModeledMethod[],
|
||||
): ModeledMethodValidationError[] {
|
||||
@@ -97,12 +22,7 @@ export function validateModeledMethods(
|
||||
// an error for any duplicates.
|
||||
const seenModeledMethods = new Set<string>();
|
||||
for (const modeledMethod of consideredModeledMethods) {
|
||||
const canonicalModeledMethod = canonicalizeModeledMethod(modeledMethod);
|
||||
const key = JSON.stringify(
|
||||
canonicalModeledMethod,
|
||||
// This ensures the keys are always in the same order
|
||||
Object.keys(canonicalModeledMethod).sort(),
|
||||
);
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
if (seenModeledMethods.has(key)) {
|
||||
result.push({
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
filterAndSort,
|
||||
} from "../../model-editor/shared/model-alerts-filter-sort";
|
||||
import type { ModelAlertsFilterSortState } from "../../model-editor/shared/model-alerts-filter-sort";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
|
||||
type Props = {
|
||||
initialViewState?: ModelAlertsViewState;
|
||||
@@ -62,6 +63,10 @@ export function ModelAlerts({
|
||||
const [filterSortValue, setFilterSortValue] =
|
||||
useState<ModelAlertsFilterSortState>(defaultFilterSortState);
|
||||
|
||||
const [revealedModel, setRevealedModel] = useState<ModeledMethod | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (evt: MessageEvent) => {
|
||||
if (evt.origin === window.origin) {
|
||||
@@ -87,6 +92,10 @@ export function ModelAlerts({
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "revealModel": {
|
||||
setRevealedModel(msg.modeledMethod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// sanitize origin
|
||||
@@ -146,7 +155,11 @@ export function ModelAlerts({
|
||||
// but we don't have a unique identifier for models. In the future,
|
||||
// we may need to consider coming up with unique identifiers for models
|
||||
// and using those as keys.
|
||||
<ModelAlertsResults key={i} modelAlerts={alerts} />
|
||||
<ModelAlertsResults
|
||||
key={i}
|
||||
modelAlerts={alerts}
|
||||
revealedModel={revealedModel}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { styled } from "styled-components";
|
||||
import type { ModelAlerts } from "../../model-editor/model-alerts/model-alerts";
|
||||
import { Codicon } from "../common";
|
||||
import { useState } from "react";
|
||||
import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react";
|
||||
import { VSCodeBadge, VSCodeLink } from "@vscode/webview-ui-toolkit/react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { formatDecimal } from "../../common/number";
|
||||
import AnalysisAlertResult from "../variant-analysis/AnalysisAlertResult";
|
||||
import { MethodName } from "../model-editor/MethodName";
|
||||
import { ModelDetails } from "./ModelDetails";
|
||||
import { vscode } from "../vscode-api";
|
||||
import { createModeledMethodKey } from "../../model-editor/modeled-method";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
|
||||
// This will ensure that these icons have a className which we can use in the TitleContainer
|
||||
const ExpandCollapseCodicon = styled(Codicon)``;
|
||||
@@ -36,6 +39,11 @@ const ModelTypeText = styled.span`
|
||||
color: var(--vscode-descriptionForeground);
|
||||
`;
|
||||
|
||||
const ViewLink = styled(VSCodeLink)`
|
||||
white-space: nowrap;
|
||||
padding: 0 0 0.25em 1em;
|
||||
`;
|
||||
|
||||
const ModelDetailsContainer = styled.div`
|
||||
padding-top: 10px;
|
||||
`;
|
||||
@@ -53,12 +61,38 @@ const Alert = styled.li`
|
||||
|
||||
interface Props {
|
||||
modelAlerts: ModelAlerts;
|
||||
revealedModel: ModeledMethod | null;
|
||||
}
|
||||
|
||||
export const ModelAlertsResults = ({
|
||||
modelAlerts,
|
||||
revealedModel,
|
||||
}: Props): React.JSX.Element => {
|
||||
const [isExpanded, setExpanded] = useState(true);
|
||||
const viewInModelEditor = useCallback(
|
||||
() =>
|
||||
vscode.postMessage({
|
||||
t: "revealInModelEditor",
|
||||
method: modelAlerts.model,
|
||||
}),
|
||||
[modelAlerts.model],
|
||||
);
|
||||
|
||||
const ref = useRef<HTMLElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
revealedModel &&
|
||||
createModeledMethodKey(modelAlerts.model) ===
|
||||
createModeledMethodKey(revealedModel)
|
||||
) {
|
||||
ref.current?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
}
|
||||
}, [modelAlerts.model, revealedModel]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TitleContainer onClick={() => setExpanded(!isExpanded)}>
|
||||
@@ -71,6 +105,14 @@ export const ModelAlertsResults = ({
|
||||
<VSCodeBadge>{formatDecimal(modelAlerts.alerts.length)}</VSCodeBadge>
|
||||
<MethodName {...modelAlerts.model}></MethodName>
|
||||
<ModelTypeText>{modelAlerts.model.type}</ModelTypeText>
|
||||
<ViewLink
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
viewInModelEditor();
|
||||
}}
|
||||
>
|
||||
View
|
||||
</ViewLink>
|
||||
</TitleContainer>
|
||||
{isExpanded && (
|
||||
<>
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
|
||||
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||
import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react";
|
||||
import { vscode } from "../vscode-api";
|
||||
|
||||
const ModelAlertsButton = styled(VSCodeBadge)`
|
||||
cursor: pointer;
|
||||
@@ -23,10 +24,17 @@ export const ModelAlertsIndicator = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluationRun || !modeledMethod) {
|
||||
if (!evaluationRun?.variantAnalysis || !modeledMethod) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const revealInModelAlertsView = () => {
|
||||
vscode.postMessage({
|
||||
t: "revealInModelAlertsView",
|
||||
modeledMethod,
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Once we have alert provenance, we can show actual alert counts here.
|
||||
// For now, we show a random number.
|
||||
const number = Math.floor(Math.random() * 10);
|
||||
@@ -37,6 +45,7 @@ export const ModelAlertsIndicator = ({
|
||||
aria-label="Model alerts"
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
revealInModelAlertsView();
|
||||
}}
|
||||
>
|
||||
{number}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { dirSync } from "tmp";
|
||||
import type { BaseLogger, Logger } from "../../../src/common/logging";
|
||||
import { TeeLogger } from "../../../src/common/logging";
|
||||
import { OutputChannelLogger } from "../../../src/common/logging/vscode";
|
||||
import type { Disposable } from "../../../src/common/disposable-object";
|
||||
|
||||
jest.setTimeout(999999);
|
||||
|
||||
@@ -66,6 +67,8 @@ describe("OutputChannelLogger tests", function () {
|
||||
|
||||
// should have created 1 side log
|
||||
expect(readdirSync(tempFolders.storagePath.name)).toEqual(["hucairz"]);
|
||||
|
||||
hucairz.dispose();
|
||||
});
|
||||
|
||||
it("should create a side log", async () => {
|
||||
@@ -86,12 +89,15 @@ describe("OutputChannelLogger tests", function () {
|
||||
expect(
|
||||
readFileSync(join(tempFolders.storagePath.name, "second"), "utf8"),
|
||||
).toBe("yyy\n");
|
||||
|
||||
first.dispose();
|
||||
second.dispose();
|
||||
});
|
||||
|
||||
function createSideLogger(
|
||||
logger: Logger,
|
||||
additionalLogLocation: string,
|
||||
): BaseLogger {
|
||||
): BaseLogger & Disposable {
|
||||
return new TeeLogger(
|
||||
logger,
|
||||
join(tempFolders.storagePath.name, additionalLogLocation),
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { createNoneModeledMethod } from "../../factories/model-editor/modeled-method-factories";
|
||||
import {
|
||||
createNeutralModeledMethod,
|
||||
createNoneModeledMethod,
|
||||
createSinkModeledMethod,
|
||||
createSourceModeledMethod,
|
||||
createSummaryModeledMethod,
|
||||
} from "../../factories/model-editor/modeled-method-factories";
|
||||
import type { ModeledMethod } from "../../../src/model-editor/modeled-method";
|
||||
import {
|
||||
createModeledMethodKey,
|
||||
modeledMethodSupportsInput,
|
||||
modeledMethodSupportsKind,
|
||||
modeledMethodSupportsOutput,
|
||||
@@ -50,3 +57,124 @@ describe("modeledMethodSupportsProvenance", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createModeledMethodKey", () => {
|
||||
it("should create a key for a modeled method", () => {
|
||||
const modeledMethod = createNoneModeledMethod();
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const expectedKey =
|
||||
'{"endpointType":"method","methodName":"createQuery","methodParameters":"(String)","packageName":"org.sql2o","signature":"org.sql2o.Connection#createQuery(String)","type":"none","typeName":"Connection"}';
|
||||
|
||||
expect(key).toBe(expectedKey);
|
||||
});
|
||||
|
||||
it("should check that two modeled methods are the same", () => {
|
||||
const modeledMethod = createSummaryModeledMethod();
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const modeledMethod2 = createSummaryModeledMethod();
|
||||
const key2 = createModeledMethodKey(modeledMethod2);
|
||||
|
||||
// Object references are different, but the keys are the same.
|
||||
expect(modeledMethod).not.toBe(modeledMethod2);
|
||||
expect(key).toEqual(key2);
|
||||
});
|
||||
|
||||
it("should always set provenance to manual", () => {
|
||||
const modeledMethod = createSinkModeledMethod({
|
||||
provenance: "df-generated",
|
||||
});
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
expect(key).not.toContain('"provenance":"df-generated"');
|
||||
expect(key).toContain('"provenance":"manual"');
|
||||
});
|
||||
|
||||
describe("ignores unused properties", () => {
|
||||
it("for source modeled methods", () => {
|
||||
const modeledMethod = createSourceModeledMethod({
|
||||
output: "ReturnValue",
|
||||
...{
|
||||
input: "Argument[this]",
|
||||
},
|
||||
});
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const modeledMethod2 = createSourceModeledMethod({
|
||||
output: "ReturnValue",
|
||||
...{
|
||||
input: "Argument[1]",
|
||||
},
|
||||
});
|
||||
const key2 = createModeledMethodKey(modeledMethod2);
|
||||
|
||||
expect(key).not.toContain("input");
|
||||
expect(key).toEqual(key2);
|
||||
});
|
||||
|
||||
it("for sink modeled methods", () => {
|
||||
const modeledMethod = createSinkModeledMethod({
|
||||
input: "Argument[this]",
|
||||
...{
|
||||
output: "ReturnValue",
|
||||
},
|
||||
});
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const modeledMethod2 = createSinkModeledMethod({
|
||||
input: "Argument[this]",
|
||||
...{
|
||||
output: "Argument[this]",
|
||||
},
|
||||
});
|
||||
const key2 = createModeledMethodKey(modeledMethod2);
|
||||
|
||||
expect(key).not.toContain("output");
|
||||
expect(key).toEqual(key2);
|
||||
});
|
||||
|
||||
it("for summary modeled methods", () => {
|
||||
const modeledMethod = createSummaryModeledMethod({
|
||||
input: "Argument[this]",
|
||||
output: "ReturnValue",
|
||||
...{ supported: true },
|
||||
});
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const modeledMethod2 = createSummaryModeledMethod({
|
||||
input: "Argument[this]",
|
||||
output: "ReturnValue",
|
||||
...{ supported: false },
|
||||
});
|
||||
const key2 = createModeledMethodKey(modeledMethod2);
|
||||
|
||||
expect(key).not.toContain("supported");
|
||||
expect(key).toEqual(key2);
|
||||
});
|
||||
|
||||
it("for neutral modeled methods", () => {
|
||||
const modeledMethod = createNeutralModeledMethod({
|
||||
type: "neutral",
|
||||
...{
|
||||
input: "Argument[this]",
|
||||
output: "ReturnValue",
|
||||
},
|
||||
});
|
||||
const key = createModeledMethodKey(modeledMethod);
|
||||
|
||||
const modeledMethod2 = createNeutralModeledMethod({
|
||||
type: "neutral",
|
||||
...{
|
||||
input: "Argument[1]",
|
||||
output: "ReturnValue",
|
||||
},
|
||||
});
|
||||
const key2 = createModeledMethodKey(modeledMethod2);
|
||||
|
||||
expect(key).not.toContain("input");
|
||||
expect(key).not.toContain("output");
|
||||
expect(key).toEqual(key2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,10 +3,7 @@ import { Uri, window } from "vscode";
|
||||
|
||||
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
||||
import type { DatabaseManager } from "../../../../src/databases/local-databases";
|
||||
import {
|
||||
importLocalDatabase,
|
||||
promptImportInternetDatabase,
|
||||
} from "../../../../src/databases/database-fetcher";
|
||||
import { DatabaseFetcher } from "../../../../src/databases/database-fetcher";
|
||||
import {
|
||||
cleanDatabases,
|
||||
dbLoc,
|
||||
@@ -15,9 +12,8 @@ import {
|
||||
storagePath,
|
||||
testprojLoc,
|
||||
} from "../../global.helper";
|
||||
import { createMockCommandManager } from "../../../__mocks__/commandsMock";
|
||||
import { utimesSync } from "fs";
|
||||
import { remove, existsSync } from "fs-extra";
|
||||
import { existsSync, remove, utimesSync } from "fs-extra";
|
||||
import { createMockApp } from "../../../__mocks__/appMock";
|
||||
|
||||
/**
|
||||
* Run various integration tests for databases
|
||||
@@ -51,14 +47,16 @@ describe("database-fetcher", () => {
|
||||
describe("importLocalDatabase", () => {
|
||||
it("should add a database from an archive", async () => {
|
||||
const uri = Uri.file(dbLoc);
|
||||
let dbItem = await importLocalDatabase(
|
||||
createMockCommandManager(),
|
||||
uri.toString(true),
|
||||
const databaseFetcher = new DatabaseFetcher(
|
||||
createMockApp(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
progressCallback,
|
||||
cli,
|
||||
);
|
||||
let dbItem = await databaseFetcher.importLocalDatabase(
|
||||
uri.toString(true),
|
||||
progressCallback,
|
||||
);
|
||||
expect(dbItem).toBe(databaseManager.currentDatabaseItem);
|
||||
expect(dbItem).toBe(databaseManager.databaseItems[0]);
|
||||
expect(dbItem).toBeDefined();
|
||||
@@ -68,14 +66,16 @@ describe("database-fetcher", () => {
|
||||
});
|
||||
|
||||
it("should import a testproj database", async () => {
|
||||
let dbItem = await importLocalDatabase(
|
||||
createMockCommandManager(),
|
||||
Uri.file(testprojLoc).toString(true),
|
||||
const databaseFetcher = new DatabaseFetcher(
|
||||
createMockApp(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
progressCallback,
|
||||
cli,
|
||||
);
|
||||
let dbItem = await databaseFetcher.importLocalDatabase(
|
||||
Uri.file(testprojLoc).toString(true),
|
||||
progressCallback,
|
||||
);
|
||||
expect(dbItem).toBe(databaseManager.currentDatabaseItem);
|
||||
expect(dbItem).toBe(databaseManager.databaseItems[0]);
|
||||
expect(dbItem).toBeDefined();
|
||||
@@ -109,13 +109,14 @@ describe("database-fetcher", () => {
|
||||
// Provide a database URL when prompted
|
||||
inputBoxStub.mockResolvedValue(DB_URL);
|
||||
|
||||
let dbItem = await promptImportInternetDatabase(
|
||||
createMockCommandManager(),
|
||||
const databaseFetcher = new DatabaseFetcher(
|
||||
createMockApp(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
progressCallback,
|
||||
cli,
|
||||
);
|
||||
let dbItem =
|
||||
await databaseFetcher.promptImportInternetDatabase(progressCallback);
|
||||
expect(dbItem).toBeDefined();
|
||||
dbItem = dbItem!;
|
||||
expect(dbItem.name).toBe("db");
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
DatabaseManager,
|
||||
FullDatabaseOptions,
|
||||
} from "../../../../src/databases/local-databases";
|
||||
import * as databaseFetcher from "../../../../src/databases/database-fetcher";
|
||||
import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher";
|
||||
import { createMockDB } from "../../../factories/databases/databases";
|
||||
import { asError } from "../../../../src/common/helpers-pure";
|
||||
import { Setting } from "../../../../src/config";
|
||||
@@ -42,6 +42,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
let mockApp: App;
|
||||
let wizard: SkeletonQueryWizard;
|
||||
let mockDatabaseManager: DatabaseManager;
|
||||
let databaseFetcher: DatabaseFetcher;
|
||||
let dir: DirResult;
|
||||
let storagePath: string;
|
||||
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
@@ -55,11 +56,8 @@ describe("SkeletonQueryWizard", () => {
|
||||
let createExampleQlFileSpy: jest.SpiedFunction<
|
||||
typeof QlPackGenerator.prototype.createExampleQlFile
|
||||
>;
|
||||
let downloadGitHubDatabaseSpy: jest.SpiedFunction<
|
||||
typeof databaseFetcher.downloadGitHubDatabase
|
||||
>;
|
||||
let askForGitHubRepoSpy: jest.SpiedFunction<
|
||||
typeof databaseFetcher.askForGitHubRepo
|
||||
let promptImportGithubDatabaseMock: jest.MockedFunction<
|
||||
DatabaseFetcher["promptImportGithubDatabase"]
|
||||
>;
|
||||
let openTextDocumentSpy: jest.SpiedFunction<
|
||||
typeof workspace.openTextDocument
|
||||
@@ -115,6 +113,11 @@ describe("SkeletonQueryWizard", () => {
|
||||
},
|
||||
] as WorkspaceFolder[]);
|
||||
|
||||
promptImportGithubDatabaseMock = jest.fn().mockReturnValue(undefined);
|
||||
databaseFetcher = mockedObject<DatabaseFetcher>({
|
||||
promptImportGithubDatabase: promptImportGithubDatabaseMock,
|
||||
});
|
||||
|
||||
quickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValueOnce(
|
||||
mockedQuickPickItem({
|
||||
label: chosenLanguage,
|
||||
@@ -133,9 +136,6 @@ describe("SkeletonQueryWizard", () => {
|
||||
createExampleQlFileSpy = jest
|
||||
.spyOn(QlPackGenerator.prototype, "createExampleQlFile")
|
||||
.mockResolvedValue(undefined);
|
||||
downloadGitHubDatabaseSpy = jest
|
||||
.spyOn(databaseFetcher, "downloadGitHubDatabase")
|
||||
.mockResolvedValue(undefined);
|
||||
openTextDocumentSpy = jest
|
||||
.spyOn(workspace, "openTextDocument")
|
||||
.mockResolvedValue({} as TextDocument);
|
||||
@@ -145,13 +145,9 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
|
||||
askForGitHubRepoSpy = jest
|
||||
.spyOn(databaseFetcher, "askForGitHubRepo")
|
||||
.mockResolvedValue(QUERY_LANGUAGE_TO_DATABASE_REPO[chosenLanguage]);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -172,7 +168,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
QueryLanguage.Swift,
|
||||
);
|
||||
@@ -202,7 +198,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
title: "Download database",
|
||||
}),
|
||||
);
|
||||
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
|
||||
expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should download database for selected language when selecting download in prompt", async () => {
|
||||
@@ -219,7 +215,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
await wizard.execute();
|
||||
await wizard.waitForDownload();
|
||||
|
||||
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();
|
||||
expect(promptImportGithubDatabaseMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should open the query file", async () => {
|
||||
@@ -320,7 +316,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManagerWithItems,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
});
|
||||
@@ -328,7 +324,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
it("should not download a new database for language", async () => {
|
||||
await wizard.execute();
|
||||
|
||||
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
|
||||
expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not select the database", async () => {
|
||||
@@ -369,7 +365,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManagerWithItems,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
});
|
||||
@@ -377,7 +373,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
it("should not download a new database for language", async () => {
|
||||
await wizard.execute();
|
||||
|
||||
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
|
||||
expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should select an existing database", async () => {
|
||||
@@ -409,54 +405,23 @@ describe("SkeletonQueryWizard", () => {
|
||||
});
|
||||
|
||||
describe("if database is missing", () => {
|
||||
describe("if the user chooses to downloaded the suggested database from GitHub", () => {
|
||||
beforeEach(() => {
|
||||
showInformationMessageSpy.mockImplementation(
|
||||
async (_message, options, item) => {
|
||||
if (item === undefined) {
|
||||
return options as MessageItem;
|
||||
}
|
||||
beforeEach(() => {
|
||||
showInformationMessageSpy.mockImplementation(
|
||||
async (_message, options, item) => {
|
||||
if (item === undefined) {
|
||||
return options as MessageItem;
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should download a new database for language", async () => {
|
||||
await wizard.execute();
|
||||
await wizard.waitForDownload();
|
||||
|
||||
expect(askForGitHubRepoSpy).toHaveBeenCalled();
|
||||
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();
|
||||
});
|
||||
return item;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("if the user choses to download a different database from GitHub than the one suggested", () => {
|
||||
beforeEach(() => {
|
||||
showInformationMessageSpy.mockImplementation(
|
||||
async (_message, options, item) => {
|
||||
if (item === undefined) {
|
||||
return options as MessageItem;
|
||||
}
|
||||
it("should download a new database for language", async () => {
|
||||
await wizard.execute();
|
||||
await wizard.waitForDownload();
|
||||
|
||||
return item;
|
||||
},
|
||||
);
|
||||
|
||||
const chosenGitHubRepo = "pickles-owner/pickles-repo";
|
||||
|
||||
askForGitHubRepoSpy = jest
|
||||
.spyOn(databaseFetcher, "askForGitHubRepo")
|
||||
.mockResolvedValue(chosenGitHubRepo);
|
||||
});
|
||||
|
||||
it("should download the newly chosen database", async () => {
|
||||
await wizard.execute();
|
||||
await wizard.waitForDownload();
|
||||
|
||||
expect(askForGitHubRepoSpy).toHaveBeenCalled();
|
||||
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();
|
||||
});
|
||||
expect(promptImportGithubDatabaseMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -504,7 +469,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
QueryLanguage.Javascript,
|
||||
);
|
||||
@@ -725,7 +690,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
});
|
||||
@@ -754,7 +719,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
});
|
||||
@@ -787,7 +752,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
QueryLanguage.Swift,
|
||||
);
|
||||
@@ -830,7 +795,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
jest.fn(),
|
||||
mockApp,
|
||||
mockDatabaseManager,
|
||||
storagePath,
|
||||
databaseFetcher,
|
||||
selectedItems,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@ import type {
|
||||
} from "../../src/databases/local-databases";
|
||||
import type { CodeQLCliServer } from "../../src/codeql-cli/cli";
|
||||
import type { CodeQLExtensionInterface } from "../../src/extension";
|
||||
import { importLocalDatabase } from "../../src/databases/database-fetcher";
|
||||
import { createMockCommandManager } from "../__mocks__/commandsMock";
|
||||
import { DatabaseFetcher } from "../../src/databases/database-fetcher";
|
||||
import { createMockApp } from "../__mocks__/appMock";
|
||||
|
||||
// This file contains helpers shared between tests that work with an activated extension.
|
||||
|
||||
@@ -40,15 +40,17 @@ export async function ensureTestDatabase(
|
||||
// Add a database, but make sure the database manager is empty first
|
||||
await cleanDatabases(databaseManager);
|
||||
const uri = Uri.file(dbLoc);
|
||||
const maybeDbItem = await importLocalDatabase(
|
||||
createMockCommandManager(),
|
||||
uri.toString(true),
|
||||
const databaseFetcher = new DatabaseFetcher(
|
||||
createMockApp(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cli,
|
||||
);
|
||||
const maybeDbItem = await databaseFetcher.importLocalDatabase(
|
||||
uri.toString(true),
|
||||
(_p) => {
|
||||
/**/
|
||||
},
|
||||
cli,
|
||||
);
|
||||
|
||||
if (!maybeDbItem) {
|
||||
|
||||
@@ -10,13 +10,11 @@ import {
|
||||
askForGitHubDatabaseDownload,
|
||||
downloadDatabaseFromGitHub,
|
||||
} from "../../../../../src/databases/github-databases/download";
|
||||
import type { DatabaseManager } from "../../../../../src/databases/local-databases";
|
||||
import type { GitHubDatabaseConfig } from "../../../../../src/config";
|
||||
import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
|
||||
import { createMockCommandManager } from "../../../../__mocks__/commandsMock";
|
||||
import * as databaseFetcher from "../../../../../src/databases/database-fetcher";
|
||||
import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher";
|
||||
import * as dialog from "../../../../../src/common/vscode/dialog";
|
||||
import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api";
|
||||
import { createMockApp } from "../../../../__mocks__/appMock";
|
||||
|
||||
describe("askForGitHubDatabaseDownload", () => {
|
||||
const setDownload = jest.fn();
|
||||
@@ -96,11 +94,9 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
let octokit: Octokit;
|
||||
const owner = "github";
|
||||
const repo = "codeql";
|
||||
let databaseManager: DatabaseManager;
|
||||
let databaseFetcher: DatabaseFetcher;
|
||||
|
||||
const storagePath = "/a/b/c/d";
|
||||
let cliServer: CodeQLCliServer;
|
||||
const commandManager = createMockCommandManager();
|
||||
const app = createMockApp();
|
||||
|
||||
let databases = [
|
||||
mockedObject<CodeqlDatabase>({
|
||||
@@ -116,14 +112,17 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
];
|
||||
|
||||
let showQuickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction<
|
||||
typeof databaseFetcher.downloadGitHubDatabaseFromUrl
|
||||
let downloadGitHubDatabaseFromUrlMock: jest.MockedFunction<
|
||||
DatabaseFetcher["downloadGitHubDatabaseFromUrl"]
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
octokit = mockedObject<Octokit>({});
|
||||
databaseManager = mockedObject<DatabaseManager>({});
|
||||
cliServer = mockedObject<CodeQLCliServer>({});
|
||||
|
||||
downloadGitHubDatabaseFromUrlMock = jest.fn().mockReturnValue(undefined);
|
||||
databaseFetcher = mockedObject<DatabaseFetcher>({
|
||||
downloadGitHubDatabaseFromUrl: downloadGitHubDatabaseFromUrlMock,
|
||||
});
|
||||
|
||||
showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue(
|
||||
mockedQuickPickItem([
|
||||
@@ -132,9 +131,6 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
}),
|
||||
]),
|
||||
);
|
||||
downloadGitHubDatabaseFromUrlSpy = jest
|
||||
.spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl")
|
||||
.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("downloads the database", async () => {
|
||||
@@ -143,14 +139,12 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
databases[0].url,
|
||||
databases[0].id,
|
||||
databases[0].created_at,
|
||||
@@ -159,9 +153,6 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
@@ -207,14 +198,12 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
databases[1].url,
|
||||
databases[1].id,
|
||||
databases[1].created_at,
|
||||
@@ -223,9 +212,6 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
@@ -263,14 +249,12 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(2);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
databases[0].url,
|
||||
databases[0].id,
|
||||
databases[0].created_at,
|
||||
@@ -279,13 +263,10 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
databases[1].url,
|
||||
databases[1].id,
|
||||
databases[1].created_at,
|
||||
@@ -294,9 +275,6 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
@@ -328,13 +306,11 @@ describe("downloadDatabaseFromGitHub", () => {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled();
|
||||
expect(downloadGitHubDatabaseFromUrlMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createMockApp } from "../../../../__mocks__/appMock";
|
||||
import type { App } from "../../../../../src/common/app";
|
||||
import type { DatabaseManager } from "../../../../../src/databases/local-databases";
|
||||
import { mockEmptyDatabaseManager } from "../../query-testing/test-runner-helpers";
|
||||
import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
|
||||
import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers";
|
||||
import type { GitHubDatabaseConfig } from "../../../../../src/config";
|
||||
import { GitHubDatabasesModule } from "../../../../../src/databases/github-databases";
|
||||
@@ -16,13 +15,13 @@ import * as githubDatabasesApi from "../../../../../src/databases/github-databas
|
||||
import * as githubDatabasesDownload from "../../../../../src/databases/github-databases/download";
|
||||
import * as githubDatabasesUpdates from "../../../../../src/databases/github-databases/updates";
|
||||
import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates";
|
||||
import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher";
|
||||
|
||||
describe("GitHubDatabasesModule", () => {
|
||||
describe("promptGitHubRepositoryDownload", () => {
|
||||
let app: App;
|
||||
let databaseManager: DatabaseManager;
|
||||
let databaseStoragePath: string;
|
||||
let cliServer: CodeQLCliServer;
|
||||
const databaseFetcher = mockedObject<DatabaseFetcher>({});
|
||||
let config: GitHubDatabaseConfig;
|
||||
let gitHubDatabasesModule: GitHubDatabasesModule;
|
||||
|
||||
@@ -64,8 +63,6 @@ describe("GitHubDatabasesModule", () => {
|
||||
beforeEach(() => {
|
||||
app = createMockApp();
|
||||
databaseManager = mockEmptyDatabaseManager();
|
||||
databaseStoragePath = "/a/b/some-path";
|
||||
cliServer = mockedObject<CodeQLCliServer>({});
|
||||
config = mockedObject<GitHubDatabaseConfig>({
|
||||
download: "ask",
|
||||
update: "ask",
|
||||
@@ -74,8 +71,7 @@ describe("GitHubDatabasesModule", () => {
|
||||
gitHubDatabasesModule = new GitHubDatabasesModule(
|
||||
app,
|
||||
databaseManager,
|
||||
databaseStoragePath,
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
config,
|
||||
);
|
||||
|
||||
@@ -124,8 +120,7 @@ describe("GitHubDatabasesModule", () => {
|
||||
gitHubDatabasesModule = new GitHubDatabasesModule(
|
||||
app,
|
||||
databaseManager,
|
||||
databaseStoragePath,
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
config,
|
||||
);
|
||||
|
||||
@@ -206,9 +201,7 @@ describe("GitHubDatabasesModule", () => {
|
||||
owner,
|
||||
repo,
|
||||
databases,
|
||||
databaseManager,
|
||||
databaseStoragePath,
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
});
|
||||
@@ -250,8 +243,7 @@ describe("GitHubDatabasesModule", () => {
|
||||
repo,
|
||||
databaseUpdates,
|
||||
databaseManager,
|
||||
databaseStoragePath,
|
||||
cliServer,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -10,9 +10,7 @@ import {
|
||||
import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api";
|
||||
import type { DatabaseManager } from "../../../../../src/databases/local-databases";
|
||||
import type { GitHubDatabaseConfig } from "../../../../../src/config";
|
||||
import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
|
||||
import { createMockCommandManager } from "../../../../__mocks__/commandsMock";
|
||||
import * as databaseFetcher from "../../../../../src/databases/database-fetcher";
|
||||
import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher";
|
||||
import * as dialog from "../../../../../src/common/vscode/dialog";
|
||||
import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates";
|
||||
import {
|
||||
@@ -20,6 +18,7 @@ import {
|
||||
downloadDatabaseUpdateFromGitHub,
|
||||
isNewerDatabaseAvailable,
|
||||
} from "../../../../../src/databases/github-databases/updates";
|
||||
import { createMockApp } from "../../../../__mocks__/appMock";
|
||||
|
||||
describe("isNewerDatabaseAvailable", () => {
|
||||
const owner = "github";
|
||||
@@ -344,9 +343,8 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
const owner = "github";
|
||||
const repo = "codeql";
|
||||
let databaseManager: DatabaseManager;
|
||||
const storagePath = "/a/b/c/d";
|
||||
let cliServer: CodeQLCliServer;
|
||||
const commandManager = createMockCommandManager();
|
||||
let databaseFetcher: DatabaseFetcher;
|
||||
const app = createMockApp();
|
||||
|
||||
let updates: DatabaseUpdate[] = [
|
||||
{
|
||||
@@ -367,8 +365,8 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
];
|
||||
|
||||
let showQuickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction<
|
||||
typeof databaseFetcher.downloadGitHubDatabaseFromUrl
|
||||
let downloadGitHubDatabaseFromUrlMock: jest.MockedFunction<
|
||||
DatabaseFetcher["downloadGitHubDatabaseFromUrl"]
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -376,7 +374,11 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
databaseManager = mockedObject<DatabaseManager>({
|
||||
currentDatabaseItem: mockDatabaseItem(),
|
||||
});
|
||||
cliServer = mockedObject<CodeQLCliServer>({});
|
||||
|
||||
downloadGitHubDatabaseFromUrlMock = jest.fn().mockReturnValue(undefined);
|
||||
databaseFetcher = mockedObject<DatabaseFetcher>({
|
||||
downloadGitHubDatabaseFromUrl: downloadGitHubDatabaseFromUrlMock,
|
||||
});
|
||||
|
||||
showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue(
|
||||
mockedQuickPickItem([
|
||||
@@ -385,9 +387,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
}),
|
||||
]),
|
||||
);
|
||||
downloadGitHubDatabaseFromUrlSpy = jest
|
||||
.spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl")
|
||||
.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("downloads the database", async () => {
|
||||
@@ -397,13 +396,12 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
updates,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
updates[0].database.url,
|
||||
updates[0].database.id,
|
||||
updates[0].database.created_at,
|
||||
@@ -412,9 +410,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
@@ -476,13 +471,12 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
updates,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
updates[1].database.url,
|
||||
updates[1].database.id,
|
||||
updates[1].database.created_at,
|
||||
@@ -491,9 +485,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
@@ -532,13 +523,12 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
updates,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(2);
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
updates[0].database.url,
|
||||
updates[0].database.id,
|
||||
updates[0].database.created_at,
|
||||
@@ -547,13 +537,10 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||
expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith(
|
||||
updates[1].database.url,
|
||||
updates[1].database.id,
|
||||
updates[1].database.created_at,
|
||||
@@ -562,9 +549,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
octokit,
|
||||
expect.anything(),
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
@@ -597,12 +581,11 @@ describe("downloadDatabaseUpdateFromGitHub", () => {
|
||||
repo,
|
||||
updates,
|
||||
databaseManager,
|
||||
storagePath,
|
||||
cliServer,
|
||||
commandManager,
|
||||
databaseFetcher,
|
||||
app.commands,
|
||||
);
|
||||
|
||||
expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled();
|
||||
expect(downloadGitHubDatabaseFromUrlMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import { testDisposeHandler } from "../../test-dispose-handler";
|
||||
import { createMockApp } from "../../../__mocks__/appMock";
|
||||
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||
import { mockedQuickPickItem, mockedObject } from "../../utils/mocking.helpers";
|
||||
import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher";
|
||||
|
||||
describe("local-databases-ui", () => {
|
||||
const storageDir = dirSync({ unsafeCleanup: true }).name;
|
||||
@@ -104,6 +105,7 @@ describe("local-databases-ui", () => {
|
||||
},
|
||||
setCurrentDatabaseItem: () => {},
|
||||
} as any,
|
||||
mockedObject<DatabaseFetcher>({}),
|
||||
{
|
||||
onLanguageContextChanged: () => {
|
||||
/**/
|
||||
@@ -141,6 +143,7 @@ describe("local-databases-ui", () => {
|
||||
setCurrentDatabaseItem: () => {},
|
||||
currentDatabaseItem: { databaseUri: Uri.file(db1) },
|
||||
} as any,
|
||||
mockedObject<DatabaseFetcher>({}),
|
||||
{
|
||||
onLanguageContextChanged: () => {
|
||||
/**/
|
||||
@@ -177,6 +180,7 @@ describe("local-databases-ui", () => {
|
||||
const databaseUI = new DatabaseUI(
|
||||
app,
|
||||
databaseManager,
|
||||
mockedObject<DatabaseFetcher>({}),
|
||||
{
|
||||
onLanguageContextChanged: () => {
|
||||
/**/
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { ModelConfigListener } from "../../../../src/config";
|
||||
import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock";
|
||||
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||
import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
|
||||
import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher";
|
||||
|
||||
describe("ModelEditorView", () => {
|
||||
const app = createMockApp({});
|
||||
@@ -22,6 +23,7 @@ describe("ModelEditorView", () => {
|
||||
onDidChangeConfiguration: jest.fn(),
|
||||
});
|
||||
const databaseManager = mockEmptyDatabaseManager();
|
||||
const databaseFetcher = mockedObject<DatabaseFetcher>({});
|
||||
const variantAnalysisManager = mockedObject<VariantAnalysisManager>({});
|
||||
const cliServer = mockedObject<CodeQLCliServer>({});
|
||||
const queryRunner = mockedObject<QueryRunner>({});
|
||||
@@ -50,6 +52,7 @@ describe("ModelEditorView", () => {
|
||||
modelingEvents,
|
||||
modelConfig,
|
||||
databaseManager,
|
||||
databaseFetcher,
|
||||
variantAnalysisManager,
|
||||
cliServer,
|
||||
queryRunner,
|
||||
|
||||
Reference in New Issue
Block a user