Merge pull request #2042 from github/elena/add-more-test-coverage
Add test coverage for `openDatabase`
This commit is contained in:
@@ -154,67 +154,69 @@ export async function findSourceArchive(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveDatabase(
|
|
||||||
databasePath: string,
|
|
||||||
): Promise<DatabaseContents> {
|
|
||||||
const name = basename(databasePath);
|
|
||||||
|
|
||||||
// Look for dataset and source archive.
|
|
||||||
const datasetUri = await findDataset(databasePath);
|
|
||||||
const sourceArchiveUri = await findSourceArchive(databasePath);
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: DatabaseKind.Database,
|
|
||||||
name,
|
|
||||||
datasetUri,
|
|
||||||
sourceArchiveUri,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the relative paths of all `.dbscheme` files in the given directory. */
|
/** Gets the relative paths of all `.dbscheme` files in the given directory. */
|
||||||
async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
|
async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
|
||||||
return await glob("*.dbscheme", { cwd: dbDirectory });
|
return await glob("*.dbscheme", { cwd: dbDirectory });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveDatabaseContents(
|
export class DatabaseResolver {
|
||||||
uri: vscode.Uri,
|
public static async resolveDatabaseContents(
|
||||||
): Promise<DatabaseContents> {
|
uri: vscode.Uri,
|
||||||
if (uri.scheme !== "file") {
|
): Promise<DatabaseContents> {
|
||||||
throw new Error(
|
if (uri.scheme !== "file") {
|
||||||
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
|
throw new Error(
|
||||||
);
|
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
|
||||||
}
|
);
|
||||||
const databasePath = uri.fsPath;
|
}
|
||||||
if (!(await pathExists(databasePath))) {
|
const databasePath = uri.fsPath;
|
||||||
throw new InvalidDatabaseError(
|
if (!(await pathExists(databasePath))) {
|
||||||
`Database '${databasePath}' does not exist.`,
|
throw new InvalidDatabaseError(
|
||||||
);
|
`Database '${databasePath}' does not exist.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = await this.resolveDatabase(databasePath);
|
||||||
|
|
||||||
|
if (contents === undefined) {
|
||||||
|
throw new InvalidDatabaseError(
|
||||||
|
`'${databasePath}' is not a valid database.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a single dbscheme file within the database.
|
||||||
|
// This should be found in the dataset directory, regardless of the form of database.
|
||||||
|
const dbPath = contents.datasetUri.fsPath;
|
||||||
|
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
|
||||||
|
if (dbSchemeFiles.length === 0) {
|
||||||
|
throw new InvalidDatabaseError(
|
||||||
|
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
|
||||||
|
);
|
||||||
|
} else if (dbSchemeFiles.length > 1) {
|
||||||
|
throw new InvalidDatabaseError(
|
||||||
|
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
contents.dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
|
||||||
|
}
|
||||||
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contents = await resolveDatabase(databasePath);
|
public static async resolveDatabase(
|
||||||
|
databasePath: string,
|
||||||
|
): Promise<DatabaseContents> {
|
||||||
|
const name = basename(databasePath);
|
||||||
|
|
||||||
if (contents === undefined) {
|
// Look for dataset and source archive.
|
||||||
throw new InvalidDatabaseError(
|
const datasetUri = await findDataset(databasePath);
|
||||||
`'${databasePath}' is not a valid database.`,
|
const sourceArchiveUri = await findSourceArchive(databasePath);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for a single dbscheme file within the database.
|
return {
|
||||||
// This should be found in the dataset directory, regardless of the form of database.
|
kind: DatabaseKind.Database,
|
||||||
const dbPath = contents.datasetUri.fsPath;
|
name,
|
||||||
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
|
datasetUri,
|
||||||
if (dbSchemeFiles.length === 0) {
|
sourceArchiveUri,
|
||||||
throw new InvalidDatabaseError(
|
};
|
||||||
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
|
|
||||||
);
|
|
||||||
} else if (dbSchemeFiles.length > 1) {
|
|
||||||
throw new InvalidDatabaseError(
|
|
||||||
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
contents.dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
|
|
||||||
}
|
}
|
||||||
return contents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An item in the list of available databases */
|
/** An item in the list of available databases */
|
||||||
@@ -370,7 +372,9 @@ export class DatabaseItemImpl implements DatabaseItem {
|
|||||||
public async refresh(): Promise<void> {
|
public async refresh(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
this._contents = await resolveDatabaseContents(this.databaseUri);
|
this._contents = await DatabaseResolver.resolveDatabaseContents(
|
||||||
|
this.databaseUri,
|
||||||
|
);
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._contents = undefined;
|
this._contents = undefined;
|
||||||
@@ -602,7 +606,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
uri: vscode.Uri,
|
uri: vscode.Uri,
|
||||||
displayName?: string,
|
displayName?: string,
|
||||||
): Promise<DatabaseItem> {
|
): Promise<DatabaseItem> {
|
||||||
const contents = await resolveDatabaseContents(uri);
|
const contents = await DatabaseResolver.resolveDatabaseContents(uri);
|
||||||
// Ignore the source archive for QLTest databases by default.
|
// Ignore the source archive for QLTest databases by default.
|
||||||
const isQLTestDatabase = extname(uri.fsPath) === ".testproj";
|
const isQLTestDatabase = extname(uri.fsPath) === ".testproj";
|
||||||
const fullOptions: FullDatabaseOptions = {
|
const fullOptions: FullDatabaseOptions = {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
DatabaseContents,
|
DatabaseContents,
|
||||||
FullDatabaseOptions,
|
FullDatabaseOptions,
|
||||||
findSourceArchive,
|
findSourceArchive,
|
||||||
|
DatabaseResolver,
|
||||||
} from "../../../src/databases";
|
} from "../../../src/databases";
|
||||||
import { Logger } from "../../../src/common";
|
import { Logger } from "../../../src/common";
|
||||||
import { ProgressCallback } from "../../../src/commandRunner";
|
import { ProgressCallback } from "../../../src/commandRunner";
|
||||||
@@ -21,6 +22,7 @@ import {
|
|||||||
import { testDisposeHandler } from "../test-dispose-handler";
|
import { testDisposeHandler } from "../test-dispose-handler";
|
||||||
import { QueryRunner } from "../../../src/queryRunner";
|
import { QueryRunner } from "../../../src/queryRunner";
|
||||||
import * as helpers from "../../../src/helpers";
|
import * as helpers from "../../../src/helpers";
|
||||||
|
import { Setting } from "../../../src/config";
|
||||||
|
|
||||||
describe("databases", () => {
|
describe("databases", () => {
|
||||||
const MOCK_DB_OPTIONS: FullDatabaseOptions = {
|
const MOCK_DB_OPTIONS: FullDatabaseOptions = {
|
||||||
@@ -623,6 +625,98 @@ describe("databases", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("openDatabase", () => {
|
||||||
|
let createSkeletonPacksSpy: jest.SpyInstance;
|
||||||
|
let resolveDatabaseContentsSpy: jest.SpyInstance;
|
||||||
|
let addDatabaseSourceArchiveFolderSpy: jest.SpyInstance;
|
||||||
|
let mockDbItem: DatabaseItemImpl;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createSkeletonPacksSpy = jest
|
||||||
|
.spyOn(databaseManager, "createSkeletonPacks")
|
||||||
|
.mockImplementation(async () => {
|
||||||
|
/* no-op */
|
||||||
|
});
|
||||||
|
|
||||||
|
resolveDatabaseContentsSpy = jest
|
||||||
|
.spyOn(DatabaseResolver, "resolveDatabaseContents")
|
||||||
|
.mockResolvedValue({} as DatabaseContents);
|
||||||
|
|
||||||
|
addDatabaseSourceArchiveFolderSpy = jest.spyOn(
|
||||||
|
databaseManager,
|
||||||
|
"addDatabaseSourceArchiveFolder",
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock("fs", () => ({
|
||||||
|
promises: {
|
||||||
|
pathExists: jest.fn().mockResolvedValue(true),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mockDbItem = createMockDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve the database contents", async () => {
|
||||||
|
await databaseManager.openDatabase(
|
||||||
|
{} as ProgressCallback,
|
||||||
|
{} as CancellationToken,
|
||||||
|
mockDbItem.databaseUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(resolveDatabaseContentsSpy).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add database source archive folder", async () => {
|
||||||
|
await databaseManager.openDatabase(
|
||||||
|
{} as ProgressCallback,
|
||||||
|
{} as CancellationToken,
|
||||||
|
mockDbItem.databaseUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when codeQL.codespacesTemplate is set to true", () => {
|
||||||
|
it("should create a skeleton QL pack", async () => {
|
||||||
|
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(true);
|
||||||
|
|
||||||
|
await databaseManager.openDatabase(
|
||||||
|
{} as ProgressCallback,
|
||||||
|
{} as CancellationToken,
|
||||||
|
mockDbItem.databaseUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createSkeletonPacksSpy).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when codeQL.codespacesTemplate is set to false", () => {
|
||||||
|
it("should not create a skeleton QL pack", async () => {
|
||||||
|
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(false);
|
||||||
|
|
||||||
|
await databaseManager.openDatabase(
|
||||||
|
{} as ProgressCallback,
|
||||||
|
{} as CancellationToken,
|
||||||
|
mockDbItem.databaseUri,
|
||||||
|
);
|
||||||
|
expect(createSkeletonPacksSpy).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when codeQL.codespacesTemplate is not set", () => {
|
||||||
|
it("should not create a skeleton QL pack", async () => {
|
||||||
|
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(undefined);
|
||||||
|
|
||||||
|
await databaseManager.openDatabase(
|
||||||
|
{} as ProgressCallback,
|
||||||
|
{} as CancellationToken,
|
||||||
|
mockDbItem.databaseUri,
|
||||||
|
);
|
||||||
|
expect(createSkeletonPacksSpy).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function createMockDB(
|
function createMockDB(
|
||||||
mockDbOptions = MOCK_DB_OPTIONS,
|
mockDbOptions = MOCK_DB_OPTIONS,
|
||||||
// source archive location must be a real(-ish) location since
|
// source archive location must be a real(-ish) location since
|
||||||
|
|||||||
Reference in New Issue
Block a user