Add integration tests for database fetching

This commit is contained in:
Andrew Eisenberg
2020-11-23 17:34:17 -08:00
parent 16eac45822
commit 864041efcb
5 changed files with 151 additions and 17 deletions

View File

@@ -20,11 +20,11 @@ import { logger } from './logging';
/**
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
*
* @param databasesManager the DatabaseManager
* @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function promptImportInternetDatabase(
databasesManager: DatabaseManager,
databaseManager: DatabaseManager,
storagePath: string,
progress: ProgressCallback,
token: CancellationToken,
@@ -40,7 +40,7 @@ export async function promptImportInternetDatabase(
const item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
databaseManager,
storagePath,
progress,
token
@@ -59,11 +59,11 @@ export async function promptImportInternetDatabase(
* User enters a project url and then the user is asked which language
* to download (if there is more than one)
*
* @param databasesManager the DatabaseManager
* @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function promptImportLgtmDatabase(
databasesManager: DatabaseManager,
databaseManager: DatabaseManager,
storagePath: string,
progress: ProgressCallback,
token: CancellationToken
@@ -81,7 +81,7 @@ export async function promptImportLgtmDatabase(
if (databaseUrl) {
const item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
databaseManager,
storagePath,
progress,
token
@@ -102,12 +102,12 @@ export async function promptImportLgtmDatabase(
* Imports a database from a local archive.
*
* @param databaseUrl the file url of the archive to import
* @param databasesManager the DatabaseManager
* @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function importArchiveDatabase(
databaseUrl: string,
databasesManager: DatabaseManager,
databaseManager: DatabaseManager,
storagePath: string,
progress: ProgressCallback,
token: CancellationToken,
@@ -115,7 +115,7 @@ export async function importArchiveDatabase(
try {
const item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
databaseManager,
storagePath,
progress,
token
@@ -140,14 +140,14 @@ export async function importArchiveDatabase(
* or in the local filesystem.
*
* @param databaseUrl URL from which to grab the database
* @param databasesManager the DatabaseManager
* @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database.
* @param progress callback to send progress messages to
* @param token cancellation token
*/
async function databaseArchiveFetcher(
databaseUrl: string,
databasesManager: DatabaseManager,
databaseManager: DatabaseManager,
storagePath: string,
progress: ProgressCallback,
token: CancellationToken
@@ -189,8 +189,8 @@ async function databaseArchiveFetcher(
});
await ensureZippedSourceLocation(dbPath);
const item = await databasesManager.openDatabase(progress, token, Uri.file(dbPath));
databasesManager.setCurrentDatabaseItem(item);
const item = await databaseManager.openDatabase(progress, token, Uri.file(dbPath));
await databaseManager.setCurrentDatabaseItem(item);
return item;
} else {
throw new Error('Database not found in archive.');

View File

@@ -122,6 +122,8 @@ export interface CodeQLExtensionInterface {
readonly cliServer: CodeQLCliServer;
readonly qs: qsClient.QueryServerClient;
readonly distributionManager: DistributionManager;
readonly databaseManager: DatabaseManager;
readonly databaseUI: DatabaseUI;
}
/**
@@ -675,7 +677,9 @@ async function activateWithInstalledDistribution(
ctx,
cliServer,
qs,
distributionManager
distributionManager,
databaseManager: dbm,
databaseUI
};
}

View File

@@ -0,0 +1,130 @@
import 'mocha';
import 'sinon-chai';
import * as sinon from 'sinon';
import * as path from 'path';
import * as tmp from 'tmp';
import { expect } from 'chai';
import { ConfigurationTarget, workspace, extensions, CancellationToken, Uri } from 'vscode';
import * as vscode from 'vscode';
import * as fs from 'fs-extra';
import { CodeQLExtensionInterface } from '../../extension';
import { DatabaseManager } from '../../databases';
import { promptImportLgtmDatabase, importArchiveDatabase, promptImportInternetDatabase } from '../../databaseFetcher';
import { ProgressCallback } from '../../helpers';
import { fail } from 'assert';
import fetch from 'node-fetch';
/**
* Run various integration tests for databases
*/
describe('Databases', function() {
const DB_URL = 'https://github.com/github/vscode-codeql/files/5586722/simple-db.zip';
const LGTM_URL = 'https://lgtm.com/projects/g/aeisenberg/angular-bind-notifier/';
this.timeout(60000);
let databaseManager: DatabaseManager;
let sandbox: sinon.SinonSandbox;
let storagePath: string;
let storagePathCleanup: () => void;
let inputBoxStub: sinon.SinonStub;
let progressCallback: ProgressCallback;
beforeEach(async () => {
try {
// Set it here before activation to ensure we don't accidentally try to download a cli
await workspace.getConfiguration().update('codeQL.cli.executablePath', process.env.CLI_PATH, ConfigurationTarget.Global);
const extension = await extensions.getExtension<CodeQLExtensionInterface | {}>('GitHub.vscode-codeql')!.activate();
if ('cliServer' in extension) {
databaseManager = extension.databaseManager;
} else {
throw new Error('Extension not initialized. Make sure cli is downloaded and installed properly.');
}
sandbox = sinon.createSandbox();
const dir = tmp.dirSync();
storagePath = dir.name;
// the uri.fsPath function on windows returns a lowercase drive letter
// so, force the storage path string to be lowercase, too.
if (storagePath.substring(0, 2).match(/[A-Z]:/)) {
storagePath = storagePath.substring(0, 1).toLocaleLowerCase() + storagePath.substring(1);
}
storagePathCleanup = dir.removeCallback;
progressCallback = sandbox.spy();
inputBoxStub = sandbox.stub(vscode.window, 'showInputBox');
} catch (e) {
fail(e);
}
});
afterEach(() => {
try {
databaseManager.dispose();
sandbox.restore();
storagePathCleanup();
} catch (e) {
fail(e);
}
});
it('should add a database from a folder', async () => {
const result = await downloadDb();
try {
const progressCallback = sandbox.spy() as ProgressCallback;
const uri = Uri.file(result.dbLoc);
let dbItem = await importArchiveDatabase(uri.toString(true), databaseManager, storagePath, progressCallback, {} as CancellationToken);
expect(dbItem).to.be.eq(databaseManager.currentDatabaseItem);
expect(dbItem).to.be.eq(databaseManager.databaseItems[0]);
expect(dbItem).not.to.be.undefined;
dbItem = dbItem!;
expect(dbItem.name).to.eq('db');
expect(dbItem.databaseUri.fsPath).to.eq(path.join(storagePath, 'db', 'db'));
} finally {
result.removeCallback();
}
});
it('should add a database from lgtm with only one language', async () => {
inputBoxStub.resolves(LGTM_URL);
let dbItem = await promptImportLgtmDatabase(databaseManager, storagePath, progressCallback, {} as CancellationToken);
expect(dbItem).not.to.be.undefined;
dbItem = dbItem!;
expect(dbItem.name).to.eq('aeisenberg_angular-bind-notifier_106179a');
expect(dbItem.databaseUri.fsPath).to.eq(path.join(storagePath, 'javascript', 'aeisenberg_angular-bind-notifier_106179a'));
});
it('should add a database from a url', async () => {
inputBoxStub.resolves(DB_URL);
let dbItem = await promptImportInternetDatabase(databaseManager, storagePath, progressCallback, {} as CancellationToken);
expect(dbItem).not.to.be.undefined;
dbItem = dbItem!;
expect(dbItem.name).to.eq('db');
expect(dbItem.databaseUri.fsPath).to.eq(path.join(storagePath, 'simple-db', 'db'));
});
async function downloadDb(): Promise<{
removeCallback: () => void; dbLoc: string;
}> {
return new Promise((resolve, reject) => {
fetch(DB_URL).then(response => {
const dir = tmp.dirSync();
const dbLoc = path.join(dir.name, 'db.zip');
const dest = fs.createWriteStream(dbLoc);
response.body.pipe(dest);
response.body.on('error', reject);
dest.on('error', reject);
dest.on('close', () => {
resolve({
removeCallback: dir.removeCallback,
dbLoc
});
});
});
});
}
});

View File

@@ -1,7 +1,6 @@
import 'mocha';
import 'sinon-chai';
import { expect } from 'chai';
import { ConfigurationTarget, workspace, extensions } from 'vscode';
import { SemVer } from 'semver';

View File

@@ -6,7 +6,7 @@ import {
downloadAndUnzipVSCode,
resolveCliPathFromVSCodeExecutablePath
} from 'vscode-test';
import { assertNever } from '../helpers-pure';
import { assertNever } from '../pure/helpers-pure';
// For some reason, `TestOptions` is not exported directly from `vscode-test`,
// but we can be tricky and import directly from the out file.
@@ -120,9 +120,10 @@ function getLaunchArgs(dir: TestDir) {
];
case TestDir.CliIntegration:
return undefined;
break;
default:
assertNever(dir);
}
return undefined;
}