Merge remote-tracking branch 'origin/main' into koesie10/sort-repositories
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
**/* @github/codeql-vscode-reviewers
|
||||
**/remote-queries/ @github/code-scanning-secexp-reviewers
|
||||
**/variant-analysis/ @github/code-scanning-secexp-reviewers
|
||||
**/databases/ @github/code-scanning-secexp-reviewers
|
||||
|
||||
@@ -102,11 +102,17 @@ We have several types of tests:
|
||||
|
||||
The CLI integration tests require an instance of the CodeQL CLI to run so they will require some extra setup steps. When adding new tests to our test suite, please be mindful of whether they need to be in the cli-integration folder. If the tests don't depend on the CLI, they are better suited to being a VSCode integration test.
|
||||
|
||||
Any test data you're using (sample projects, config files, etc.) must go in a `src/vscode-tests/*/data` directory. When you run the tests, the test runner will copy the data directory to `out/vscode-tests/*/data`.
|
||||
|
||||
#### Running the tests
|
||||
|
||||
Pre-requisites:
|
||||
1. Run `npm run build`.
|
||||
2. You will need to have `npm run watch` running in the background.
|
||||
|
||||
##### 1. From the terminal
|
||||
|
||||
First move into the `extensions/ql-vscode` directory. Then, depending on which tests you want to run, use the appropriate command to run the tests:
|
||||
Then, from the `extensions/ql-vscode` directory, use the appropriate command to run the tests:
|
||||
|
||||
* Unit tests: `npm run test:unit`
|
||||
* View Tests: `npm test:view`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as gulp from 'gulp';
|
||||
import { compileTypeScript, watchTypeScript, cleanOutput } from './typescript';
|
||||
import { compileTextMateGrammar } from './textmate';
|
||||
import { copyTestData } from './tests';
|
||||
import { copyTestData, watchTestData } from './tests';
|
||||
import { compileView, watchView } from './webpack';
|
||||
import { packageExtension } from './package';
|
||||
import { injectAppInsightsKey } from './appInsights';
|
||||
@@ -21,6 +21,7 @@ export {
|
||||
watchView,
|
||||
compileTypeScript,
|
||||
copyTestData,
|
||||
watchTestData,
|
||||
injectAppInsightsKey,
|
||||
compileView,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import * as gulp from 'gulp';
|
||||
|
||||
export function copyTestData() {
|
||||
copyNoWorkspaceData();
|
||||
copyCliIntegrationData();
|
||||
return Promise.resolve();
|
||||
return Promise.all([
|
||||
copyNoWorkspaceData(),
|
||||
copyCliIntegrationData()
|
||||
]);
|
||||
}
|
||||
|
||||
export function watchTestData() {
|
||||
return gulp.watch(['src/vscode-tests/*/data/**/*'], copyTestData);
|
||||
}
|
||||
|
||||
function copyNoWorkspaceData() {
|
||||
|
||||
2598
extensions/ql-vscode/package-lock.json
generated
2598
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1253,14 +1253,14 @@
|
||||
"watch": "npm-run-all -p watch:*",
|
||||
"watch:extension": "tsc --watch",
|
||||
"watch:webpack": "gulp watchView",
|
||||
"watch:files": "gulp watchTestData",
|
||||
"test": "npm-run-all -p test:*",
|
||||
"test:unit": "mocha --config .mocharc.json test/pure-tests/**/*.ts",
|
||||
"test:view": "jest",
|
||||
"integration-setup": "rm -rf ./out/vscode-tests && gulp",
|
||||
"integration": "npm run integration-setup && node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
|
||||
"integration:no-workspace": "npm run integration-setup && node ./out/vscode-tests/run-integration-tests.js no-workspace",
|
||||
"integration:minimal-workspace": "npm run integration-setup && node ./out/vscode-tests/run-integration-tests.js minimal-workspace",
|
||||
"cli-integration": "npm run integration-setup && node ./out/vscode-tests/run-integration-tests.js cli-integration",
|
||||
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
|
||||
"integration:no-workspace": "node ./out/vscode-tests/run-integration-tests.js no-workspace",
|
||||
"integration:minimal-workspace": "node ./out/vscode-tests/run-integration-tests.js minimal-workspace",
|
||||
"cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration",
|
||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||
"format": "tsfmt -r && eslint . --ext .ts,.tsx --fix",
|
||||
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
|
||||
@@ -1277,6 +1277,7 @@
|
||||
"@vscode/codicons": "^0.0.31",
|
||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"classnames": "~2.2.6",
|
||||
"d3": "^7.6.1",
|
||||
"d3-graphviz": "^2.6.1",
|
||||
|
||||
3
extensions/ql-vscode/src/databases/README.md
Normal file
3
extensions/ql-vscode/src/databases/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Databases
|
||||
|
||||
This folder contains code for the new experimental databases panel and new query run experience.
|
||||
66
extensions/ql-vscode/src/databases/db-config-store.ts
Normal file
66
extensions/ql-vscode/src/databases/db-config-store.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { cloneDbConfig, DbConfig } from './db-config';
|
||||
import * as chokidar from 'chokidar';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
|
||||
export class DbConfigStore extends DisposableObject {
|
||||
private readonly configPath: string;
|
||||
|
||||
private config: DbConfig;
|
||||
private configWatcher: chokidar.FSWatcher | undefined;
|
||||
|
||||
public constructor(workspaceStoragePath: string) {
|
||||
super();
|
||||
this.configPath = path.join(workspaceStoragePath, 'workspace-databases.json');
|
||||
|
||||
this.config = this.createEmptyConfig();
|
||||
this.configWatcher = undefined;
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
await this.loadConfig();
|
||||
this.watchConfig();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.configWatcher?.unwatch(this.configPath);
|
||||
}
|
||||
|
||||
public getConfig(): DbConfig {
|
||||
// Clone the config so that it's not modified outside of this class.
|
||||
return cloneDbConfig(this.config);
|
||||
}
|
||||
|
||||
private async loadConfig(): Promise<void> {
|
||||
if (!await fs.pathExists(this.configPath)) {
|
||||
await fs.writeJSON(this.configPath, this.createEmptyConfig(), { spaces: 2 });
|
||||
}
|
||||
|
||||
await this.readConfig();
|
||||
}
|
||||
|
||||
private async readConfig(): Promise<void> {
|
||||
this.config = await fs.readJSON(this.configPath);
|
||||
}
|
||||
|
||||
private readConfigSync(): void {
|
||||
this.config = fs.readJSONSync(this.configPath);
|
||||
}
|
||||
|
||||
private watchConfig(): void {
|
||||
this.configWatcher = chokidar.watch(this.configPath).on('change', () => {
|
||||
this.readConfigSync();
|
||||
});
|
||||
}
|
||||
|
||||
private createEmptyConfig(): DbConfig {
|
||||
return {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
29
extensions/ql-vscode/src/databases/db-config.ts
Normal file
29
extensions/ql-vscode/src/databases/db-config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// Contains models for the data we want to store in the database config
|
||||
|
||||
export interface DbConfig {
|
||||
remote: RemoteDbConfig;
|
||||
}
|
||||
|
||||
export interface RemoteDbConfig {
|
||||
repositoryLists: RemoteRepositoryList[];
|
||||
owners: string[];
|
||||
repositories: string[];
|
||||
}
|
||||
|
||||
export interface RemoteRepositoryList {
|
||||
name: string;
|
||||
repositories: string[];
|
||||
}
|
||||
|
||||
export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
return {
|
||||
remote: {
|
||||
repositoryLists: config.remote.repositoryLists.map((list) => ({
|
||||
name: list.name,
|
||||
repositories: [...list.repositories],
|
||||
})),
|
||||
owners: [...config.remote.owners],
|
||||
repositories: [...config.remote.repositories],
|
||||
}
|
||||
};
|
||||
}
|
||||
53
extensions/ql-vscode/src/databases/db-item.ts
Normal file
53
extensions/ql-vscode/src/databases/db-item.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// This file contains models that are used to represent the databases.
|
||||
|
||||
export enum DbItemKind {
|
||||
RootLocal = 'RootLocal',
|
||||
RootRemote = 'RootRemote',
|
||||
RemoteSystemDefinedList = 'RemoteSystemDefinedList',
|
||||
RemoteUserDefinedList = 'RemoteUserDefinedList',
|
||||
RemoteOwner = 'RemoteOwner',
|
||||
RemoteRepo = 'RemoteRepo'
|
||||
}
|
||||
|
||||
export interface RootLocalDbItem {
|
||||
kind: DbItemKind.RootLocal;
|
||||
}
|
||||
|
||||
export interface RootRemoteDbItem {
|
||||
kind: DbItemKind.RootRemote;
|
||||
children: RemoteDbItem[];
|
||||
}
|
||||
|
||||
export type DbItem =
|
||||
| RootLocalDbItem
|
||||
| RootRemoteDbItem
|
||||
| RemoteDbItem
|
||||
|
||||
export type RemoteDbItem =
|
||||
| RemoteSystemDefinedListDbItem
|
||||
| RemoteUserDefinedListDbItem
|
||||
| RemoteOwnerDbItem
|
||||
| RemoteRepoDbItem;
|
||||
|
||||
export interface RemoteSystemDefinedListDbItem {
|
||||
kind: DbItemKind.RemoteSystemDefinedList;
|
||||
listName: string;
|
||||
listDisplayName: string;
|
||||
listDescription: string;
|
||||
}
|
||||
|
||||
export interface RemoteUserDefinedListDbItem {
|
||||
kind: DbItemKind.RemoteUserDefinedList;
|
||||
listName: string;
|
||||
repos: RemoteRepoDbItem[];
|
||||
}
|
||||
|
||||
export interface RemoteOwnerDbItem {
|
||||
kind: DbItemKind.RemoteOwner;
|
||||
ownerName: string;
|
||||
}
|
||||
|
||||
export interface RemoteRepoDbItem {
|
||||
kind: DbItemKind.RemoteRepo;
|
||||
repoFullName: string;
|
||||
}
|
||||
22
extensions/ql-vscode/src/databases/db-manager.ts
Normal file
22
extensions/ql-vscode/src/databases/db-manager.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { logger } from '../logging';
|
||||
import { DbConfigStore } from './db-config-store';
|
||||
import { DbItem } from './db-item';
|
||||
|
||||
export class DbManager {
|
||||
constructor(
|
||||
private readonly dbConfigStore: DbConfigStore
|
||||
) {
|
||||
}
|
||||
|
||||
public loadDatabases(): void {
|
||||
const config = this.dbConfigStore.getConfig();
|
||||
void logger.log(`Loaded databases: ${JSON.stringify(config)}`);
|
||||
|
||||
// This will be fleshed out in a future change.
|
||||
}
|
||||
|
||||
public getDbItems(): DbItem[] {
|
||||
// This will be fleshed out in a future change.
|
||||
return [];
|
||||
}
|
||||
}
|
||||
44
extensions/ql-vscode/src/databases/db-module.ts
Normal file
44
extensions/ql-vscode/src/databases/db-module.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { isCanary, isNewQueryRunExperienceEnabled } from '../config';
|
||||
import { logger } from '../logging';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
import { DbConfigStore } from './db-config-store';
|
||||
import { DbManager } from './db-manager';
|
||||
import { DbPanel } from './ui/db-panel';
|
||||
|
||||
export class DbModule extends DisposableObject {
|
||||
public async initialize(
|
||||
extensionContext: vscode.ExtensionContext
|
||||
): Promise<void> {
|
||||
if (extensionContext.extensionMode !== vscode.ExtensionMode.Development ||
|
||||
!isCanary() ||
|
||||
!isNewQueryRunExperienceEnabled()) {
|
||||
// Currently, we only want to expose the new database panel when we
|
||||
// are in development and canary mode and the developer has enabled the
|
||||
// new query run experience.
|
||||
return;
|
||||
}
|
||||
|
||||
void logger.log('Initializing database module');
|
||||
|
||||
const storagePath = extensionContext.storageUri?.fsPath || extensionContext.globalStorageUri.fsPath;
|
||||
const dbConfigStore = new DbConfigStore(storagePath);
|
||||
await dbConfigStore.initialize();
|
||||
|
||||
const dbManager = new DbManager(dbConfigStore);
|
||||
dbManager.loadDatabases();
|
||||
|
||||
const dbPanel = new DbPanel(dbManager);
|
||||
|
||||
this.push(dbPanel);
|
||||
this.push(dbConfigStore);
|
||||
}
|
||||
}
|
||||
|
||||
export async function initializeDbModule(
|
||||
extensionContext: vscode.ExtensionContext
|
||||
): Promise<DbModule> {
|
||||
const dbModule = new DbModule();
|
||||
await dbModule.initialize(extensionContext);
|
||||
return dbModule;
|
||||
}
|
||||
23
extensions/ql-vscode/src/databases/ui/db-panel.ts
Normal file
23
extensions/ql-vscode/src/databases/ui/db-panel.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { DisposableObject } from '../../pure/disposable-object';
|
||||
import { DbManager } from '../db-manager';
|
||||
import { DbTreeDataProvider } from './db-tree-data-provider';
|
||||
|
||||
export class DbPanel extends DisposableObject {
|
||||
private readonly dataProvider: DbTreeDataProvider;
|
||||
|
||||
public constructor(
|
||||
dbManager: DbManager
|
||||
) {
|
||||
super();
|
||||
|
||||
this.dataProvider = new DbTreeDataProvider(dbManager);
|
||||
|
||||
const treeView = vscode.window.createTreeView('codeQLDatabasesExperimental', {
|
||||
treeDataProvider: this.dataProvider,
|
||||
canSelectMany: false
|
||||
});
|
||||
|
||||
this.push(treeView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { logger } from '../../logging';
|
||||
import { ProviderResult, TreeDataProvider, TreeItem } from 'vscode';
|
||||
import { DbTreeViewItem } from './db-tree-view-item';
|
||||
import { DbManager } from '../db-manager';
|
||||
|
||||
export class DbTreeDataProvider implements TreeDataProvider<DbTreeViewItem> {
|
||||
private dbTreeItems: DbTreeViewItem[];
|
||||
|
||||
public constructor(
|
||||
private readonly dbManager: DbManager
|
||||
) {
|
||||
this.dbTreeItems = this.createTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when expanding a node (including the root node).
|
||||
* @param node The node to expand.
|
||||
* @returns The children of the node.
|
||||
*/
|
||||
public getChildren(node?: DbTreeViewItem): ProviderResult<DbTreeViewItem[]> {
|
||||
if (!node) {
|
||||
// We're at the root.
|
||||
return Promise.resolve(this.dbTreeItems);
|
||||
} else {
|
||||
return Promise.resolve(node.children);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UI presentation of the element that gets displayed in the view.
|
||||
* @param node The node to represent.
|
||||
* @returns The UI presentation of the node.
|
||||
*/
|
||||
public getTreeItem(node: DbTreeViewItem): TreeItem | Thenable<TreeItem> {
|
||||
return node;
|
||||
}
|
||||
|
||||
private createTree(): DbTreeViewItem[] {
|
||||
const dbItems = this.dbManager.getDbItems();
|
||||
|
||||
// This will be fleshed out in a future change.
|
||||
void logger.log(`Creating database tree with ${dbItems.length} items`);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
17
extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts
Normal file
17
extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { DbItem } from '../db-item';
|
||||
|
||||
/**
|
||||
* Represents an item in the database tree view.
|
||||
*/
|
||||
export class DbTreeViewItem extends vscode.TreeItem {
|
||||
constructor(
|
||||
public readonly dbItem: DbItem,
|
||||
public readonly label: string,
|
||||
public readonly tooltip: string,
|
||||
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
|
||||
public readonly children: DbTreeViewItem[]
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ import { VariantAnalysisManager } from './remote-queries/variant-analysis-manage
|
||||
import { createVariantAnalysisContentProvider } from './remote-queries/variant-analysis-content-provider';
|
||||
import { VSCodeMockGitHubApiServer } from './mocks/vscode-mock-gh-api-server';
|
||||
import { VariantAnalysisResultsManager } from './remote-queries/variant-analysis-results-manager';
|
||||
import { initializeDbModule } from './databases/db-module';
|
||||
|
||||
/**
|
||||
* extension.ts
|
||||
@@ -1229,6 +1230,9 @@ async function activateWithInstalledDistribution(
|
||||
void logger.log('Reading query history');
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
const dbModule = await initializeDbModule(ctx);
|
||||
ctx.subscriptions.push(dbModule);
|
||||
|
||||
void logger.log('Successfully finished extension initialization.');
|
||||
|
||||
return {
|
||||
|
||||
@@ -173,33 +173,53 @@ export class HistoryTreeDataProvider extends DisposableObject implements TreeDat
|
||||
|
||||
// Populate the icon and the context value. We use the context value to
|
||||
// control which commands are visible in the context menu.
|
||||
let hasResults;
|
||||
treeItem.iconPath = this.getIconPath(element);
|
||||
treeItem.contextValue = await this.getContextValue(element);
|
||||
|
||||
return treeItem;
|
||||
}
|
||||
|
||||
private getIconPath(element: QueryHistoryInfo): ThemeIcon | string {
|
||||
switch (element.status) {
|
||||
case QueryStatus.InProgress:
|
||||
treeItem.iconPath = new ThemeIcon('sync~spin');
|
||||
treeItem.contextValue = element.t === 'local' ? 'inProgressResultsItem' : 'inProgressRemoteResultsItem';
|
||||
break;
|
||||
return new ThemeIcon('sync~spin');
|
||||
case QueryStatus.Completed:
|
||||
if (element.t === 'local') {
|
||||
hasResults = await element.completedQuery?.query.hasInterpretedResults();
|
||||
treeItem.iconPath = this.localSuccessIconPath;
|
||||
treeItem.contextValue = hasResults
|
||||
? 'interpretedResultsItem'
|
||||
: 'rawResultsItem';
|
||||
return this.localSuccessIconPath;
|
||||
} else {
|
||||
treeItem.iconPath = this.remoteSuccessIconPath;
|
||||
treeItem.contextValue = 'remoteResultsItem';
|
||||
return this.remoteSuccessIconPath;
|
||||
}
|
||||
break;
|
||||
case QueryStatus.Failed:
|
||||
treeItem.iconPath = this.failedIconPath;
|
||||
treeItem.contextValue = element.t === 'local' ? 'cancelledResultsItem' : 'cancelledRemoteResultsItem';
|
||||
break;
|
||||
return this.failedIconPath;
|
||||
default:
|
||||
assertNever(element.status);
|
||||
}
|
||||
}
|
||||
|
||||
return treeItem;
|
||||
private async getContextValue(element: QueryHistoryInfo): Promise<string> {
|
||||
switch (element.status) {
|
||||
case QueryStatus.InProgress:
|
||||
if (element.t === 'local') {
|
||||
return 'inProgressResultsItem';
|
||||
} else if (element.t === 'variant-analysis' && element.variantAnalysis.actionsWorkflowRunId === undefined) {
|
||||
return 'pendingRemoteResultsItem';
|
||||
} else {
|
||||
return 'inProgressRemoteResultsItem';
|
||||
}
|
||||
case QueryStatus.Completed:
|
||||
if (element.t === 'local') {
|
||||
const hasResults = await element.completedQuery?.query.hasInterpretedResults();
|
||||
return hasResults
|
||||
? 'interpretedResultsItem'
|
||||
: 'rawResultsItem';
|
||||
} else {
|
||||
return 'remoteResultsItem';
|
||||
}
|
||||
case QueryStatus.Failed:
|
||||
return element.t === 'local' ? 'cancelledResultsItem' : 'cancelledRemoteResultsItem';
|
||||
default:
|
||||
assertNever(element.status);
|
||||
}
|
||||
}
|
||||
|
||||
getChildren(
|
||||
|
||||
@@ -24,7 +24,7 @@ const Template: ComponentStory<typeof VariantAnalysisComponent> = (args) => (
|
||||
);
|
||||
|
||||
const variantAnalysis: VariantAnalysisDomainModel = {
|
||||
...createMockVariantAnalysis(VariantAnalysisStatus.InProgress),
|
||||
...createMockVariantAnalysis({ status: VariantAnalysisStatus.InProgress }),
|
||||
controllerRepo: {
|
||||
id: 1,
|
||||
fullName: 'octodemo/variant-analysis-controller',
|
||||
|
||||
@@ -39,64 +39,67 @@ const interpretedResultsForRepo = (nwo: string): AnalysisAlert[] | undefined =>
|
||||
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
variantAnalysis: createMockVariantAnalysis(VariantAnalysisStatus.InProgress, [
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 63537249,
|
||||
fullName: 'facebook/create-react-app',
|
||||
private: false,
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: [
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 63537249,
|
||||
fullName: 'facebook/create-react-app',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded, resultCount: 198,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded, resultCount: 198,
|
||||
},
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 167174,
|
||||
fullName: 'jquery/jquery',
|
||||
private: false,
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 167174,
|
||||
fullName: 'jquery/jquery',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 67,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 67,
|
||||
},
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 237159,
|
||||
fullName: 'expressjs/express',
|
||||
private: false,
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 237159,
|
||||
fullName: 'expressjs/express',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 26,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 26,
|
||||
},
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 15062869,
|
||||
fullName: 'facebook/jest',
|
||||
private: false,
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 15062869,
|
||||
fullName: 'facebook/jest',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 24195339,
|
||||
fullName: 'angular/angular',
|
||||
private: false,
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 24195339,
|
||||
fullName: 'angular/angular',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.InProgress,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.InProgress,
|
||||
},
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 24560307,
|
||||
fullName: 'babel/babel',
|
||||
private: false,
|
||||
{
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 24560307,
|
||||
fullName: 'babel/babel',
|
||||
private: false,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
||||
},
|
||||
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
||||
},
|
||||
]),
|
||||
]
|
||||
}),
|
||||
repositoryResults: [
|
||||
{
|
||||
variantAnalysisId: 1,
|
||||
|
||||
@@ -64,40 +64,46 @@ const Template: ComponentStory<typeof VariantAnalysisHeader> = (args) => (
|
||||
|
||||
export const InProgress = Template.bind({});
|
||||
InProgress.args = {
|
||||
variantAnalysis: createMockVariantAnalysis(VariantAnalysisStatus.InProgress, [
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 99_999,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 0,
|
||||
},
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
]),
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: [
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 99_999,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 0,
|
||||
},
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
createMockScannedRepo(),
|
||||
]
|
||||
}),
|
||||
};
|
||||
|
||||
export const Succeeded = Template.bind({});
|
||||
Succeeded.args = {
|
||||
...InProgress.args,
|
||||
variantAnalysis: {
|
||||
...createMockVariantAnalysis(VariantAnalysisStatus.Succeeded, Array.from({ length: 1000 }, (_) => ({
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 100,
|
||||
}))),
|
||||
...createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.Succeeded,
|
||||
scannedRepos: Array.from({ length: 1000 }, (_) => ({
|
||||
...createMockScannedRepo(),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 100,
|
||||
}))
|
||||
}),
|
||||
createdAt: new Date(1661262726000).toISOString(),
|
||||
completedAt: new Date(1661263446000).toISOString(),
|
||||
},
|
||||
@@ -107,7 +113,11 @@ export const Failed = Template.bind({});
|
||||
Failed.args = {
|
||||
...InProgress.args,
|
||||
variantAnalysis: {
|
||||
...createMockVariantAnalysis(VariantAnalysisStatus.Failed, [], {}),
|
||||
...createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.Failed,
|
||||
scannedRepos: [],
|
||||
skippedRepos: {}
|
||||
}),
|
||||
createdAt: new Date(1661263436000).toISOString(),
|
||||
completedAt: new Date(1661263446000).toISOString(),
|
||||
},
|
||||
|
||||
@@ -27,111 +27,118 @@ const Template: ComponentStory<typeof VariantAnalysisOutcomePanels> = (args) =>
|
||||
|
||||
export const WithoutSkippedRepos = Template.bind({});
|
||||
WithoutSkippedRepos.args = {
|
||||
variantAnalysis: createMockVariantAnalysis(VariantAnalysisStatus.InProgress, [
|
||||
{
|
||||
...createMockScannedRepo('hello-world-1'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 99_999,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-2'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-3'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 0,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-4'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-5'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-6'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-7'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-8'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-9'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-10'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
]),
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: [
|
||||
{
|
||||
...createMockScannedRepo('hello-world-1'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 99_999,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-2'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-3'),
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 0,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-4'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-5'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-6'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-7'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-8'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-9'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo('hello-world-10'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
]
|
||||
}),
|
||||
};
|
||||
|
||||
export const WithSkippedRepos = Template.bind({});
|
||||
WithSkippedRepos.args = {
|
||||
...WithoutSkippedRepos.args,
|
||||
variantAnalysis: createMockVariantAnalysis(VariantAnalysisStatus.InProgress, WithoutSkippedRepos.args.variantAnalysis?.scannedRepos, {
|
||||
notFoundRepos: {
|
||||
repositoryCount: 2,
|
||||
repositories: [
|
||||
{
|
||||
fullName: 'octodemo/hello-globe'
|
||||
},
|
||||
{
|
||||
fullName: 'octodemo/hello-planet'
|
||||
}
|
||||
]
|
||||
},
|
||||
noCodeqlDbRepos: {
|
||||
repositoryCount: 4,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 100,
|
||||
fullName: 'octodemo/no-db-1'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 101,
|
||||
fullName: 'octodemo/no-db-2'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 102,
|
||||
fullName: 'octodemo/no-db-3'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 103,
|
||||
fullName: 'octodemo/no-db-4'
|
||||
}
|
||||
]
|
||||
},
|
||||
overLimitRepos: {
|
||||
repositoryCount: 1,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 201,
|
||||
fullName: 'octodemo/over-limit-1'
|
||||
}
|
||||
]
|
||||
},
|
||||
accessMismatchRepos: {
|
||||
repositoryCount: 1,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 205,
|
||||
fullName: 'octodemo/private'
|
||||
}
|
||||
]
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: WithoutSkippedRepos.args.variantAnalysis?.scannedRepos,
|
||||
skippedRepos: {
|
||||
notFoundRepos: {
|
||||
repositoryCount: 2,
|
||||
repositories: [
|
||||
{
|
||||
fullName: 'octodemo/hello-globe'
|
||||
},
|
||||
{
|
||||
fullName: 'octodemo/hello-planet'
|
||||
}
|
||||
]
|
||||
},
|
||||
noCodeqlDbRepos: {
|
||||
repositoryCount: 4,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 100,
|
||||
fullName: 'octodemo/no-db-1'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 101,
|
||||
fullName: 'octodemo/no-db-2'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 102,
|
||||
fullName: 'octodemo/no-db-3'
|
||||
},
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 103,
|
||||
fullName: 'octodemo/no-db-4'
|
||||
}
|
||||
]
|
||||
},
|
||||
overLimitRepos: {
|
||||
repositoryCount: 1,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 201,
|
||||
fullName: 'octodemo/over-limit-1'
|
||||
}
|
||||
]
|
||||
},
|
||||
accessMismatchRepos: {
|
||||
repositoryCount: 1,
|
||||
repositories: [
|
||||
{
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 205,
|
||||
fullName: 'octodemo/private'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
};
|
||||
@@ -139,9 +146,13 @@ WithSkippedRepos.args = {
|
||||
export const WithOnlyWarningsSkippedRepos = Template.bind({});
|
||||
WithOnlyWarningsSkippedRepos.args = {
|
||||
...WithoutSkippedRepos.args,
|
||||
variantAnalysis: createMockVariantAnalysis(VariantAnalysisStatus.InProgress, WithoutSkippedRepos.args.variantAnalysis?.scannedRepos, {
|
||||
...WithSkippedRepos.args.variantAnalysis?.skippedRepos,
|
||||
notFoundRepos: undefined,
|
||||
noCodeqlDbRepos: undefined,
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: WithoutSkippedRepos.args.variantAnalysis?.scannedRepos,
|
||||
skippedRepos: {
|
||||
...WithSkippedRepos.args.variantAnalysis?.skippedRepos,
|
||||
notFoundRepos: undefined,
|
||||
noCodeqlDbRepos: undefined,
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -12,80 +12,83 @@ import { createMockScannedRepo } from '../../../vscode-tests/factories/remote-qu
|
||||
import { defaultFilterSortState, SortKey } from '../filterSort';
|
||||
|
||||
describe(VariantAnalysisAnalyzedRepos.name, () => {
|
||||
const defaultVariantAnalysis = createMockVariantAnalysis(VariantAnalysisStatus.InProgress, [
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 1,
|
||||
fullName: 'octodemo/hello-world-1',
|
||||
private: false,
|
||||
stargazersCount: 5_000,
|
||||
const defaultVariantAnalysis = createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: [
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 1,
|
||||
fullName: 'octodemo/hello-world-1',
|
||||
private: false,
|
||||
stargazersCount: 5_000,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 2,
|
||||
fullName: 'octodemo/hello-world-2',
|
||||
private: false,
|
||||
stargazersCount: 20_000,
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 2,
|
||||
fullName: 'octodemo/hello-world-2',
|
||||
private: false,
|
||||
stargazersCount: 20_000,
|
||||
},
|
||||
resultCount: 200,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
resultCount: 200,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 3,
|
||||
fullName: 'octodemo/hello-world-3',
|
||||
private: true,
|
||||
stargazersCount: 20,
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 3,
|
||||
fullName: 'octodemo/hello-world-3',
|
||||
private: true,
|
||||
stargazersCount: 20,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Failed,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 4,
|
||||
fullName: 'octodemo/hello-world-4',
|
||||
private: false,
|
||||
stargazersCount: 8_000,
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 4,
|
||||
fullName: 'octodemo/hello-world-4',
|
||||
private: false,
|
||||
stargazersCount: 8_000,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.InProgress,
|
||||
},
|
||||
resultCount: undefined,
|
||||
analysisStatus: VariantAnalysisRepoStatus.InProgress,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 5,
|
||||
fullName: 'octodemo/hello-world-5',
|
||||
private: false,
|
||||
stargazersCount: 50_000,
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 5,
|
||||
fullName: 'octodemo/hello-world-5',
|
||||
private: false,
|
||||
stargazersCount: 50_000,
|
||||
},
|
||||
resultCount: 55_323,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
resultCount: 55_323,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 6,
|
||||
fullName: 'octodemo/hello-world-6',
|
||||
private: false,
|
||||
stargazersCount: 1,
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
repository: {
|
||||
...createMockRepositoryWithMetadata(),
|
||||
id: 6,
|
||||
fullName: 'octodemo/hello-world-6',
|
||||
private: false,
|
||||
stargazersCount: 1,
|
||||
},
|
||||
resultCount: 10_000,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
resultCount: 10_000,
|
||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||
},
|
||||
]);
|
||||
]
|
||||
});
|
||||
|
||||
const render = (props: Partial<VariantAnalysisAnalyzedReposProps> = {}) => {
|
||||
return reactRender(
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createMockScannedRepo } from '../../../vscode-tests/factories/remote-qu
|
||||
|
||||
describe(VariantAnalysisOutcomePanels.name, () => {
|
||||
const defaultVariantAnalysis = {
|
||||
...createMockVariantAnalysis(VariantAnalysisStatus.InProgress),
|
||||
...createMockVariantAnalysis({ status: VariantAnalysisStatus.InProgress }),
|
||||
controllerRepo: {
|
||||
id: 1,
|
||||
fullName: 'octodemo/variant-analysis-controller',
|
||||
|
||||
@@ -178,7 +178,7 @@ describe('Variant Analysis Manager', async function() {
|
||||
let dummyVariantAnalysis: VariantAnalysis;
|
||||
|
||||
beforeEach(async () => {
|
||||
dummyVariantAnalysis = createMockVariantAnalysis();
|
||||
dummyVariantAnalysis = createMockVariantAnalysis({});
|
||||
removeAnalysisResultsStub = sandbox.stub(variantAnalysisResultsManager, 'removeAnalysisResults');
|
||||
removeStorageStub = sandbox.stub(fs, 'remove');
|
||||
});
|
||||
@@ -203,7 +203,7 @@ describe('Variant Analysis Manager', async function() {
|
||||
let monitorVariantAnalysisCommandSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
variantAnalysis = createMockVariantAnalysis();
|
||||
variantAnalysis = createMockVariantAnalysis({});
|
||||
|
||||
variantAnalysisRemovedSpy = sinon.spy();
|
||||
variantAnalysisManager.onVariantAnalysisRemoved(variantAnalysisRemovedSpy);
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('Variant Analysis Monitor', async function() {
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
variantAnalysis = createMockVariantAnalysis();
|
||||
variantAnalysis = createMockVariantAnalysis({});
|
||||
|
||||
try {
|
||||
extension = await extensions.getExtension<CodeQLExtensionInterface | Record<string, never>>('GitHub.vscode-codeql')!.activate();
|
||||
|
||||
@@ -1,84 +1,88 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import {
|
||||
InitialQueryInfo,
|
||||
CompletedQueryInfo,
|
||||
CompletedLocalQueryInfo,
|
||||
LocalQueryInfo,
|
||||
} from '../../../query-results';
|
||||
import { QueryEvaluationInfo, QueryWithResults } from '../../../run-queries-shared';
|
||||
import { CancellationTokenSource } from 'vscode';
|
||||
import { QueryResultType } from '../../../pure/legacy-messages';
|
||||
import { QueryMetadata } from '../../../pure/interface-types';
|
||||
|
||||
export function createMockLocalQueryInfo(
|
||||
startTime: string,
|
||||
userSpecifiedLabel?: string
|
||||
): LocalQueryInfo {
|
||||
return ({
|
||||
t: 'local',
|
||||
userSpecifiedLabel,
|
||||
startTime: startTime,
|
||||
getQueryFileName() {
|
||||
return 'query-file.ql';
|
||||
},
|
||||
getQueryName() {
|
||||
return 'query-name';
|
||||
},
|
||||
initialInfo: ({
|
||||
databaseInfo: {
|
||||
databaseUri: 'unused',
|
||||
name: 'db-name',
|
||||
},
|
||||
} as unknown) as InitialQueryInfo,
|
||||
completedQuery: ({
|
||||
resultCount: 456,
|
||||
statusString: 'in progress',
|
||||
} as unknown) as CompletedQueryInfo,
|
||||
} as unknown) as CompletedLocalQueryInfo;
|
||||
}
|
||||
|
||||
export function createMockLocalQuery(
|
||||
dbName = 'a',
|
||||
queryWithResults?: QueryWithResults,
|
||||
isFail = false
|
||||
): LocalQueryInfo {
|
||||
const initialQueryInfo = {
|
||||
databaseInfo: { name: dbName },
|
||||
start: new Date(),
|
||||
queryPath: 'hucairz'
|
||||
} as InitialQueryInfo;
|
||||
|
||||
export function createMockLocalQueryInfo({
|
||||
startTime = new Date(),
|
||||
resultCount = 0,
|
||||
userSpecifiedLabel = undefined,
|
||||
failureReason = undefined,
|
||||
dbName = 'db-name',
|
||||
hasMetadata = false,
|
||||
queryWithResults = undefined,
|
||||
}: {
|
||||
startTime?: Date,
|
||||
resultCount?: number,
|
||||
userSpecifiedLabel?: string,
|
||||
failureReason?: string,
|
||||
dbName?: string,
|
||||
hasMetadata?: boolean,
|
||||
queryWithResults?: QueryWithResults | undefined,
|
||||
}): LocalQueryInfo {
|
||||
const cancellationToken = {
|
||||
dispose: () => { /**/ },
|
||||
} as CancellationTokenSource;
|
||||
|
||||
const fqi = new LocalQueryInfo(
|
||||
initialQueryInfo,
|
||||
cancellationToken,
|
||||
);
|
||||
const initialQueryInfo = {
|
||||
queryText: 'select 1',
|
||||
isQuickQuery: false,
|
||||
isQuickEval: false,
|
||||
queryName: 'query-name',
|
||||
queryPath: 'query-file.ql',
|
||||
databaseInfo: {
|
||||
databaseUri: 'databaseUri',
|
||||
name: dbName,
|
||||
},
|
||||
start: startTime,
|
||||
id: faker.datatype.number().toString(),
|
||||
userSpecifiedLabel
|
||||
} as InitialQueryInfo;
|
||||
|
||||
const localQuery = new LocalQueryInfo(initialQueryInfo, cancellationToken);
|
||||
|
||||
localQuery.failureReason = failureReason;
|
||||
|
||||
if (queryWithResults) {
|
||||
fqi.completeThisQuery(queryWithResults);
|
||||
localQuery.completeThisQuery(queryWithResults);
|
||||
localQuery.completedQuery?.setResultCount(1);
|
||||
} else if (resultCount > 0) {
|
||||
const queryWithResults = createMockQueryWithResults({ hasMetadata });
|
||||
localQuery.completeThisQuery(queryWithResults);
|
||||
localQuery.completedQuery?.setResultCount(resultCount);
|
||||
}
|
||||
|
||||
if (isFail) {
|
||||
fqi.failureReason = 'failure reason';
|
||||
}
|
||||
|
||||
return fqi;
|
||||
return localQuery;
|
||||
}
|
||||
|
||||
export function createMockQueryWithResults(
|
||||
sandbox: sinon.SinonSandbox,
|
||||
export function createMockQueryWithResults({
|
||||
sandbox = undefined,
|
||||
didRunSuccessfully = true,
|
||||
hasInterpretedResults = true
|
||||
): QueryWithResults {
|
||||
hasInterpretedResults = true,
|
||||
hasMetadata = undefined
|
||||
}: {
|
||||
sandbox?: sinon.SinonSandbox,
|
||||
didRunSuccessfully?: boolean,
|
||||
hasInterpretedResults?: boolean,
|
||||
hasMetadata?: boolean,
|
||||
}): QueryWithResults {
|
||||
const dispose = sandbox ? sandbox.spy() : () => { /**/ };
|
||||
const deleteQuery = sandbox ? sandbox.stub() : () => { /**/ };
|
||||
const metadata = hasMetadata ? { name: 'query-name' } as QueryMetadata : undefined;
|
||||
|
||||
return {
|
||||
query: {
|
||||
hasInterpretedResults: () => Promise.resolve(hasInterpretedResults),
|
||||
deleteQuery: sandbox.stub(),
|
||||
deleteQuery,
|
||||
metadata,
|
||||
} as unknown as QueryEvaluationInfo,
|
||||
successful: didRunSuccessfully,
|
||||
message: 'foo',
|
||||
dispose: sandbox.spy(),
|
||||
dispose,
|
||||
result: {
|
||||
evaluationTime: 1,
|
||||
queryId: 0,
|
||||
|
||||
@@ -5,15 +5,18 @@ export function createMockRemoteQueryHistoryItem({
|
||||
date = new Date('2022-01-01T00:00:00.000Z'),
|
||||
status = QueryStatus.InProgress,
|
||||
failureReason = undefined,
|
||||
resultCount = 16,
|
||||
resultCount = undefined,
|
||||
repositoryCount = 0,
|
||||
userSpecifiedLabel = undefined,
|
||||
executionStartTime = date.getTime(),
|
||||
userSpecifiedLabel = undefined
|
||||
}: {
|
||||
date?: Date;
|
||||
status?: QueryStatus;
|
||||
failureReason?: string;
|
||||
resultCount?: number;
|
||||
repositoryCount?: number;
|
||||
repositories?: string[];
|
||||
executionStartTime?: number;
|
||||
userSpecifiedLabel?: string;
|
||||
}): RemoteQueryHistoryItem {
|
||||
return ({
|
||||
@@ -32,7 +35,7 @@ export function createMockRemoteQueryHistoryItem({
|
||||
owner: 'github',
|
||||
name: 'vscode-codeql-integration-tests',
|
||||
},
|
||||
executionStartTime: date.getTime(),
|
||||
executionStartTime,
|
||||
actionsWorkflowRunId: 1,
|
||||
repositoryCount,
|
||||
},
|
||||
|
||||
@@ -10,11 +10,17 @@ import { createMockScannedRepos } from './scanned-repositories';
|
||||
import { createMockSkippedRepos } from './skipped-repositories';
|
||||
import { createMockRepository } from './repository';
|
||||
|
||||
export function createMockVariantAnalysis(
|
||||
status: VariantAnalysisStatus = VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: VariantAnalysisScannedRepository[] = createMockScannedRepos(),
|
||||
skippedRepos: VariantAnalysisSkippedRepositories = createMockSkippedRepos()
|
||||
): VariantAnalysis {
|
||||
export function createMockVariantAnalysis({
|
||||
status = VariantAnalysisStatus.InProgress,
|
||||
scannedRepos = createMockScannedRepos(),
|
||||
skippedRepos = createMockSkippedRepos(),
|
||||
executionStartTime = faker.datatype.number()
|
||||
}: {
|
||||
status?: VariantAnalysisStatus,
|
||||
scannedRepos?: VariantAnalysisScannedRepository[],
|
||||
skippedRepos?: VariantAnalysisSkippedRepositories,
|
||||
executionStartTime?: number | undefined
|
||||
}): VariantAnalysis {
|
||||
const variantAnalysis: VariantAnalysis = {
|
||||
id: faker.datatype.number(),
|
||||
controllerRepo: {
|
||||
@@ -32,7 +38,7 @@ export function createMockVariantAnalysis(
|
||||
databases: {
|
||||
repositories: ['1', '2', '3'],
|
||||
},
|
||||
executionStartTime: faker.datatype.number(),
|
||||
executionStartTime,
|
||||
createdAt: faker.date.recent().toISOString(),
|
||||
updatedAt: faker.date.recent().toISOString(),
|
||||
status: status,
|
||||
|
||||
@@ -3,20 +3,31 @@ import { QueryStatus } from '../../../query-status';
|
||||
import { VariantAnalysisStatus } from '../../../remote-queries/shared/variant-analysis';
|
||||
import { createMockVariantAnalysis } from './shared/variant-analysis';
|
||||
|
||||
export function createMockVariantAnalysisHistoryItem(
|
||||
historyItemStatus: QueryStatus = QueryStatus.InProgress,
|
||||
variantAnalysisStatus: VariantAnalysisStatus = VariantAnalysisStatus.Succeeded,
|
||||
failureReason?: string,
|
||||
export function createMockVariantAnalysisHistoryItem({
|
||||
historyItemStatus = QueryStatus.InProgress,
|
||||
variantAnalysisStatus = VariantAnalysisStatus.Succeeded,
|
||||
failureReason = undefined,
|
||||
resultCount = 0,
|
||||
userSpecifiedLabel = 'query-name',
|
||||
executionStartTime = undefined
|
||||
}: {
|
||||
historyItemStatus?: QueryStatus,
|
||||
variantAnalysisStatus?: VariantAnalysisStatus,
|
||||
failureReason?: string | undefined,
|
||||
resultCount?: number,
|
||||
userSpecifiedLabel?: string
|
||||
): VariantAnalysisHistoryItem {
|
||||
userSpecifiedLabel?: string,
|
||||
executionStartTime?: number
|
||||
}): VariantAnalysisHistoryItem {
|
||||
return ({
|
||||
t: 'variant-analysis',
|
||||
failureReason,
|
||||
resultCount,
|
||||
status: historyItemStatus,
|
||||
completed: false,
|
||||
variantAnalysis: createMockVariantAnalysis(variantAnalysisStatus),
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: variantAnalysisStatus,
|
||||
executionStartTime: executionStartTime
|
||||
}),
|
||||
userSpecifiedLabel,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ import { QueryHistoryConfig } from '../../config';
|
||||
import { HistoryItemLabelProvider } from '../../history-item-label-provider';
|
||||
import { createMockLocalQueryInfo } from '../factories/local-queries/local-query-history-item';
|
||||
import { createMockRemoteQueryHistoryItem } from '../factories/remote-queries/remote-query-history-item';
|
||||
import { QueryStatus } from '../../query-status';
|
||||
|
||||
describe('HistoryItemLabelProvider', () => {
|
||||
let labelProvider: HistoryItemLabelProvider;
|
||||
let config: QueryHistoryConfig;
|
||||
const date = new Date('2022-01-01T00:00:00.000Z');
|
||||
const dateStr = date.toLocaleString(env.language);
|
||||
const executionStartTime = date.getTime();
|
||||
const userSpecifiedLabel = 'user-specified-name';
|
||||
|
||||
beforeEach(() => {
|
||||
config = {
|
||||
@@ -20,47 +23,50 @@ describe('HistoryItemLabelProvider', () => {
|
||||
|
||||
describe('local queries', () => {
|
||||
it('should interpolate query when user specified', () => {
|
||||
const fqi = createMockLocalQueryInfo(dateStr, 'xxx');
|
||||
const fqi = createMockLocalQueryInfo({ startTime: date, userSpecifiedLabel, resultCount: 456, hasMetadata: true });
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq('xxx');
|
||||
expect(labelProvider.getLabel(fqi)).to.eq('user-specified-name');
|
||||
|
||||
fqi.userSpecifiedLabel = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name in progress query-file.ql (456 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`);
|
||||
|
||||
fqi.userSpecifiedLabel = '%t %q %d %s %f %r %%::%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name in progress query-file.ql (456 results) %::${dateStr} query-name db-name in progress query-file.ql (456 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`);
|
||||
});
|
||||
|
||||
it('should interpolate query when not user specified', () => {
|
||||
const fqi = createMockLocalQueryInfo(dateStr);
|
||||
const fqi = createMockLocalQueryInfo({ startTime: date, resultCount: 456, hasMetadata: true });
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq('xxx query-name xxx');
|
||||
|
||||
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name in progress query-file.ql (456 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`);
|
||||
|
||||
config.format = '%t %q %d %s %f %r %%::%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name in progress query-file.ql (456 results) %::${dateStr} query-name db-name in progress query-file.ql (456 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`);
|
||||
});
|
||||
|
||||
it('should get query short label', () => {
|
||||
const fqi = createMockLocalQueryInfo(dateStr, 'xxx');
|
||||
const fqi = createMockLocalQueryInfo({ startTime: date, userSpecifiedLabel, hasMetadata: true, resultCount: 456 });
|
||||
|
||||
// fall back on user specified if one exists.
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('xxx');
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('user-specified-name');
|
||||
|
||||
// use query name if no user-specified label exists
|
||||
delete (fqi as any).userSpecifiedLabel;
|
||||
fqi.userSpecifiedLabel = undefined;
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('query-name');
|
||||
|
||||
// use file name if no user-specified label exists and the query is not yet completed (meaning it has no results)
|
||||
const fqi2 = createMockLocalQueryInfo({ startTime: date, hasMetadata: true });
|
||||
expect(labelProvider.getShortLabel(fqi2)).to.eq('query-file.ql');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remote queries', () => {
|
||||
it('should interpolate query when user specified', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'xxx' });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ userSpecifiedLabel });
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq('xxx');
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(userSpecifiedLabel);
|
||||
|
||||
fqi.userSpecifiedLabel = '%t %q %d %s %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress %`);
|
||||
@@ -70,73 +76,73 @@ describe('HistoryItemLabelProvider', () => {
|
||||
});
|
||||
|
||||
it('should interpolate query when not user-specified', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({});
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 16 });
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq('xxx query-name (javascript) xxx');
|
||||
|
||||
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress query-file.ql (16 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %`);
|
||||
|
||||
config.format = '%t %q %d %s %f %r %%::%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress query-file.ql (16 results) %::${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress query-file.ql (16 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %::${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %`);
|
||||
});
|
||||
|
||||
it('should use number of repositories instead of controller repo if available', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 16, repositoryCount: 2 });
|
||||
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories in progress query-file.ql (16 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories completed query-file.ql (16 results) %`);
|
||||
});
|
||||
|
||||
it('should get query short label', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'xxx' });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, userSpecifiedLabel });
|
||||
|
||||
// fall back on user specified if one exists.
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('xxx');
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('user-specified-name');
|
||||
|
||||
// use query name if no user-specified label exists
|
||||
delete (fqi as any).userSpecifiedLabel;
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq('query-name');
|
||||
const fqi2 = createMockRemoteQueryHistoryItem({});
|
||||
|
||||
expect(labelProvider.getShortLabel(fqi2)).to.eq('query-name');
|
||||
});
|
||||
|
||||
describe('when results are present', () => {
|
||||
it('should display results if there are any', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ resultCount: 16, repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 16, repositoryCount: 2 });
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories in progress query-file.ql (16 results) %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories completed query-file.ql (16 results) %`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when results are not present', () => {
|
||||
it('should skip displaying them', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ resultCount: 0, repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 0, repositoryCount: 2 });
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories in progress query-file.ql %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when extra whitespace is present in the middle of the label', () => {
|
||||
it('should squash it down to a single whitespace', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ resultCount: 0, repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 0, repositoryCount: 2 });
|
||||
config.format = '%t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories in progress query-file.ql %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when extra whitespace is present at the start of the label', () => {
|
||||
it('should squash it down to a single whitespace', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ resultCount: 0, repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 0, repositoryCount: 2 });
|
||||
config.format = ' %t %q %d %s %f %r %%';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(` ${dateStr} query-name (javascript) 2 repositories in progress query-file.ql %`);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(` ${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when extra whitespace is present at the end of the label', () => {
|
||||
it('should squash it down to a single whitespace', () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ resultCount: 0, repositoryCount: 2 });
|
||||
const fqi = createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed, executionStartTime, resultCount: 0, repositoryCount: 2 });
|
||||
config.format = '%t %q %d %s %f %r %% ';
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories in progress query-file.ql % `);
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(`${dateStr} query-name (javascript) 2 repositories completed query-file.ql % `);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,22 +17,21 @@ import { VariantAnalysisRepoStatus, VariantAnalysisStatus } from '../../remote-q
|
||||
describe('Query history info', () => {
|
||||
|
||||
const date = new Date('2022-01-01T00:00:00.000Z');
|
||||
const dateStr = date.toLocaleString();
|
||||
const localQueryHistoryItem = createMockLocalQueryInfo(dateStr);
|
||||
const localQueryHistoryItem = createMockLocalQueryInfo({ startTime: date });
|
||||
const remoteQueryHistoryItem = createMockRemoteQueryHistoryItem({});
|
||||
const variantAnalysisHistoryItem: VariantAnalysisHistoryItem = {
|
||||
t: 'variant-analysis',
|
||||
status: QueryStatus.InProgress,
|
||||
completed: false,
|
||||
variantAnalysis: createMockVariantAnalysis(
|
||||
VariantAnalysisStatus.InProgress,
|
||||
createMockScannedRepos([
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: createMockScannedRepos([
|
||||
VariantAnalysisRepoStatus.Succeeded,
|
||||
VariantAnalysisRepoStatus.Pending,
|
||||
VariantAnalysisRepoStatus.InProgress,
|
||||
VariantAnalysisRepoStatus.Canceled,
|
||||
])
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
describe('getRawQueryName', () => {
|
||||
@@ -117,10 +116,10 @@ describe('Query history info', () => {
|
||||
t: 'variant-analysis',
|
||||
status: QueryStatus.InProgress,
|
||||
completed: false,
|
||||
variantAnalysis: createMockVariantAnalysis(
|
||||
VariantAnalysisStatus.InProgress,
|
||||
createMockScannedRepos([])
|
||||
),
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: createMockScannedRepos([])
|
||||
}),
|
||||
};
|
||||
const repoLabel0 = buildRepoLabel(variantAnalysisHistoryItem0);
|
||||
|
||||
@@ -131,12 +130,10 @@ describe('Query history info', () => {
|
||||
t: 'variant-analysis',
|
||||
status: QueryStatus.InProgress,
|
||||
completed: false,
|
||||
variantAnalysis: createMockVariantAnalysis(
|
||||
VariantAnalysisStatus.InProgress,
|
||||
createMockScannedRepos([
|
||||
VariantAnalysisRepoStatus.Pending,
|
||||
])
|
||||
),
|
||||
variantAnalysis: createMockVariantAnalysis({
|
||||
status: VariantAnalysisStatus.InProgress,
|
||||
scannedRepos: createMockScannedRepos([VariantAnalysisRepoStatus.Pending])
|
||||
}),
|
||||
};
|
||||
|
||||
const repoLabel1 = buildRepoLabel(variantAnalysisHistoryItem1);
|
||||
|
||||
@@ -21,13 +21,17 @@ import { EvalLogViewer } from '../../eval-log-viewer';
|
||||
import { QueryRunner } from '../../queryRunner';
|
||||
import { VariantAnalysisManager } from '../../remote-queries/variant-analysis-manager';
|
||||
import { QueryHistoryInfo } from '../../query-history-info';
|
||||
import { createMockLocalQuery, createMockQueryWithResults } from '../factories/local-queries/local-query-history-item';
|
||||
import {
|
||||
createMockLocalQueryInfo,
|
||||
createMockQueryWithResults
|
||||
} from '../factories/local-queries/local-query-history-item';
|
||||
import { createMockRemoteQueryHistoryItem } from '../factories/remote-queries/remote-query-history-item';
|
||||
import { RemoteQueryHistoryItem } from '../../remote-queries/remote-query-history-item';
|
||||
import { shuffleHistoryItems } from '../utils/query-history-helpers';
|
||||
import { createMockVariantAnalysisHistoryItem } from '../factories/remote-queries/variant-analysis-history-item';
|
||||
import { VariantAnalysisHistoryItem } from '../../remote-queries/variant-analysis-history-item';
|
||||
import { QueryStatus } from '../../query-status';
|
||||
import { VariantAnalysisStatus } from '../../remote-queries/shared/variant-analysis';
|
||||
|
||||
describe('query-history', () => {
|
||||
const mockExtensionLocation = path.join(tmpDir.name, 'mock-extension-location');
|
||||
@@ -88,10 +92,10 @@ describe('query-history', () => {
|
||||
} as any as VariantAnalysisManager;
|
||||
|
||||
localQueryHistory = [
|
||||
createMockLocalQuery('a', createMockQueryWithResults(sandbox, true)),
|
||||
createMockLocalQuery('b', createMockQueryWithResults(sandbox, true)),
|
||||
createMockLocalQuery('a', createMockQueryWithResults(sandbox, false)),
|
||||
createMockLocalQuery('a', createMockQueryWithResults(sandbox, true)),
|
||||
createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: true }) }),
|
||||
createMockLocalQueryInfo({ dbName: 'b', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: true }) }),
|
||||
createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: false }) }),
|
||||
createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: true }) }),
|
||||
];
|
||||
remoteQueryHistory = [
|
||||
createMockRemoteQueryHistoryItem({ status: QueryStatus.Completed }),
|
||||
@@ -100,13 +104,24 @@ describe('query-history', () => {
|
||||
createMockRemoteQueryHistoryItem({ status: QueryStatus.InProgress })
|
||||
];
|
||||
variantAnalysisHistory = [
|
||||
createMockVariantAnalysisHistoryItem(QueryStatus.Completed),
|
||||
createMockVariantAnalysisHistoryItem(QueryStatus.InProgress),
|
||||
createMockVariantAnalysisHistoryItem(QueryStatus.Failed),
|
||||
createMockVariantAnalysisHistoryItem(QueryStatus.InProgress)
|
||||
createMockVariantAnalysisHistoryItem({
|
||||
historyItemStatus: QueryStatus.Completed,
|
||||
variantAnalysisStatus: VariantAnalysisStatus.Succeeded
|
||||
}),
|
||||
createMockVariantAnalysisHistoryItem({
|
||||
historyItemStatus: QueryStatus.InProgress,
|
||||
variantAnalysisStatus: VariantAnalysisStatus.InProgress
|
||||
}),
|
||||
createMockVariantAnalysisHistoryItem({
|
||||
historyItemStatus: QueryStatus.Failed,
|
||||
variantAnalysisStatus: VariantAnalysisStatus.Failed
|
||||
}),
|
||||
createMockVariantAnalysisHistoryItem({
|
||||
historyItemStatus: QueryStatus.InProgress,
|
||||
variantAnalysisStatus: VariantAnalysisStatus.InProgress
|
||||
})
|
||||
];
|
||||
allHistory = shuffleHistoryItems([...localQueryHistory, ...remoteQueryHistory, ...variantAnalysisHistory]);
|
||||
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -415,7 +430,7 @@ describe('query-history', () => {
|
||||
it('should throw an error when a query is not successful', async () => {
|
||||
const thisQuery = localQueryHistory[3];
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
allHistory[0] = createMockLocalQuery('a', createMockQueryWithResults(sandbox, false));
|
||||
allHistory[0] = createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: false }) });
|
||||
|
||||
try {
|
||||
await (queryHistoryManager as any).findOtherQueryToCompare(thisQuery, [thisQuery, allHistory[0]]);
|
||||
@@ -696,40 +711,45 @@ describe('query-history', () => {
|
||||
|
||||
describe('getTreeItem', async () => {
|
||||
it('should get a tree item with raw results', async () => {
|
||||
const mockQuery = createMockLocalQuery('a', createMockQueryWithResults(sandbox, true, /* raw results */ false));
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery);
|
||||
const mockQueryWithRawResults = createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: true, hasInterpretedResults: false }) });
|
||||
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQueryWithRawResults);
|
||||
expect(treeItem.command).to.deep.eq({
|
||||
title: 'Query History Item',
|
||||
command: 'codeQLQueryHistory.itemClicked',
|
||||
arguments: [mockQuery],
|
||||
tooltip: labelProvider.getLabel(mockQuery),
|
||||
arguments: [mockQueryWithRawResults],
|
||||
tooltip: labelProvider.getLabel(mockQueryWithRawResults),
|
||||
});
|
||||
expect(treeItem.label).to.contain('hucairz');
|
||||
expect(treeItem.label).to.contain('query-file.ql');
|
||||
expect(treeItem.contextValue).to.eq('rawResultsItem');
|
||||
expect(treeItem.iconPath).to.deep.eq(vscode.Uri.file(mockExtensionLocation + '/media/drive.svg').fsPath);
|
||||
});
|
||||
|
||||
it('should get a tree item with interpreted results', async () => {
|
||||
const mockQuery = createMockLocalQuery('a', createMockQueryWithResults(sandbox, true, /* interpreted results */ true));
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery);
|
||||
const mockQueryWithInterpretedResults = createMockLocalQueryInfo({ dbName: 'a', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: true, hasInterpretedResults: true }) });
|
||||
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQueryWithInterpretedResults);
|
||||
expect(treeItem.contextValue).to.eq('interpretedResultsItem');
|
||||
expect(treeItem.iconPath).to.deep.eq(vscode.Uri.file(mockExtensionLocation + '/media/drive.svg').fsPath);
|
||||
});
|
||||
|
||||
it('should get a tree item that did not complete successfully', async () => {
|
||||
const mockQuery = createMockLocalQuery('a', createMockQueryWithResults(sandbox, false), false);
|
||||
const mockQuery = createMockLocalQueryInfo({ dbName: 'a', failureReason: 'failure reason', queryWithResults: createMockQueryWithResults({ sandbox, didRunSuccessfully: false }) });
|
||||
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery);
|
||||
expect(treeItem.iconPath).to.eq(vscode.Uri.file(mockExtensionLocation + '/media/red-x.svg').fsPath);
|
||||
});
|
||||
|
||||
it('should get a tree item that failed before creating any results', async () => {
|
||||
const mockQuery = createMockLocalQuery('a', undefined, true);
|
||||
const mockQuery = createMockLocalQueryInfo({ dbName: 'a', failureReason: 'failure reason' });
|
||||
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery);
|
||||
expect(treeItem.iconPath).to.eq(vscode.Uri.file(mockExtensionLocation + '/media/red-x.svg').fsPath);
|
||||
});
|
||||
|
||||
it('should get a tree item that is in progress', async () => {
|
||||
const mockQuery = createMockLocalQuery('a');
|
||||
const mockQuery = createMockLocalQueryInfo({ dbName: 'a' });
|
||||
|
||||
const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery);
|
||||
expect(treeItem.iconPath).to.deep.eq({
|
||||
id: 'sync~spin', color: undefined
|
||||
@@ -739,7 +759,7 @@ describe('query-history', () => {
|
||||
|
||||
describe('getChildren', () => {
|
||||
it('fetch children correctly', () => {
|
||||
const mockQuery = createMockLocalQuery();
|
||||
const mockQuery = createMockLocalQueryInfo({});
|
||||
historyTreeDataProvider.allHistory.push(mockQuery);
|
||||
expect(historyTreeDataProvider.getChildren()).to.deep.eq([mockQuery]);
|
||||
expect(historyTreeDataProvider.getChildren(mockQuery)).to.deep.eq([]);
|
||||
@@ -747,12 +767,16 @@ describe('query-history', () => {
|
||||
|
||||
describe('sorting', () => {
|
||||
const history = [
|
||||
item('a', 2, 'remote', 24),
|
||||
item('b', 10, 'local', 20),
|
||||
item('c', 5, 'local', 30),
|
||||
item('d', 1, 'local', 25),
|
||||
item('e', 6, 'remote', 5),
|
||||
createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'a', executionStartTime: 2, resultCount: 24, status: QueryStatus.Completed }),
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'b', startTime: new Date(10), resultCount: 20 }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'c', executionStartTime: 15, resultCount: 456, historyItemStatus: QueryStatus.Completed, variantAnalysisStatus: VariantAnalysisStatus.Succeeded }),
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'd', startTime: new Date(5), resultCount: 30 }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'e', executionStartTime: 50, resultCount: 15, historyItemStatus: QueryStatus.InProgress, variantAnalysisStatus: VariantAnalysisStatus.InProgress }),
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'f', startTime: new Date(1), resultCount: 13 }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'g', executionStartTime: 7, resultCount: 30, historyItemStatus: QueryStatus.Failed, variantAnalysisStatus: VariantAnalysisStatus.Failed }),
|
||||
createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'h', executionStartTime: 6, resultCount: 5, status: QueryStatus.InProgress })
|
||||
];
|
||||
|
||||
let treeDataProvider: HistoryTreeDataProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -778,7 +802,7 @@ describe('query-history', () => {
|
||||
});
|
||||
|
||||
it('should get children for date ascending', async () => {
|
||||
const expected = [history[3], history[0], history[2], history[4], history[1]];
|
||||
const expected = [history[5], history[0], history[3], history[7], history[6], history[1], history[2], history[4]];
|
||||
treeDataProvider.sortOrder = SortOrder.DateAsc;
|
||||
|
||||
const children = await treeDataProvider.getChildren();
|
||||
@@ -786,7 +810,8 @@ describe('query-history', () => {
|
||||
});
|
||||
|
||||
it('should get children for date descending', async () => {
|
||||
const expected = [history[3], history[0], history[2], history[4], history[1]].reverse();
|
||||
const expected = [history[5], history[0], history[3], history[7], history[6], history[1], history[2], history[4]].reverse();
|
||||
|
||||
treeDataProvider.sortOrder = SortOrder.DateDesc;
|
||||
|
||||
const children = await treeDataProvider.getChildren();
|
||||
@@ -794,35 +819,48 @@ describe('query-history', () => {
|
||||
});
|
||||
|
||||
it('should get children for result count ascending', async () => {
|
||||
const expected = [history[4], history[1], history[0], history[3], history[2]];
|
||||
const expected = [history[7], history[5], history[4], history[1], history[0], history[3], history[6], history[2]];
|
||||
treeDataProvider.sortOrder = SortOrder.CountAsc;
|
||||
|
||||
const children = await treeDataProvider.getChildren();
|
||||
|
||||
expect(children).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
it('should get children for result count descending', async () => {
|
||||
const expected = [history[4], history[1], history[0], history[3], history[2]].reverse();
|
||||
const expected = [history[7], history[5], history[4], history[1], history[0], history[3], history[6], history[2]].reverse();
|
||||
treeDataProvider.sortOrder = SortOrder.CountDesc;
|
||||
|
||||
const children = await treeDataProvider.getChildren();
|
||||
expect(children).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
it('should get children for result count ascending when there are no results', async () => {
|
||||
// fall back to name
|
||||
const thisHistory = [item('a', 10), item('b', 50), item('c', 1)];
|
||||
it('should fall back to name ascending when there are no results', async () => {
|
||||
const thisHistory = [
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'a', resultCount: 0, startTime: new Date(10) }),
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'b', resultCount: 0, startTime: new Date(1) }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'c', resultCount: 0, historyItemStatus: QueryStatus.Completed, variantAnalysisStatus: VariantAnalysisStatus.Failed }),
|
||||
createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'd', resultCount: 0, executionStartTime: 50, status: QueryStatus.Completed }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'e', resultCount: 0, historyItemStatus: QueryStatus.InProgress, variantAnalysisStatus: VariantAnalysisStatus.Failed }),
|
||||
];
|
||||
(queryHistoryManager!.treeDataProvider as any).history = [...thisHistory];
|
||||
const expected = [...thisHistory];
|
||||
|
||||
treeDataProvider.sortOrder = SortOrder.CountAsc;
|
||||
|
||||
const children = await treeDataProvider.getChildren();
|
||||
|
||||
expect(children).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
it('should get children for result count descending when there are no results', async () => {
|
||||
// fall back to name
|
||||
const thisHistory = [item('a', 10), item('b', 50), item('c', 1)];
|
||||
it('should fall back to name descending when there are no results', async () => {
|
||||
const thisHistory = [
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'a', resultCount: 0, startTime: new Date(10) }),
|
||||
createMockLocalQueryInfo({ userSpecifiedLabel: 'b', resultCount: 0, startTime: new Date(1) }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'c', resultCount: 0, historyItemStatus: QueryStatus.Completed, variantAnalysisStatus: VariantAnalysisStatus.Failed }),
|
||||
createMockRemoteQueryHistoryItem({ userSpecifiedLabel: 'd', resultCount: 0, executionStartTime: 50, status: QueryStatus.Completed }),
|
||||
createMockVariantAnalysisHistoryItem({ userSpecifiedLabel: 'e', resultCount: 0, historyItemStatus: QueryStatus.InProgress, variantAnalysisStatus: VariantAnalysisStatus.Failed }),
|
||||
];
|
||||
(queryHistoryManager!.treeDataProvider as any).history = [...thisHistory];
|
||||
const expected = [...thisHistory].reverse();
|
||||
treeDataProvider.sortOrder = SortOrder.CountDesc;
|
||||
@@ -830,45 +868,6 @@ describe('query-history', () => {
|
||||
const children = await treeDataProvider.getChildren();
|
||||
expect(children).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
function item(label: string, start: number, t = 'local', resultCount?: number) {
|
||||
if (t === 'local') {
|
||||
return {
|
||||
getQueryName() {
|
||||
return label;
|
||||
},
|
||||
getQueryFileName() {
|
||||
return label + '.ql';
|
||||
},
|
||||
initialInfo: {
|
||||
start: new Date(start),
|
||||
databaseInfo: {
|
||||
name: 'test',
|
||||
}
|
||||
},
|
||||
completedQuery: {
|
||||
resultCount,
|
||||
},
|
||||
t
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: 'success',
|
||||
remoteQuery: {
|
||||
queryFilePath: label + '.ql',
|
||||
queryName: label,
|
||||
executionStartTime: start,
|
||||
controllerRepository: {
|
||||
name: 'test',
|
||||
owner: 'user',
|
||||
},
|
||||
repositories: []
|
||||
},
|
||||
resultCount,
|
||||
t
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"remote": {
|
||||
"repositoryLists": [
|
||||
{
|
||||
"name": "repoList1",
|
||||
"repositories": ["foo/bar", "foo/baz"]
|
||||
}
|
||||
],
|
||||
"owners": [],
|
||||
"repositories": ["owner/repo1", "owner/repo2", "owner/repo3"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { DbConfigStore } from '../../../src/databases/db-config-store';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('db config store', async () => {
|
||||
const tempWorkspaceStoragePath = path.join(__dirname, 'test-workspace');
|
||||
const testDataStoragePath = path.join(__dirname, 'data');
|
||||
|
||||
beforeEach(async () => {
|
||||
await fs.ensureDir(tempWorkspaceStoragePath);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.remove(tempWorkspaceStoragePath);
|
||||
});
|
||||
|
||||
it('should create a new config if one does not exist', async () => {
|
||||
const configPath = path.join(tempWorkspaceStoragePath, 'workspace-databases.json');
|
||||
|
||||
const configStore = new DbConfigStore(tempWorkspaceStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
expect(await fs.pathExists(configPath)).to.be.true;
|
||||
const config = configStore.getConfig();
|
||||
expect(config.remote.repositoryLists).to.be.empty;
|
||||
expect(config.remote.owners).to.be.empty;
|
||||
expect(config.remote.repositories).to.be.empty;
|
||||
});
|
||||
|
||||
it('should load an existing config', async () => {
|
||||
const configStore = new DbConfigStore(testDataStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig();
|
||||
expect(config.remote.repositoryLists).to.have.length(1);
|
||||
expect(config.remote.repositoryLists[0]).to.deep.equal({
|
||||
'name': 'repoList1',
|
||||
'repositories': ['foo/bar', 'foo/baz']
|
||||
});
|
||||
expect(config.remote.owners).to.be.empty;
|
||||
expect(config.remote.repositories).to.have.length(3);
|
||||
expect(config.remote.repositories).to.deep.equal(['owner/repo1', 'owner/repo2', 'owner/repo3']);
|
||||
});
|
||||
|
||||
it('should not allow modification of the config', async () => {
|
||||
const configStore = new DbConfigStore(testDataStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig();
|
||||
config.remote.repositoryLists = [];
|
||||
|
||||
const reRetrievedConfig = configStore.getConfig();
|
||||
expect(reRetrievedConfig.remote.repositoryLists).to.have.length(1);
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,7 @@ describe('isVariantAnalysisComplete', async () => {
|
||||
const uncallableArtifactDownloadChecker = () => { throw new Error('Should not be called'); };
|
||||
|
||||
beforeEach(() => {
|
||||
variantAnalysis = createMockVariantAnalysis();
|
||||
variantAnalysis = createMockVariantAnalysis({});
|
||||
});
|
||||
|
||||
describe('when variant analysis status is InProgress', async () => {
|
||||
@@ -104,7 +104,7 @@ describe('isVariantAnalysisComplete', async () => {
|
||||
|
||||
describe('getActionsWorkflowRunUrl', () => {
|
||||
it('should get the run url', () => {
|
||||
const variantAnalysis = createMockVariantAnalysis();
|
||||
const variantAnalysis = createMockVariantAnalysis({});
|
||||
|
||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user