Merge remote-tracking branch 'origin/main' into koesie10/add-metadata-to-repo-row

This commit is contained in:
Koen Vlaswinkel
2022-11-02 14:05:08 +01:00
75 changed files with 1456 additions and 993 deletions

View File

@@ -1,6 +1,4 @@
{
"exit": true,
"require": [
"test/mocha.setup.js"
]
"require": ["test/mocha.setup.js"]
}

View File

@@ -1256,9 +1256,11 @@
"test": "npm-run-all -p test:*",
"test:unit": "mocha --config .mocharc.json test/pure-tests/**/*.ts",
"test:view": "jest",
"preintegration": "rm -rf ./out/vscode-tests && gulp",
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
"cli-integration": "npm run preintegration && node ./out/vscode-tests/run-integration-tests.js cli-integration",
"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",
"update-vscode": "node ./node_modules/vscode/bin/install",
"format": "tsfmt -r && eslint . --ext .ts,.tsx --fix",
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",

View File

@@ -525,9 +525,6 @@ async function activateWithInstalledDistribution(
ctx.subscriptions.push(logScannerService);
ctx.subscriptions.push(logScannerService.scanners.registerLogScannerProvider(new JoinOrderScannerProvider(() => joinOrderWarningThreshold())));
void logger.log('Reading query history');
await qhm.readQueryHistory();
void logger.log('Initializing compare view.');
const compareView = new CompareView(
ctx,
@@ -1229,6 +1226,9 @@ async function activateWithInstalledDistribution(
await commands.executeCommand('codeQLDatabases.removeOrphanedDatabases');
void logger.log('Reading query history');
await qhm.readQueryHistory();
void logger.log('Successfully finished extension initialization.');
return {

View File

@@ -7,6 +7,7 @@ import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-it
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
import { assertNever } from './pure/helpers-pure';
import { pluralize } from './pure/word';
import { humanizeQueryStatus } from './query-status';
interface InterpolateReplacements {
t: string; // Start time
@@ -86,7 +87,7 @@ export class HistoryItemLabelProvider {
q: `${item.remoteQuery.queryName} (${item.remoteQuery.language})`,
d: buildRepoLabel(item),
r: resultCount,
s: item.status,
s: humanizeQueryStatus(item.status),
f: path.basename(item.remoteQuery.queryFilePath),
'%': '%'
};
@@ -99,7 +100,7 @@ export class HistoryItemLabelProvider {
q: `${item.variantAnalysis.query.name} (${item.variantAnalysis.query.language})`,
d: buildRepoLabel(item),
r: resultCount,
s: item.status,
s: humanizeQueryStatus(item.status),
f: path.basename(item.variantAnalysis.query.filePath),
'%': '%',
};

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -970,7 +970,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -972,7 +972,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -972,7 +972,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -972,7 +972,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -972,7 +972,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -972,7 +972,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -973,7 +973,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -962,7 +962,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -966,7 +966,7 @@
}
]
},
"not_found_repo_nwos": {
"not_found_repos": {
"repository_count": 110,
"repository_full_names": [
"evanw/node-source-map-support",

View File

@@ -90,3 +90,7 @@ function createFormatter(unit: string) {
unitDisplay: 'long'
});
}
export async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

View File

@@ -694,7 +694,7 @@ export class QueryHistoryManager extends DisposableObject {
await this.remoteQueriesManager.rehydrateRemoteQuery(item.queryId, item.remoteQuery, item.status);
}
if (item.t === 'variant-analysis') {
await this.variantAnalysisManager.rehydrateVariantAnalysis(item.variantAnalysis, item.status);
await this.variantAnalysisManager.rehydrateVariantAnalysis(item.variantAnalysis);
}
}));
}

View File

@@ -21,3 +21,16 @@ export function variantAnalysisStatusToQueryStatus(status: VariantAnalysisStatus
assertNever(status);
}
}
export function humanizeQueryStatus(status: QueryStatus): string {
switch (status) {
case QueryStatus.InProgress:
return 'in progress';
case QueryStatus.Completed:
return 'completed';
case QueryStatus.Failed:
return 'failed';
default:
return 'unknown';
}
}

View File

@@ -79,7 +79,7 @@ export interface VariantAnalysisRepoTask {
export interface VariantAnalysisSkippedRepositories {
access_mismatch_repos?: VariantAnalysisSkippedRepositoryGroup,
not_found_repo_nwos?: VariantAnalysisNotFoundRepositoryGroup,
not_found_repos?: VariantAnalysisNotFoundRepositoryGroup,
no_codeql_db_repos?: VariantAnalysisSkippedRepositoryGroup,
over_limit_repos?: VariantAnalysisSkippedRepositoryGroup
}

View File

@@ -205,6 +205,7 @@ function generateMarkdownForPathResults(
const stepCount = codeFlow.threadFlows.length;
const title = `Path with ${stepCount} steps`;
for (let i = 0; i < stepCount; i++) {
const listNumber = i + 1;
const threadFlow = codeFlow.threadFlows[i];
const link = createMarkdownRemoteFileRef(
threadFlow.fileLink,
@@ -217,8 +218,9 @@ function generateMarkdownForPathResults(
threadFlow.highlightedRegion
);
// Indent the snippet to fit with the numbered list.
const codeSnippetIndented = codeSnippet.map((line) => ` ${line}`);
pathLines.push(`${i + 1}. ${link}`, ...codeSnippetIndented);
// The indentation is "n + 2" where the list number is an n-digit number.
const codeSnippetIndented = codeSnippet.map(line => ' '.repeat(listNumber.toString().length + 2) + line);
pathLines.push(`${listNumber}. ${link}`, ...codeSnippetIndented);
}
lines.push(
...buildExpandableMarkdownSection(title, pathLines)

View File

@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { Credentials } from '../authentication';
import { Logger } from '../logging';
import { sleep } from '../pure/time';
import { getWorkflowStatus, isArtifactAvailable, RESULT_INDEX_ARTIFACT_NAME } from './gh-api/gh-actions-api-client';
import { RemoteQuery } from './remote-query';
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
@@ -30,7 +31,7 @@ export class RemoteQueriesMonitor {
let attemptCount = 0;
while (attemptCount <= RemoteQueriesMonitor.maxAttemptCount) {
await this.sleep(RemoteQueriesMonitor.sleepTime);
await sleep(RemoteQueriesMonitor.sleepTime);
if (cancellationToken && cancellationToken.isCancellationRequested) {
return { status: 'Cancelled' };
@@ -70,10 +71,6 @@ export class RemoteQueriesMonitor {
void this.logger.log('Variant analysis monitoring timed out after 2 days');
return { status: 'Cancelled' };
}
private async sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

View File

@@ -1,16 +1,11 @@
import { VariantAnalysis } from './variant-analysis';
export type VariantAnalysisMonitorStatus =
| 'InProgress'
| 'CompletedSuccessfully'
| 'CompletedUnsuccessfully'
| 'Failed'
| 'Cancelled'
| 'TimedOut';
| 'Completed'
| 'Canceled';
export interface VariantAnalysisMonitorResult {
status: VariantAnalysisMonitorStatus;
error?: string;
scannedReposDownloaded?: number[],
variantAnalysis?: VariantAnalysis
}

View File

@@ -47,6 +47,13 @@ export enum VariantAnalysisStatus {
Canceled = 'canceled',
}
export function isFinalVariantAnalysisStatus(status: VariantAnalysisStatus): boolean {
return [
// All states that indicates the analysis has completed and cannot change status anymore.
VariantAnalysisStatus.Succeeded, VariantAnalysisStatus.Failed, VariantAnalysisStatus.Canceled,
].includes(status);
}
export enum VariantAnalysisFailureReason {
NoReposQueried = 'noReposQueried',
InternalError = 'internalError',
@@ -132,6 +139,26 @@ export interface VariantAnalysisSubmission {
}
}
export async function isVariantAnalysisComplete(
variantAnalysis: VariantAnalysis,
artifactDownloaded: (repo: VariantAnalysisScannedRepository) => Promise<boolean>
): Promise<boolean> {
// It's only acceptable to have no scanned repos if the variant analysis is not in a final state.
// Otherwise it means the analysis hit some kind of internal error or there were no repos to scan.
if (variantAnalysis.scannedRepos === undefined || variantAnalysis.scannedRepos.length === 0) {
return variantAnalysis.status !== VariantAnalysisStatus.InProgress;
}
return (await Promise.all(variantAnalysis.scannedRepos.map(repo => isVariantAnalysisRepoComplete(repo, artifactDownloaded)))).every(x => x);
}
async function isVariantAnalysisRepoComplete(
repo: VariantAnalysisScannedRepository,
artifactDownloaded: (repo: VariantAnalysisScannedRepository) => Promise<boolean>
): Promise<boolean> {
return hasRepoScanCompleted(repo) && (!repoHasDownloadableArtifact(repo) || await artifactDownloaded(repo));
}
/**
* @param status
* @returns whether the status is in a completed state, i.e. it cannot normally change state anymore
@@ -153,6 +180,14 @@ export function hasRepoScanCompleted(repo: VariantAnalysisScannedRepository): bo
return isCompletedAnalysisRepoStatus(repo.analysisStatus);
}
/**
* @param repo
* @returns whether the repo scan has an artifact that can be downloaded
*/
export function repoHasDownloadableArtifact(repo: VariantAnalysisScannedRepository): boolean {
return repo.analysisStatus === VariantAnalysisRepoStatus.Succeeded;
}
/**
* @param repos
* @returns the total number of results. Will be `undefined` when there are no repos with results.

View File

@@ -11,7 +11,9 @@ import {
VariantAnalysisScannedRepository as ApiVariantAnalysisScannedRepository
} from './gh-api/variant-analysis';
import {
isVariantAnalysisComplete,
VariantAnalysis, VariantAnalysisQueryLanguage,
VariantAnalysisScannedRepository,
VariantAnalysisScannedRepositoryDownloadStatus,
VariantAnalysisScannedRepositoryResult,
VariantAnalysisScannedRepositoryState
@@ -24,7 +26,6 @@ import { getControllerRepo } from './run-remote-query';
import { processUpdatedVariantAnalysis } from './variant-analysis-processor';
import PQueue from 'p-queue';
import { createTimestampFile, showAndLogErrorMessage } from '../helpers';
import { QueryStatus } from '../query-status';
import * as fs from 'fs-extra';
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
@@ -55,21 +56,24 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
this.variantAnalysisResultsManager.onResultLoaded(this.onRepoResultLoaded.bind(this));
}
public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis, status: QueryStatus) {
public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis) {
if (!(await this.variantAnalysisRecordExists(variantAnalysis.id))) {
// In this case, the variant analysis was deleted from disk, most likely because
// it was purged by another workspace.
this._onVariantAnalysisRemoved.fire(variantAnalysis);
} else {
await this.setVariantAnalysis(variantAnalysis);
if (status === QueryStatus.InProgress) {
// In this case, last time we checked, the query was still in progress.
// We need to setup the monitor to check for completion.
await commands.executeCommand('codeQL.monitorVariantAnalysis', variantAnalysis);
if (!await isVariantAnalysisComplete(variantAnalysis, this.makeResultDownloadChecker(variantAnalysis))) {
void commands.executeCommand('codeQL.monitorVariantAnalysis', variantAnalysis);
}
}
}
private makeResultDownloadChecker(variantAnalysis: VariantAnalysis): (repo: VariantAnalysisScannedRepository) => Promise<boolean> {
const storageLocation = this.getVariantAnalysisStorageLocation(variantAnalysis.id);
return (repo) => this.variantAnalysisResultsManager.isVariantAnalysisRepoDownloaded(storageLocation, repo.repository.fullName);
}
public async removeVariantAnalysis(variantAnalysis: VariantAnalysis) {
this.variantAnalysisResultsManager.removeAnalysisResults(variantAnalysis);
await this.removeStorageDirectory(variantAnalysis.id);

View File

@@ -2,14 +2,15 @@ import { ExtensionContext, CancellationToken, commands, EventEmitter } from 'vsc
import { Credentials } from '../authentication';
import * as ghApiClient from './gh-api/gh-api-client';
import { VariantAnalysis, VariantAnalysisStatus } from './shared/variant-analysis';
import { isFinalVariantAnalysisStatus, VariantAnalysis } from './shared/variant-analysis';
import {
VariantAnalysis as VariantAnalysisApiResponse,
VariantAnalysisScannedRepository
} from './gh-api/variant-analysis';
import { VariantAnalysisMonitorResult } from './shared/variant-analysis-monitor-result';
import { processFailureReason, processUpdatedVariantAnalysis } from './variant-analysis-processor';
import { processUpdatedVariantAnalysis } from './variant-analysis-processor';
import { DisposableObject } from '../pure/disposable-object';
import { sleep } from '../pure/time';
export class VariantAnalysisMonitor extends DisposableObject {
// With a sleep of 5 seconds, the maximum number of attempts takes
@@ -36,38 +37,22 @@ export class VariantAnalysisMonitor extends DisposableObject {
throw Error('Error authenticating with GitHub');
}
let variantAnalysisSummary: VariantAnalysisApiResponse;
let attemptCount = 0;
const scannedReposDownloaded: number[] = [];
this._onVariantAnalysisChange.fire(variantAnalysis);
while (attemptCount <= VariantAnalysisMonitor.maxAttemptCount) {
await this.sleep(VariantAnalysisMonitor.sleepTime);
await sleep(VariantAnalysisMonitor.sleepTime);
if (cancellationToken && cancellationToken.isCancellationRequested) {
return { status: 'Cancelled', error: 'Variant Analysis was canceled.' };
return { status: 'Canceled' };
}
variantAnalysisSummary = await ghApiClient.getVariantAnalysis(
const variantAnalysisSummary = await ghApiClient.getVariantAnalysis(
credentials,
variantAnalysis.controllerRepo.id,
variantAnalysis.id
);
if (variantAnalysisSummary.failure_reason) {
variantAnalysis.status = VariantAnalysisStatus.Failed;
variantAnalysis.failureReason = processFailureReason(variantAnalysisSummary.failure_reason);
this._onVariantAnalysisChange.fire(variantAnalysis);
return {
status: 'Failed',
error: `Variant Analysis has failed: ${variantAnalysisSummary.failure_reason}`,
variantAnalysis: variantAnalysis
};
}
variantAnalysis = processUpdatedVariantAnalysis(variantAnalysis, variantAnalysisSummary);
this._onVariantAnalysisChange.fire(variantAnalysis);
@@ -75,14 +60,14 @@ export class VariantAnalysisMonitor extends DisposableObject {
const downloadedRepos = this.downloadVariantAnalysisResults(variantAnalysisSummary, scannedReposDownloaded);
scannedReposDownloaded.push(...downloadedRepos);
if (variantAnalysisSummary.status === 'completed') {
if (isFinalVariantAnalysisStatus(variantAnalysis.status) || variantAnalysis.failureReason) {
break;
}
attemptCount++;
}
return { status: 'CompletedSuccessfully', scannedReposDownloaded: scannedReposDownloaded };
return { status: 'Completed', scannedReposDownloaded, variantAnalysis };
}
private scheduleForDownload(
@@ -124,8 +109,4 @@ export class VariantAnalysisMonitor extends DisposableObject {
return downloadedRepos;
}
private async sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

View File

@@ -62,7 +62,7 @@ export function processUpdatedVariantAnalysis(
executionStartTime: previousVariantAnalysis.executionStartTime,
createdAt: response.created_at,
updatedAt: response.updated_at,
status: processApiStatus(response.status),
status: processApiStatus(response.status, response.failure_reason),
completedAt: response.completed_at,
actionsWorkflowRunId: response.actions_workflow_run_id,
scannedRepos: scannedRepos,
@@ -102,7 +102,7 @@ function processSkippedRepositories(
return {
accessMismatchRepos: processRepoGroup(skippedRepos.access_mismatch_repos),
notFoundRepos: processNotFoundRepoGroup(skippedRepos.not_found_repo_nwos),
notFoundRepos: processNotFoundRepoGroup(skippedRepos.not_found_repos),
noCodeqlDbRepos: processRepoGroup(skippedRepos.no_codeql_db_repos),
overLimitRepos: processRepoGroup(skippedRepos.over_limit_repos)
};
@@ -163,12 +163,13 @@ function processApiRepoStatus(analysisStatus: ApiVariantAnalysisRepoStatus): Var
}
}
function processApiStatus(status: ApiVariantAnalysisStatus): VariantAnalysisStatus {
switch (status) {
case 'in_progress':
return VariantAnalysisStatus.InProgress;
case 'completed':
return VariantAnalysisStatus.Succeeded;
function processApiStatus(status: ApiVariantAnalysisStatus, failureReason: string | undefined): VariantAnalysisStatus {
if (status === 'in_progress') {
return VariantAnalysisStatus.InProgress;
} else if (failureReason !== undefined) {
return VariantAnalysisStatus.Failed;
} else {
return VariantAnalysisStatus.Succeeded;
}
}

View File

@@ -146,7 +146,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
throw new Error('Missing results file');
}
private async isVariantAnalysisRepoDownloaded(
public async isVariantAnalysisRepoDownloaded(
variantAnalysisStoragePath: string,
repositoryFullName: string,
): Promise<boolean> {

View File

@@ -1,6 +1,6 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { CancellationTokenSource, extensions } from 'vscode';
import { CancellationTokenSource, commands, extensions } from 'vscode';
import { CodeQLExtensionInterface } from '../../../extension';
import { logger } from '../../../logging';
import * as config from '../../../config';
@@ -21,15 +21,17 @@ import { createMockVariantAnalysisRepoTask } from '../../factories/remote-querie
import { CodeQLCliServer } from '../../../cli';
import { storagePath } from '../global.helper';
import { VariantAnalysisResultsManager } from '../../../remote-queries/variant-analysis-results-manager';
import { VariantAnalysis } from '../../../remote-queries/shared/variant-analysis';
import { createMockVariantAnalysis } from '../../factories/remote-queries/shared/variant-analysis';
import { VariantAnalysis } from '../../../remote-queries/shared/variant-analysis';
import * as VariantAnalysisModule from '../../../remote-queries/shared/variant-analysis';
import { createTimestampFile } from '../../../helpers';
describe('Variant Analysis Manager', async function() {
let sandbox: sinon.SinonSandbox;
let cli: CodeQLCliServer;
let cancellationTokenSource: CancellationTokenSource;
let variantAnalysisManager: VariantAnalysisManager;
let variantAnalysis: VariantAnalysisApiResponse;
let variantAnalysisApiResponse: VariantAnalysisApiResponse;
let scannedRepos: ApiVariantAnalysisScannedRepository[];
let getVariantAnalysisRepoStub: sinon.SinonStub;
let getVariantAnalysisRepoResultStub: sinon.SinonStub;
@@ -45,7 +47,7 @@ describe('Variant Analysis Manager', async function() {
cancellationTokenSource = new CancellationTokenSource();
scannedRepos = createMockScannedRepos();
variantAnalysis = createMockApiResponse('in_progress', scannedRepos);
variantAnalysisApiResponse = createMockApiResponse('in_progress', scannedRepos);
try {
const extension = await extensions.getExtension<CodeQLExtensionInterface | Record<string, never>>('GitHub.vscode-codeql')!.activate();
@@ -68,7 +70,7 @@ describe('Variant Analysis Manager', async function() {
try {
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
scannedRepos[0],
variantAnalysis,
variantAnalysisApiResponse,
cancellationTokenSource.token
);
} catch (error: any) {
@@ -105,7 +107,7 @@ describe('Variant Analysis Manager', async function() {
it('should not try to download the result', async () => {
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
scannedRepos[0],
variantAnalysis,
variantAnalysisApiResponse,
cancellationTokenSource.token
);
@@ -129,7 +131,7 @@ describe('Variant Analysis Manager', async function() {
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
scannedRepos[0],
variantAnalysis,
variantAnalysisApiResponse,
cancellationTokenSource.token
);
@@ -139,7 +141,7 @@ describe('Variant Analysis Manager', async function() {
it('should fetch a repo task', async () => {
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
scannedRepos[0],
variantAnalysis,
variantAnalysisApiResponse,
cancellationTokenSource.token
);
@@ -149,7 +151,7 @@ describe('Variant Analysis Manager', async function() {
it('should fetch a repo result', async () => {
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
scannedRepos[0],
variantAnalysis,
variantAnalysisApiResponse,
cancellationTokenSource.token
);
@@ -161,9 +163,9 @@ describe('Variant Analysis Manager', async function() {
it('should pop download tasks off the queue', async () => {
const getResultsSpy = sandbox.spy(variantAnalysisManager, 'autoDownloadVariantAnalysisResult');
await variantAnalysisManager.enqueueDownload(scannedRepos[0], variantAnalysis, cancellationTokenSource.token);
await variantAnalysisManager.enqueueDownload(scannedRepos[1], variantAnalysis, cancellationTokenSource.token);
await variantAnalysisManager.enqueueDownload(scannedRepos[2], variantAnalysis, cancellationTokenSource.token);
await variantAnalysisManager.enqueueDownload(scannedRepos[0], variantAnalysisApiResponse, cancellationTokenSource.token);
await variantAnalysisManager.enqueueDownload(scannedRepos[1], variantAnalysisApiResponse, cancellationTokenSource.token);
await variantAnalysisManager.enqueueDownload(scannedRepos[2], variantAnalysisApiResponse, cancellationTokenSource.token);
expect(variantAnalysisManager.downloadsQueueSize()).to.equal(0);
expect(getResultsSpy).to.have.been.calledThrice;
@@ -194,4 +196,77 @@ describe('Variant Analysis Manager', async function() {
});
});
});
describe('when rehydrating a query', async () => {
let variantAnalysis: VariantAnalysis;
let variantAnalysisRemovedSpy: sinon.SinonSpy;
let monitorVariantAnalysisCommandSpy: sinon.SinonSpy;
beforeEach(() => {
variantAnalysis = createMockVariantAnalysis();
variantAnalysisRemovedSpy = sinon.spy();
variantAnalysisManager.onVariantAnalysisRemoved(variantAnalysisRemovedSpy);
monitorVariantAnalysisCommandSpy = sinon.spy();
sandbox.stub(commands, 'executeCommand').callsFake(monitorVariantAnalysisCommandSpy);
});
describe('when variant analysis record doesn\'t exist', async () => {
it('should remove the variant analysis', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.calledOnce(variantAnalysisRemovedSpy);
});
it('should not trigger a monitoring command', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.notCalled(monitorVariantAnalysisCommandSpy);
});
});
describe('when variant analysis record does exist', async () => {
let variantAnalysisStorageLocation: string;
beforeEach(async () => {
variantAnalysisStorageLocation = variantAnalysisManager.getVariantAnalysisStorageLocation(variantAnalysis.id);
await createTimestampFile(variantAnalysisStorageLocation);
});
afterEach(() => {
fs.rmSync(variantAnalysisStorageLocation, { recursive: true });
});
describe('when the variant analysis is not complete', async () => {
beforeEach(() => {
sandbox.stub(VariantAnalysisModule, 'isVariantAnalysisComplete').resolves(false);
});
it('should not remove the variant analysis', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.notCalled(variantAnalysisRemovedSpy);
});
it('should trigger a monitoring command', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.calledWith(monitorVariantAnalysisCommandSpy, 'codeQL.monitorVariantAnalysis');
});
});
describe('when the variant analysis is complete', async () => {
beforeEach(() => {
sandbox.stub(VariantAnalysisModule, 'isVariantAnalysisComplete').resolves(true);
});
it('should not remove the variant analysis', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.notCalled(variantAnalysisRemovedSpy);
});
it('should not trigger a monitoring command', async () => {
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
sinon.assert.notCalled(monitorVariantAnalysisCommandSpy);
});
});
});
});
});

View File

@@ -83,14 +83,14 @@ describe('Variant Analysis Monitor', async function() {
const result = await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis, cancellationTokenSource.token);
expect(result).to.eql({ status: 'Cancelled', error: 'Variant Analysis was canceled.' });
expect(result).to.eql({ status: 'Canceled' });
});
describe('when the variant analysis fails', async () => {
let mockFailedApiResponse: VariantAnalysisApiResponse;
beforeEach(async function() {
mockFailedApiResponse = createFailedMockApiResponse('in_progress');
mockFailedApiResponse = createFailedMockApiResponse();
mockGetVariantAnalysis = sandbox.stub(ghApiClient, 'getVariantAnalysis').resolves(mockFailedApiResponse);
});
@@ -98,8 +98,7 @@ describe('Variant Analysis Monitor', async function() {
const result = await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis, cancellationTokenSource.token);
expect(mockGetVariantAnalysis.calledOnce).to.be.true;
expect(result.status).to.eql('Failed');
expect(result.error).to.eql(`Variant Analysis has failed: ${mockFailedApiResponse.failure_reason}`);
expect(result.status).to.eql('Completed');
expect(result.variantAnalysis?.status).to.equal(VariantAnalysisStatus.Failed);
expect(result.variantAnalysis?.failureReason).to.equal(processFailureReason(mockFailedApiResponse.failure_reason as VariantAnalysisFailureReason));
});
@@ -130,7 +129,7 @@ describe('Variant Analysis Monitor', async function() {
it('should succeed and return a list of scanned repo ids', async () => {
const result = await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis, cancellationTokenSource.token);
expect(result.status).to.equal('CompletedSuccessfully');
expect(result.status).to.equal('Completed');
expect(result.scannedReposDownloaded).to.eql(succeededRepos.map(r => r.repository.id));
});
@@ -173,7 +172,7 @@ describe('Variant Analysis Monitor', async function() {
it('should succeed and return an empty list of scanned repo ids', async () => {
const result = await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis, cancellationTokenSource.token);
expect(result.status).to.equal('CompletedSuccessfully');
expect(result.status).to.equal('Completed');
expect(result.scannedReposDownloaded).to.eql([]);
});
@@ -194,7 +193,7 @@ describe('Variant Analysis Monitor', async function() {
it('should succeed and return an empty list of scanned repo ids', async () => {
const result = await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis, cancellationTokenSource.token);
expect(result.status).to.equal('CompletedSuccessfully');
expect(result.status).to.equal('Completed');
expect(result.scannedReposDownloaded).to.eql([]);
});

View File

@@ -13,7 +13,6 @@ import { CodeQLCliServer } from '../../../cli';
import { storagePath } from '../global.helper';
import { faker } from '@faker-js/faker';
import * as ghApiClient from '../../../remote-queries/gh-api/gh-api-client';
import { VariantAnalysisRepoTask } from '../../../remote-queries/gh-api/variant-analysis';
describe(VariantAnalysisResultsManager.name, function() {
this.timeout(10000);
@@ -47,15 +46,32 @@ describe(VariantAnalysisResultsManager.name, function() {
describe('download', () => {
let getOctokitStub: sinon.SinonStub;
let variantAnalysisStoragePath: string;
const mockCredentials = {
getOctokit: () => Promise.resolve({
request: getOctokitStub
})
} as unknown as Credentials;
let dummyRepoTask = createMockVariantAnalysisRepoTask();
let variantAnalysisStoragePath: string;
let repoTaskStorageDirectory: string;
beforeEach(async () => {
dummyRepoTask = createMockVariantAnalysisRepoTask();
variantAnalysisStoragePath = path.join(storagePath, variantAnalysisId.toString());
repoTaskStorageDirectory = variantAnalysisResultsManager.getRepoStorageDirectory(variantAnalysisStoragePath, dummyRepoTask.repository.full_name);
});
afterEach(async () => {
if (fs.existsSync(variantAnalysisStoragePath)) {
fs.rmSync(variantAnalysisStoragePath, { recursive: true });
}
});
describe('isVariantAnalysisRepoDownloaded', () => {
it('should return false when no results are downloaded', async () => {
expect(await variantAnalysisResultsManager.isVariantAnalysisRepoDownloaded(variantAnalysisStoragePath, dummyRepoTask.repository.full_name)).to.equal(false);
});
});
describe('when the artifact_url is missing', async () => {
@@ -79,14 +95,9 @@ describe(VariantAnalysisResultsManager.name, function() {
});
describe('when the artifact_url is present', async () => {
let dummyRepoTask: VariantAnalysisRepoTask;
let storageDirectory: string;
let arrayBuffer: ArrayBuffer;
beforeEach(async () => {
dummyRepoTask = createMockVariantAnalysisRepoTask();
storageDirectory = variantAnalysisResultsManager.getRepoStorageDirectory(variantAnalysisStoragePath, dummyRepoTask.repository.full_name);
const sourceFilePath = path.join(__dirname, '../../../../src/vscode-tests/cli-integration/data/variant-analysis-results.zip');
arrayBuffer = fs.readFileSync(sourceFilePath).buffer;
@@ -96,11 +107,6 @@ describe(VariantAnalysisResultsManager.name, function() {
.resolves(arrayBuffer);
});
afterEach(async () => {
fs.removeSync(`${storageDirectory}/results.zip`);
fs.removeSync(`${storageDirectory}/results`);
});
it('should call the API to download the results', async () => {
await variantAnalysisResultsManager.download(
mockCredentials,
@@ -120,7 +126,7 @@ describe(VariantAnalysisResultsManager.name, function() {
variantAnalysisStoragePath
);
expect(fs.existsSync(`${storageDirectory}/results.zip`)).to.be.true;
expect(fs.existsSync(`${repoTaskStorageDirectory}/results.zip`)).to.be.true;
});
it('should unzip the results in a `results/` folder', async () => {
@@ -131,7 +137,20 @@ describe(VariantAnalysisResultsManager.name, function() {
variantAnalysisStoragePath
);
expect(fs.existsSync(`${storageDirectory}/results/results.sarif`)).to.be.true;
expect(fs.existsSync(`${repoTaskStorageDirectory}/results/results.sarif`)).to.be.true;
});
describe('isVariantAnalysisRepoDownloaded', () => {
it('should return true once results are downloaded', async () => {
await variantAnalysisResultsManager.download(
mockCredentials,
variantAnalysisId,
dummyRepoTask,
variantAnalysisStoragePath
);
expect(await variantAnalysisResultsManager.isVariantAnalysisRepoDownloaded(variantAnalysisStoragePath, dummyRepoTask.repository.full_name)).to.equal(true);
});
});
});
});

View File

@@ -4,6 +4,9 @@ import {
CompletedLocalQueryInfo,
LocalQueryInfo,
} from '../../../query-results';
import { QueryEvaluationInfo, QueryWithResults } from '../../../run-queries-shared';
import { CancellationTokenSource } from 'vscode';
import { QueryResultType } from '../../../pure/legacy-messages';
export function createMockLocalQueryInfo(
startTime: string,
@@ -31,3 +34,56 @@ export function createMockLocalQueryInfo(
} 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;
const cancellationToken = {
dispose: () => { /**/ },
} as CancellationTokenSource;
const fqi = new LocalQueryInfo(
initialQueryInfo,
cancellationToken,
);
if (queryWithResults) {
fqi.completeThisQuery(queryWithResults);
}
if (isFail) {
fqi.failureReason = 'failure reason';
}
return fqi;
}
export function createMockQueryWithResults(
sandbox: sinon.SinonSandbox,
didRunSuccessfully = true,
hasInterpretedResults = true
): QueryWithResults {
return {
query: {
hasInterpretedResults: () => Promise.resolve(hasInterpretedResults),
deleteQuery: sandbox.stub(),
} as unknown as QueryEvaluationInfo,
successful: didRunSuccessfully,
message: 'foo',
dispose: sandbox.spy(),
result: {
evaluationTime: 1,
queryId: 0,
runId: 0,
resultType: QueryResultType.SUCCESS,
}
};
}

View File

@@ -10,7 +10,7 @@ export function createMockSkippedRepos(): VariantAnalysisSkippedRepositories {
return {
access_mismatch_repos: createMockSkippedRepoGroup(),
no_codeql_db_repos: createMockSkippedRepoGroup(),
not_found_repo_nwos: createMockNotFoundSkippedRepoGroup(),
not_found_repos: createMockNotFoundSkippedRepoGroup(),
over_limit_repos: createMockSkippedRepoGroup()
};
}

View File

@@ -40,13 +40,10 @@ export function createMockApiResponse(
}
export function createFailedMockApiResponse(
status: VariantAnalysisStatus = 'in_progress',
scannedRepos: VariantAnalysisScannedRepository[] = createMockScannedRepos(),
skippedRepos: VariantAnalysisSkippedRepositories = createMockSkippedRepos(),
): VariantAnalysisApiResponse {
const variantAnalysis = createMockApiResponse(status, scannedRepos, skippedRepos);
variantAnalysis.status = status;
const variantAnalysis = createMockApiResponse('completed', scannedRepos, skippedRepos);
variantAnalysis.failure_reason = 'internal_error';
return variantAnalysis;
}

View File

@@ -1,31 +1,41 @@
import { RemoteQueryHistoryItem } from '../../../remote-queries/remote-query-history-item';
import { QueryStatus } from '../../../query-status';
export function createMockRemoteQueryHistoryItem({
date = new Date('2022-01-01T00:00:00.000Z'),
status = QueryStatus.InProgress,
failureReason = undefined,
resultCount = 16,
userSpecifiedLabel = undefined,
repositoryCount = 0,
userSpecifiedLabel = undefined,
}: {
date?: Date;
status?: QueryStatus;
failureReason?: string;
resultCount?: number;
userSpecifiedLabel?: string;
repositoryCount?: number;
userSpecifiedLabel?: string;
}): RemoteQueryHistoryItem {
return ({
t: 'remote',
userSpecifiedLabel,
failureReason,
resultCount,
status,
completed: false,
queryId: 'queryId',
remoteQuery: {
executionStartTime: date.getTime(),
queryName: 'query-name',
queryFilePath: 'query-file.ql',
queryText: 'select 1',
language: 'javascript',
controllerRepository: {
owner: 'github',
name: 'vscode-codeql-integration-tests',
},
language: 'javascript',
executionStartTime: date.getTime(),
actionsWorkflowRunId: 1,
repositoryCount,
},
status: 'in progress',
resultCount,
} as unknown) as RemoteQueryHistoryItem;
userSpecifiedLabel,
});
}

View File

@@ -0,0 +1,23 @@
import { VariantAnalysisHistoryItem } from '../../../remote-queries/variant-analysis-history-item';
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,
resultCount?: number,
userSpecifiedLabel?: string
): VariantAnalysisHistoryItem {
return ({
t: 'variant-analysis',
failureReason,
resultCount,
status: historyItemStatus,
completed: false,
variantAnalysis: createMockVariantAnalysis(variantAnalysisStatus),
userSpecifiedLabel,
});
}

View File

@@ -5,9 +5,7 @@ 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';
describe('HistoryItemLabelProvider', () => {
let labelProvider: HistoryItemLabelProvider;
let config: QueryHistoryConfig;
const date = new Date('2022-01-01T00:00:00.000Z');

View File

@@ -1,19 +1,18 @@
import { expect } from 'chai';
import { QueryStatus } from '../../src/query-status';
import { QueryStatus } from '../../query-status';
import {
buildRepoLabel,
getActionsWorkflowRunUrl,
getQueryId,
getQueryText,
getRawQueryName
} from '../../src/query-history-info';
import { VariantAnalysisHistoryItem } from '../../src/remote-queries/variant-analysis-history-item';
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
import { createMockScannedRepos } from '../../src/vscode-tests/factories/remote-queries/shared/scanned-repositories';
import { createMockLocalQueryInfo } from '../../src/vscode-tests/factories/local-queries/local-query-history-item';
import { createMockRemoteQueryHistoryItem } from '../../src/vscode-tests/factories/remote-queries/remote-query-history-item';
import { VariantAnalysisRepoStatus, VariantAnalysisStatus } from '../../src/remote-queries/shared/variant-analysis';
} from '../../query-history-info';
import { VariantAnalysisHistoryItem } from '../../remote-queries/variant-analysis-history-item';
import { createMockVariantAnalysis } from '../factories/remote-queries/shared/variant-analysis';
import { createMockScannedRepos } from '../factories/remote-queries/shared/scanned-repositories';
import { createMockLocalQueryInfo } from '../factories/local-queries/local-query-history-item';
import { createMockRemoteQueryHistoryItem } from '../factories/remote-queries/remote-query-history-item';
import { VariantAnalysisRepoStatus, VariantAnalysisStatus } from '../../remote-queries/shared/variant-analysis';
describe('Query history info', () => {

View File

@@ -13,6 +13,7 @@ import { slurpQueryHistory, splatQueryHistory } from '../../query-serialization'
import { formatLegacyMessage, QueryInProgress } from '../../legacy-query-server/run-queries';
import { EvaluationResult, QueryResultType } from '../../pure/legacy-messages';
import Sinon = require('sinon');
import { sleep } from '../../pure/time';
describe('query-results', () => {
let disposeSpy: sinon.SinonSpy;
@@ -455,10 +456,6 @@ describe('query-results', () => {
}
}
async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function createMockQueryWithResults(
queryPath: string,
didRunSuccessfully = true,

View File

@@ -121,9 +121,7 @@ describe('Variant Analyses and QueryHistoryManager', function() {
expect(rehydrateVariantAnalysisStub).to.have.callCount(2);
expect(rehydrateVariantAnalysisStub.getCall(0).args[0]).to.deep.eq(rawQueryHistory[0].variantAnalysis);
expect(rehydrateVariantAnalysisStub.getCall(0).args[1]).to.deep.eq(rawQueryHistory[0].status);
expect(rehydrateVariantAnalysisStub.getCall(1).args[0]).to.deep.eq(rawQueryHistory[1].variantAnalysis);
expect(rehydrateVariantAnalysisStub.getCall(1).args[1]).to.deep.eq(rawQueryHistory[1].status);
expect(qhm.treeDataProvider.allHistory[0]).to.deep.eq(rawQueryHistory[0]);
expect(qhm.treeDataProvider.allHistory[1]).to.deep.eq(rawQueryHistory[1]);

View File

@@ -0,0 +1,5 @@
import { QueryHistoryInfo } from '../../query-history-info';
export function shuffleHistoryItems(history: QueryHistoryInfo[]) {
return history.sort(() => Math.random() - 0.5);
}

View File

@@ -600,6 +600,74 @@
"endColumn": 52
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/install.js"
},
"codeSnippet": {
"startLine": 257,
"endLine": 261,
"text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n"
},
"highlightedRegion": {
"startLine": 259,
"startColumn": 42,
"endLine": 259,
"endColumn": 52
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/install.js"
},
"codeSnippet": {
"startLine": 257,
"endLine": 261,
"text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n"
},
"highlightedRegion": {
"startLine": 259,
"startColumn": 42,
"endLine": 259,
"endColumn": 52
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/install.js"
},
"codeSnippet": {
"startLine": 257,
"endLine": 261,
"text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n"
},
"highlightedRegion": {
"startLine": 259,
"startColumn": 42,
"endLine": 259,
"endColumn": 52
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/install.js"
},
"codeSnippet": {
"startLine": 257,
"endLine": 261,
"text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n"
},
"highlightedRegion": {
"startLine": 259,
"startColumn": 42,
"endLine": 259,
"endColumn": 52
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",

View File

@@ -16,44 +16,44 @@
<summary>Path with 5 steps</summary>
1. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + path.join(<strong>__dirname</strong>, "temp");
cp.execSync(cmd); // BAD
}
</code></pre>
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + path.join(<strong>__dirname</strong>, "temp");
cp.execSync(cmd); // BAD
}
</code></pre>
2. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + <strong>path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + <strong>path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
3. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = <strong>"rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = <strong>"rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
4. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let <strong>cmd = "rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let <strong>cmd = "rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
5. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5)
<pre><code class="javascript">function cleanupTemp() {
let cmd = "rm -rf " + path.join(__dirname, "temp");
cp.execSync(<strong>cmd</strong>); // BAD
}
</code></pre>
<pre><code class="javascript">function cleanupTemp() {
let cmd = "rm -rf " + path.join(__dirname, "temp");
cp.execSync(<strong>cmd</strong>); // BAD
}
</code></pre>
</details>
@@ -76,29 +76,29 @@
<summary>Path with 3 steps</summary>
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
</details>
@@ -121,29 +121,29 @@
<summary>Path with 3 steps</summary>
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
</details>
@@ -166,29 +166,29 @@
<summary>Path with 3 steps</summary>
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
</details>

View File

@@ -14,61 +14,93 @@
#### Paths
<details>
<summary>Path with 7 steps</summary>
<summary>Path with 11 steps</summary>
1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
2. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const <strong>meteorPath = path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const <strong>meteorPath = path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
3. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L44-L44)
<pre><code class="javascript"> METEOR_LATEST_VERSION,
extractPath: rootPath,
<strong>meteorPath</strong>,
release: process.env.INSTALL_METEOR_VERSION || METEOR_LATEST_VERSION,
rootPath,
</code></pre>
<pre><code class="javascript"> METEOR_LATEST_VERSION,
extractPath: rootPath,
<strong>meteorPath</strong>,
release: process.env.INSTALL_METEOR_VERSION || METEOR_LATEST_VERSION,
rootPath,
</code></pre>
4. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L12-L12)
<pre><code class="javascript">const os = require('os');
const {
<strong>meteorPath</strong>,
release,
startedPath,
</code></pre>
<pre><code class="javascript">const os = require('os');
const {
<strong>meteorPath</strong>,
release,
startedPath,
</code></pre>
5. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L11-L23)
<pre><code class="javascript">const tmp = require('tmp');
const os = require('os');
const <strong>{</strong>
<strong> meteorPath,</strong>
<strong> release,</strong>
<strong> startedPath,</strong>
<strong> extractPath,</strong>
<strong> isWindows,</strong>
<strong> rootPath,</strong>
<strong> sudoUser,</strong>
<strong> isSudo,</strong>
<strong> isMac,</strong>
<strong> METEOR_LATEST_VERSION,</strong>
<strong> shouldSetupExecPath,</strong>
<strong>} = require('./config.js')</strong>;
const { uninstall } = require('./uninstall');
const {
</code></pre>
<pre><code class="javascript">const tmp = require('tmp');
const os = require('os');
const <strong>{</strong>
<strong> meteorPath,</strong>
<strong> release,</strong>
<strong> startedPath,</strong>
<strong> extractPath,</strong>
<strong> isWindows,</strong>
<strong> rootPath,</strong>
<strong> sudoUser,</strong>
<strong> isSudo,</strong>
<strong> isMac,</strong>
<strong> METEOR_LATEST_VERSION,</strong>
<strong> shouldSetupExecPath,</strong>
<strong>} = require('./config.js')</strong>;
const { uninstall } = require('./uninstall');
const {
</code></pre>
6. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
return;
}
</code></pre>
7. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
return;
}
</code></pre>
8. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
return;
}
</code></pre>
9. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
return;
}
</code></pre>
10. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
@@ -76,7 +108,7 @@
}
</code></pre>
7. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
11. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
@@ -91,21 +123,21 @@
<summary>Path with 2 steps</summary>
1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
2. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
return;
}
</code></pre>
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
return;
}
</code></pre>
</details>

View File

@@ -26,7 +26,7 @@ describe('Variant Analysis processor', function() {
it('should process an API response and return a variant analysis', () => {
const result = processVariantAnalysis(mockSubmission, mockApiResponse);
const { access_mismatch_repos, no_codeql_db_repos, not_found_repo_nwos, over_limit_repos } = skippedRepos;
const { access_mismatch_repos, no_codeql_db_repos, not_found_repos, over_limit_repos } = skippedRepos;
expect(result).to.eql({
'id': mockApiResponse.id,
@@ -97,13 +97,13 @@ describe('Variant Analysis processor', function() {
'notFoundRepos': {
'repositories': [
{
'fullName': not_found_repo_nwos?.repository_full_names[0]
'fullName': not_found_repos?.repository_full_names[0]
},
{
'fullName': not_found_repo_nwos?.repository_full_names[1]
'fullName': not_found_repos?.repository_full_names[1]
}
],
'repositoryCount': not_found_repo_nwos?.repository_count
'repositoryCount': not_found_repos?.repository_count
},
'overLimitRepos': {
'repositories': [

View File

@@ -1,9 +1,6 @@
import { expect } from 'chai';
import {
getActionsWorkflowRunUrl,
parseVariantAnalysisQueryLanguage,
VariantAnalysisQueryLanguage
} from '../../src/remote-queries/shared/variant-analysis';
import { VariantAnalysis, parseVariantAnalysisQueryLanguage, VariantAnalysisQueryLanguage, VariantAnalysisStatus, isVariantAnalysisComplete, VariantAnalysisRepoStatus, getActionsWorkflowRunUrl } from '../../src/remote-queries/shared/variant-analysis';
import { createMockScannedRepo } from '../../src/vscode-tests/factories/remote-queries/shared/scanned-repositories';
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
describe('parseVariantAnalysisQueryLanguage', () => {
@@ -16,6 +13,95 @@ describe('parseVariantAnalysisQueryLanguage', () => {
});
});
describe('isVariantAnalysisComplete', async () => {
let variantAnalysis: VariantAnalysis;
const uncallableArtifactDownloadChecker = () => { throw new Error('Should not be called'); };
beforeEach(() => {
variantAnalysis = createMockVariantAnalysis();
});
describe('when variant analysis status is InProgress', async () => {
beforeEach(() => {
variantAnalysis.status = VariantAnalysisStatus.InProgress;
});
describe('when scanned repos is undefined', async () => {
it('should say the variant analysis is not complete', async () => {
variantAnalysis.scannedRepos = undefined;
expect(await isVariantAnalysisComplete(variantAnalysis, uncallableArtifactDownloadChecker)).to.equal(false);
});
});
describe('when scanned repos is non-empty', async () => {
describe('when not all results are downloaded', async () => {
it('should say the variant analysis is not complete', async () => {
expect(await isVariantAnalysisComplete(variantAnalysis, async () => false)).to.equal(false);
});
});
describe('when all results are downloaded', async () => {
it('should say the variant analysis is complete', async () => {
expect(await isVariantAnalysisComplete(variantAnalysis, async () => true)).to.equal(false);
});
});
});
});
for (const variantAnalysisStatus of [
VariantAnalysisStatus.Succeeded,
VariantAnalysisStatus.Failed,
VariantAnalysisStatus.Canceled
]) {
describe(`when variant analysis status is ${variantAnalysisStatus}`, async () => {
beforeEach(() => {
variantAnalysis.status = variantAnalysisStatus;
});
describe('when scanned repos is undefined', async () => {
it('should say the variant analysis is complete', async () => {
variantAnalysis.scannedRepos = undefined;
expect(await isVariantAnalysisComplete(variantAnalysis, uncallableArtifactDownloadChecker)).to.equal(true);
});
});
describe('when scanned repos is empty', async () => {
it('should say the variant analysis is complete', async () => {
variantAnalysis.scannedRepos = [];
expect(await isVariantAnalysisComplete(variantAnalysis, uncallableArtifactDownloadChecker)).to.equal(true);
});
});
describe('when a repo scan is still in progress', async () => {
it('should say the variant analysis is not complete', async () => {
variantAnalysis.scannedRepos = [
createMockScannedRepo('in-progress-repo', false, VariantAnalysisRepoStatus.InProgress),
];
expect(await isVariantAnalysisComplete(variantAnalysis, async () => false)).to.equal(false);
});
});
describe('when not all results are downloaded', async () => {
it('should say the variant analysis is not complete', async () => {
variantAnalysis.scannedRepos = [
createMockScannedRepo('in-progress-repo', false, VariantAnalysisRepoStatus.Succeeded),
];
expect(await isVariantAnalysisComplete(variantAnalysis, async () => false)).to.equal(false);
});
});
describe('when all results are downloaded', async () => {
it('should say the variant analysis is complete', async () => {
variantAnalysis.scannedRepos = [
createMockScannedRepo('in-progress-repo', false, VariantAnalysisRepoStatus.Succeeded),
];
expect(await isVariantAnalysisComplete(variantAnalysis, async () => true)).to.equal(true);
});
});
});
}
});
describe('getActionsWorkflowRunUrl', () => {
it('should get the run url', () => {
const variantAnalysis = createMockVariantAnalysis();