Skip to content

Commit f7ab8b7

Browse files
Merge pull request #2042 from github/elena/add-more-test-coverage
Add test coverage for `openDatabase`
2 parents b2fa85f + 72f3847 commit f7ab8b7

2 files changed

Lines changed: 151 additions & 53 deletions

File tree

extensions/ql-vscode/src/databases.ts

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -154,67 +154,69 @@ export async function findSourceArchive(
154154
return undefined;
155155
}
156156

157-
async function resolveDatabase(
158-
databasePath: string,
159-
): Promise<DatabaseContents> {
160-
const name = basename(databasePath);
161-
162-
// Look for dataset and source archive.
163-
const datasetUri = await findDataset(databasePath);
164-
const sourceArchiveUri = await findSourceArchive(databasePath);
165-
166-
return {
167-
kind: DatabaseKind.Database,
168-
name,
169-
datasetUri,
170-
sourceArchiveUri,
171-
};
172-
}
173-
174157
/** Gets the relative paths of all `.dbscheme` files in the given directory. */
175158
async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
176159
return await glob("*.dbscheme", { cwd: dbDirectory });
177160
}
178161

179-
async function resolveDatabaseContents(
180-
uri: vscode.Uri,
181-
): Promise<DatabaseContents> {
182-
if (uri.scheme !== "file") {
183-
throw new Error(
184-
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
185-
);
186-
}
187-
const databasePath = uri.fsPath;
188-
if (!(await pathExists(databasePath))) {
189-
throw new InvalidDatabaseError(
190-
`Database '${databasePath}' does not exist.`,
191-
);
192-
}
162+
export class DatabaseResolver {
163+
public static async resolveDatabaseContents(
164+
uri: vscode.Uri,
165+
): Promise<DatabaseContents> {
166+
if (uri.scheme !== "file") {
167+
throw new Error(
168+
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
169+
);
170+
}
171+
const databasePath = uri.fsPath;
172+
if (!(await pathExists(databasePath))) {
173+
throw new InvalidDatabaseError(
174+
`Database '${databasePath}' does not exist.`,
175+
);
176+
}
193177

194-
const contents = await resolveDatabase(databasePath);
178+
const contents = await this.resolveDatabase(databasePath);
195179

196-
if (contents === undefined) {
197-
throw new InvalidDatabaseError(
198-
`'${databasePath}' is not a valid database.`,
199-
);
180+
if (contents === undefined) {
181+
throw new InvalidDatabaseError(
182+
`'${databasePath}' is not a valid database.`,
183+
);
184+
}
185+
186+
// Look for a single dbscheme file within the database.
187+
// This should be found in the dataset directory, regardless of the form of database.
188+
const dbPath = contents.datasetUri.fsPath;
189+
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
190+
if (dbSchemeFiles.length === 0) {
191+
throw new InvalidDatabaseError(
192+
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
193+
);
194+
} else if (dbSchemeFiles.length > 1) {
195+
throw new InvalidDatabaseError(
196+
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
197+
);
198+
} else {
199+
contents.dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
200+
}
201+
return contents;
200202
}
201203

202-
// Look for a single dbscheme file within the database.
203-
// This should be found in the dataset directory, regardless of the form of database.
204-
const dbPath = contents.datasetUri.fsPath;
205-
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
206-
if (dbSchemeFiles.length === 0) {
207-
throw new InvalidDatabaseError(
208-
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
209-
);
210-
} else if (dbSchemeFiles.length > 1) {
211-
throw new InvalidDatabaseError(
212-
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
213-
);
214-
} else {
215-
contents.dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
204+
public static async resolveDatabase(
205+
databasePath: string,
206+
): Promise<DatabaseContents> {
207+
const name = basename(databasePath);
208+
209+
// Look for dataset and source archive.
210+
const datasetUri = await findDataset(databasePath);
211+
const sourceArchiveUri = await findSourceArchive(databasePath);
212+
213+
return {
214+
kind: DatabaseKind.Database,
215+
name,
216+
datasetUri,
217+
sourceArchiveUri,
218+
};
216219
}
217-
return contents;
218220
}
219221

220222
/** An item in the list of available databases */
@@ -370,7 +372,9 @@ export class DatabaseItemImpl implements DatabaseItem {
370372
public async refresh(): Promise<void> {
371373
try {
372374
try {
373-
this._contents = await resolveDatabaseContents(this.databaseUri);
375+
this._contents = await DatabaseResolver.resolveDatabaseContents(
376+
this.databaseUri,
377+
);
374378
this._error = undefined;
375379
} catch (e) {
376380
this._contents = undefined;
@@ -602,7 +606,7 @@ export class DatabaseManager extends DisposableObject {
602606
uri: vscode.Uri,
603607
displayName?: string,
604608
): Promise<DatabaseItem> {
605-
const contents = await resolveDatabaseContents(uri);
609+
const contents = await DatabaseResolver.resolveDatabaseContents(uri);
606610
// Ignore the source archive for QLTest databases by default.
607611
const isQLTestDatabase = extname(uri.fsPath) === ".testproj";
608612
const fullOptions: FullDatabaseOptions = {

extensions/ql-vscode/test/vscode-tests/minimal-workspace/databases.test.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
DatabaseContents,
1111
FullDatabaseOptions,
1212
findSourceArchive,
13+
DatabaseResolver,
1314
} from "../../../src/databases";
1415
import { Logger } from "../../../src/common";
1516
import { ProgressCallback } from "../../../src/commandRunner";
@@ -21,6 +22,7 @@ import {
2122
import { testDisposeHandler } from "../test-dispose-handler";
2223
import { QueryRunner } from "../../../src/queryRunner";
2324
import * as helpers from "../../../src/helpers";
25+
import { Setting } from "../../../src/config";
2426

2527
describe("databases", () => {
2628
const MOCK_DB_OPTIONS: FullDatabaseOptions = {
@@ -623,6 +625,98 @@ describe("databases", () => {
623625
});
624626
});
625627

628+
describe("openDatabase", () => {
629+
let createSkeletonPacksSpy: jest.SpyInstance;
630+
let resolveDatabaseContentsSpy: jest.SpyInstance;
631+
let addDatabaseSourceArchiveFolderSpy: jest.SpyInstance;
632+
let mockDbItem: DatabaseItemImpl;
633+
634+
beforeEach(() => {
635+
createSkeletonPacksSpy = jest
636+
.spyOn(databaseManager, "createSkeletonPacks")
637+
.mockImplementation(async () => {
638+
/* no-op */
639+
});
640+
641+
resolveDatabaseContentsSpy = jest
642+
.spyOn(DatabaseResolver, "resolveDatabaseContents")
643+
.mockResolvedValue({} as DatabaseContents);
644+
645+
addDatabaseSourceArchiveFolderSpy = jest.spyOn(
646+
databaseManager,
647+
"addDatabaseSourceArchiveFolder",
648+
);
649+
650+
jest.mock("fs", () => ({
651+
promises: {
652+
pathExists: jest.fn().mockResolvedValue(true),
653+
},
654+
}));
655+
656+
mockDbItem = createMockDB();
657+
});
658+
659+
it("should resolve the database contents", async () => {
660+
await databaseManager.openDatabase(
661+
{} as ProgressCallback,
662+
{} as CancellationToken,
663+
mockDbItem.databaseUri,
664+
);
665+
666+
expect(resolveDatabaseContentsSpy).toBeCalledTimes(1);
667+
});
668+
669+
it("should add database source archive folder", async () => {
670+
await databaseManager.openDatabase(
671+
{} as ProgressCallback,
672+
{} as CancellationToken,
673+
mockDbItem.databaseUri,
674+
);
675+
676+
expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(1);
677+
});
678+
679+
describe("when codeQL.codespacesTemplate is set to true", () => {
680+
it("should create a skeleton QL pack", async () => {
681+
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(true);
682+
683+
await databaseManager.openDatabase(
684+
{} as ProgressCallback,
685+
{} as CancellationToken,
686+
mockDbItem.databaseUri,
687+
);
688+
689+
expect(createSkeletonPacksSpy).toBeCalledTimes(1);
690+
});
691+
});
692+
693+
describe("when codeQL.codespacesTemplate is set to false", () => {
694+
it("should not create a skeleton QL pack", async () => {
695+
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(false);
696+
697+
await databaseManager.openDatabase(
698+
{} as ProgressCallback,
699+
{} as CancellationToken,
700+
mockDbItem.databaseUri,
701+
);
702+
expect(createSkeletonPacksSpy).toBeCalledTimes(0);
703+
});
704+
});
705+
706+
describe("when codeQL.codespacesTemplate is not set", () => {
707+
it("should not create a skeleton QL pack", async () => {
708+
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(undefined);
709+
710+
await databaseManager.openDatabase(
711+
{} as ProgressCallback,
712+
{} as CancellationToken,
713+
mockDbItem.databaseUri,
714+
);
715+
expect(createSkeletonPacksSpy).toBeCalledTimes(0);
716+
});
717+
});
718+
});
719+
626720
function createMockDB(
627721
mockDbOptions = MOCK_DB_OPTIONS,
628722
// source archive location must be a real(-ish) location since

0 commit comments

Comments
 (0)