Merge remote-tracking branch 'origin/main' into koesie10/failed-view

This commit is contained in:
Koen Vlaswinkel
2022-11-09 10:05:14 +01:00
16 changed files with 315 additions and 63 deletions

View File

@@ -2,6 +2,10 @@
## [UNRELEASED]
## 1.7.5 - 8 November 2022
- Fix a bug where the AST Viewer was not working unless the associated CodeQL library pack is in the workspace. [#1735](https://github.com/github/vscode-codeql/pull/1735)
## 1.7.4 - 29 October 2022
No user facing changes.

View File

@@ -1,12 +1,12 @@
{
"name": "vscode-codeql",
"version": "1.7.5",
"version": "1.7.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "vscode-codeql",
"version": "1.7.5",
"version": "1.7.6",
"license": "MIT",
"dependencies": {
"@octokit/plugin-retry": "^3.0.9",
@@ -15,6 +15,7 @@
"@primer/react": "^35.0.0",
"@vscode/codicons": "^0.0.31",
"@vscode/webview-ui-toolkit": "^1.0.1",
"ajv": "^8.11.0",
"child-process-promise": "^2.2.1",
"chokidar": "^3.5.3",
"classnames": "~2.2.6",
@@ -105,7 +106,6 @@
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"@vscode/test-electron": "^2.2.0",
"ajv": "^8.11.0",
"ansi-colors": "^4.1.1",
"applicationinsights": "^2.3.5",
"babel-loader": "^8.2.5",
@@ -14650,7 +14650,6 @@
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -21602,8 +21601,7 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-glob": {
"version": "3.2.11",
@@ -29613,8 +29611,7 @@
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -33897,7 +33894,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -34880,7 +34876,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -38512,7 +38507,6 @@
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -51543,7 +51537,6 @@
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -57032,8 +57025,7 @@
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
"version": "3.2.11",
@@ -63145,8 +63137,7 @@
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -66535,8 +66526,7 @@
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.10.3",
@@ -67289,8 +67279,7 @@
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
},
"require-main-filename": {
"version": "1.0.1",
@@ -70084,7 +70073,6 @@
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.7.5",
"version": "1.7.6",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -1299,6 +1299,7 @@
"@primer/react": "^35.0.0",
"@vscode/codicons": "^0.0.31",
"@vscode/webview-ui-toolkit": "^1.0.1",
"ajv": "^8.11.0",
"child-process-promise": "^2.2.1",
"chokidar": "^3.5.3",
"classnames": "~2.2.6",
@@ -1389,7 +1390,6 @@
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"@vscode/test-electron": "^2.2.0",
"ajv": "^8.11.0",
"ansi-colors": "^4.1.1",
"applicationinsights": "^2.3.5",
"babel-loader": "^8.2.5",

View File

@@ -970,6 +970,12 @@ export class CodeQLCliServer implements Disposable {
}
}
async packResolveDependencies(dir: string): Promise<{ [pack: string]: string }> {
// Uses the default `--mode use-lock`, which creates the lock file if it doesn't exist.
const results: { [pack: string]: string } = await this.runJsonCodeQlCliCommand(['pack', 'resolve-dependencies'], [dir], 'Resolving pack dependencies');
return results;
}
async generateDil(qloFile: string, outFile: string): Promise<void> {
const extraArgs = await this.cliConstraints.supportsDecompileDil()
? ['--kind', 'dil', '-o', outFile, qloFile]

View File

@@ -5,9 +5,9 @@ import { DatabaseManager, DatabaseItem } from '../databases';
import fileRangeFromURI from './fileRangeFromURI';
import { ProgressCallback } from '../commandRunner';
import { KeyType } from './keyType';
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
import { qlpackOfDatabase, resolveQueries, runContextualQuery } from './queryResolver';
import { CancellationToken, LocationLink, Uri } from 'vscode';
import { createInitialQueryInfo, QueryWithResults } from '../run-queries-shared';
import { QueryWithResults } from '../run-queries-shared';
import { QueryRunner } from '../queryRunner';
export const SELECT_QUERY_NAME = '#select';
@@ -56,15 +56,7 @@ export async function getLocationsForUriString(
const links: FullLocationLink[] = [];
for (const query of await resolveQueries(cli, qlpack, keyType)) {
const initialInfo = await createInitialQueryInfo(
Uri.file(query),
{
name: db.name,
databaseUri: db.databaseUri.toString(),
},
false
);
const results = await qs.compileAndRunQueryAgainstDatabase(db, initialInfo, queryStorageDir, progress, token, templates);
const results = await runContextualQuery(query, db, queryStorageDir, qs, cli, progress, token, templates);
if (results.successful) {
links.push(...await getLinksFromResults(results, cli, db, filter));
}

View File

@@ -1,6 +1,7 @@
import * as fs from 'fs-extra';
import * as yaml from 'js-yaml';
import * as tmp from 'tmp-promise';
import * as path from 'path';
import * as helpers from '../helpers';
import {
@@ -12,6 +13,11 @@ import {
import { CodeQLCliServer } from '../cli';
import { DatabaseItem } from '../databases';
import { QlPacksForLanguage } from '../helpers';
import { logger } from '../logging';
import { createInitialQueryInfo } from '../run-queries-shared';
import { CancellationToken, Uri } from 'vscode';
import { ProgressCallback } from '../commandRunner';
import { QueryRunner } from '../queryRunner';
export async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem): Promise<QlPacksForLanguage> {
if (db.contents === undefined) {
@@ -104,3 +110,69 @@ export async function resolveQueries(cli: CodeQLCliServer, qlpacks: QlPacksForLa
void helpers.showAndLogErrorMessage(errorMessage);
throw new Error(`Couldn't find any queries tagged ${tagOfKeyType(keyType)} in any of the following packs: ${packsToSearch.join(', ')}.`);
}
async function resolveContextualQuery(cli: CodeQLCliServer, query: string): Promise<{ packPath: string, createdTempLockFile: boolean }> {
// Contextual queries now live within the standard library packs.
// This simplifies distribution (you don't need the standard query pack to use the AST viewer),
// but if the library pack doesn't have a lockfile, we won't be able to find
// other pack dependencies of the library pack.
// Work out the enclosing pack.
const packContents = await cli.packPacklist(query, false);
const packFilePath = packContents.find((p) => ['codeql-pack.yml', 'qlpack.yml'].includes(path.basename(p)));
if (packFilePath === undefined) {
// Should not happen; we already resolved this query.
throw new Error(`Could not find a CodeQL pack file for the pack enclosing the contextual query ${query}`);
}
const packPath = path.dirname(packFilePath);
const lockFilePath = packContents.find((p) => ['codeql-pack.lock.yml', 'qlpack.lock.yml'].includes(path.basename(p)));
let createdTempLockFile = false;
if (!lockFilePath) {
// No lock file, likely because this library pack is in the package cache.
// Create a lock file so that we can resolve dependencies and library path
// for the contextual query.
void logger.log(`Library pack ${packPath} is missing a lock file; creating a temporary lock file`);
await cli.packResolveDependencies(packPath);
createdTempLockFile = true;
// Clear CLI server pack cache before installing dependencies,
// so that it picks up the new lock file, not the previously cached pack.
void logger.log('Clearing the CodeQL CLI server\'s pack cache');
await cli.clearCache();
// Install dependencies.
void logger.log(`Installing package dependencies for library pack ${packPath}`);
await cli.packInstall(packPath);
}
return { packPath, createdTempLockFile };
}
async function removeTemporaryLockFile(packPath: string) {
const tempLockFilePath = path.resolve(packPath, 'codeql-pack.lock.yml');
void logger.log(`Deleting temporary package lock file at ${tempLockFilePath}`);
// It's fine if the file doesn't exist.
await fs.promises.rm(path.resolve(packPath, 'codeql-pack.lock.yml'), { force: true });
}
export async function runContextualQuery(query: string, db: DatabaseItem, queryStorageDir: string, qs: QueryRunner, cli: CodeQLCliServer, progress: ProgressCallback, token: CancellationToken, templates: Record<string, string>) {
const { packPath, createdTempLockFile } = await resolveContextualQuery(cli, query);
const initialInfo = await createInitialQueryInfo(
Uri.file(query),
{
name: db.name,
databaseUri: db.databaseUri.toString(),
},
false
);
void logger.log(`Running contextual query ${query}; results will be stored in ${queryStorageDir}`);
const queryResult = await qs.compileAndRunQueryAgainstDatabase(
db,
initialInfo,
queryStorageDir,
progress,
token,
templates
);
if (createdTempLockFile) {
await removeTemporaryLockFile(packPath);
}
return queryResult;
}

View File

@@ -21,18 +21,17 @@ import {
KeyType,
} from './keyType';
import { FullLocationLink, getLocationsForUriString, TEMPLATE_NAME } from './locationFinder';
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
import { qlpackOfDatabase, resolveQueries, runContextualQuery } from './queryResolver';
import { isCanary, NO_CACHE_AST_VIEWER } from '../config';
import { createInitialQueryInfo, QueryWithResults } from '../run-queries-shared';
import { QueryWithResults } from '../run-queries-shared';
import { QueryRunner } from '../queryRunner';
/**
* Run templated CodeQL queries to find definitions and references in
* Runs templated CodeQL queries to find definitions in
* source-language files. We may eventually want to find a way to
* generalize this to other custom queries, e.g. showing dataflow to
* or from a selected identifier.
*/
export class TemplateQueryDefinitionProvider implements DefinitionProvider {
private cache: CachedOperation<LocationLink[]>;
@@ -77,6 +76,12 @@ export class TemplateQueryDefinitionProvider implements DefinitionProvider {
}
}
/**
* Runs templated CodeQL queries to find references in
* source-language files. We may eventually want to find a way to
* generalize this to other custom queries, e.g. showing dataflow to
* or from a selected identifier.
*/
export class TemplateQueryReferenceProvider implements ReferenceProvider {
private cache: CachedOperation<FullLocationLink[]>;
@@ -131,6 +136,10 @@ type QueryWithDb = {
dbUri: Uri
};
/**
* Run templated CodeQL queries to produce AST information for
* source-language files.
*/
export class TemplatePrintAstProvider {
private cache: CachedOperation<QueryWithDb>;
@@ -199,29 +208,18 @@ export class TemplatePrintAstProvider {
zippedArchive.pathWithinSourceArchive
};
const initialInfo = await createInitialQueryInfo(
Uri.file(query),
{
name: db.name,
databaseUri: db.databaseUri.toString(),
},
false
);
const queryResult = await runContextualQuery(query, db, this.queryStorageDir, this.qs, this.cli, progress, token, templates);
return {
query: await this.qs.compileAndRunQueryAgainstDatabase(
db,
initialInfo,
this.queryStorageDir,
progress,
token,
templates
),
query: queryResult,
dbUri: db.databaseUri
};
}
}
/**
* Run templated CodeQL queries to produce CFG information for
* source-language files.
*/
export class TemplatePrintCfgProvider {
private cache: CachedOperation<[Uri, Record<string, string>] | undefined>;

View File

@@ -3,20 +3,25 @@ import * as path from 'path';
import { cloneDbConfig, DbConfig } from './db-config';
import * as chokidar from 'chokidar';
import { DisposableObject } from '../pure/disposable-object';
import { DbConfigValidator } from './db-config-validator';
export class DbConfigStore extends DisposableObject {
private readonly configPath: string;
private readonly configValidator: DbConfigValidator;
private config: DbConfig;
private configWatcher: chokidar.FSWatcher | undefined;
public constructor(workspaceStoragePath: string) {
public constructor(
workspaceStoragePath: string,
extensionPath: string) {
super();
this.configPath = path.join(workspaceStoragePath, 'workspace-databases.json');
this.config = this.createEmptyConfig();
this.configWatcher = undefined;
this.configValidator = new DbConfigValidator(extensionPath);
}
public async initialize(): Promise<void> {
@@ -37,6 +42,10 @@ export class DbConfigStore extends DisposableObject {
return this.configPath;
}
public validateConfig(): string[] {
return this.configValidator.validate(this.config);
}
private async loadConfig(): Promise<void> {
if (!await fs.pathExists(this.configPath)) {
await fs.writeJSON(this.configPath, this.createEmptyConfig(), { spaces: 2 });

View File

@@ -0,0 +1,24 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import Ajv from 'ajv';
import { DbConfig } from './db-config';
export class DbConfigValidator {
private readonly schema: any;
constructor(extensionPath: string) {
const schemaPath = path.resolve(extensionPath, 'workspace-databases-schema.json');
this.schema = fs.readJsonSync(schemaPath);
}
public validate(dbConfig: DbConfig): string[] {
const ajv = new Ajv({ allErrors: true });
ajv.validate(this.schema, dbConfig);
if (ajv.errors) {
return ajv.errors.map((error) => `${error.instancePath} ${error.message}`);
}
return [];
}
}

View File

@@ -22,7 +22,8 @@ export class DbModule extends DisposableObject {
void logger.log('Initializing database module');
const storagePath = extensionContext.storageUri?.fsPath || extensionContext.globalStorageUri.fsPath;
const dbConfigStore = new DbConfigStore(storagePath);
const extensionPath = extensionContext.extensionPath;
const dbConfigStore = new DbConfigStore(storagePath, extensionPath);
await dbConfigStore.initialize();
const dbManager = new DbManager(dbConfigStore);

View File

@@ -258,6 +258,112 @@ FullExampleWithoutSkipped.args = {
repoResults,
};
export const Canceled = Template.bind({});
Canceled.args = {
variantAnalysis: {
...variantAnalysis,
status: VariantAnalysisStatus.Canceled,
completedAt: new Date(new Date(variantAnalysis.createdAt).getTime() + 100_000).toISOString(),
scannedRepos: [
{
repository: {
...createMockRepositoryWithMetadata(),
id: 1,
fullName: 'octodemo/hello-world-1',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 200,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 2,
fullName: 'octodemo/hello-world-2',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 10_000,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 3,
fullName: 'octodemo/hello-world-3',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 500,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 4,
fullName: 'octodemo/hello-world-4',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 5,
fullName: 'octodemo/hello-world-5',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Failed,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 6,
fullName: 'octodemo/hello-world-6',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 7,
fullName: 'octodemo/hello-world-7',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 8,
fullName: 'octodemo/hello-world-8',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 9,
fullName: 'octodemo/hello-world-9',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
{
repository: {
...createMockRepositoryWithMetadata(),
id: 10,
fullName: 'octodemo/hello-world-10',
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Canceled,
},
],
},
repoStates,
repoResults,
};
export const Failed = Template.bind({});
Failed.args = {
variantAnalysis: {

View File

@@ -56,6 +56,13 @@ export const VariantAnalysisOutcomePanels = ({
const warnings = (
<WarningsContainer>
{variantAnalysis.status === VariantAnalysisStatus.Canceled && (
<Alert
type="warning"
title="Query manually stopped"
message="This query was manually stopped before the analysis completed. Results may be partial."
/>
)}
{variantAnalysis.status === VariantAnalysisStatus.Failed && variantAnalysis.failureReason && (
<FailureReasonAlert failureReason={variantAnalysis.failureReason} showLogsButton={!!variantAnalysis.actionsWorkflowRunId} />
)}

View File

@@ -136,6 +136,14 @@ describe(VariantAnalysisOutcomePanels.name, () => {
expect(screen.getByText('No database')).toBeInTheDocument();
});
it('renders warning with canceled variant analysis', () => {
render({
status: VariantAnalysisStatus.Canceled,
});
expect(screen.getByText('Warning: Query manually stopped')).toBeInTheDocument();
});
it('renders warning with access mismatch repos', () => {
render({
skippedRepos: {

View File

@@ -21,7 +21,7 @@ import { QueryRunner } from '../../queryRunner';
* Integration tests for queries
*/
describe('Queries', function() {
this.timeout(20000);
this.timeout(20_000);
before(function() {
skipIfNoCodeQL(this);
@@ -42,7 +42,9 @@ describe('Queries', function() {
let qlFile: string;
beforeEach(async () => {
beforeEach(async function() {
this.timeout(20_000);
sandbox = sinon.createSandbox();
try {
@@ -89,7 +91,8 @@ describe('Queries', function() {
}
});
afterEach(async () => {
afterEach(async function() {
this.timeout(20_000);
try {
sandbox.restore();
safeDel(qlpackFile);

View File

@@ -4,6 +4,7 @@ import { DbConfigStore } from '../../../src/databases/db-config-store';
import { expect } from 'chai';
describe('db config store', async () => {
const extensionPath = path.join(__dirname, '../../..');
const tempWorkspaceStoragePath = path.join(__dirname, 'test-workspace');
const testDataStoragePath = path.join(__dirname, 'data');
@@ -18,7 +19,7 @@ describe('db config store', async () => {
it('should create a new config if one does not exist', async () => {
const configPath = path.join(tempWorkspaceStoragePath, 'workspace-databases.json');
const configStore = new DbConfigStore(tempWorkspaceStoragePath);
const configStore = new DbConfigStore(tempWorkspaceStoragePath, extensionPath);
await configStore.initialize();
expect(await fs.pathExists(configPath)).to.be.true;
@@ -29,7 +30,7 @@ describe('db config store', async () => {
});
it('should load an existing config', async () => {
const configStore = new DbConfigStore(testDataStoragePath);
const configStore = new DbConfigStore(testDataStoragePath, extensionPath);
await configStore.initialize();
const config = configStore.getConfig();
@@ -44,7 +45,7 @@ describe('db config store', async () => {
});
it('should not allow modification of the config', async () => {
const configStore = new DbConfigStore(testDataStoragePath);
const configStore = new DbConfigStore(testDataStoragePath, extensionPath);
await configStore.initialize();
const config = configStore.getConfig();

View File

@@ -0,0 +1,33 @@
import { expect } from 'chai';
import * as path from 'path';
import { DbConfig } from '../../../src/databases/db-config';
import { DbConfigValidator } from '../../../src/databases/db-config-validator';
describe('db config validation', async () => {
const extensionPath = path.join(__dirname, '../../..');
const configValidator = new DbConfigValidator(extensionPath);
it('should return error when file is not valid', async () => {
// We're intentionally bypassing the type check because we'd
// like to make sure validation errors are highlighted.
const dbConfig = {
'remote': {
'repositoryLists': [
{
'name': 'repoList1',
'repositories': ['foo/bar', 'foo/baz']
}
],
'repositories': ['owner/repo1', 'owner/repo2', 'owner/repo3'],
'somethingElse': 'bar'
}
} as any as DbConfig;
const validationOutput = configValidator.validate(dbConfig);
expect(validationOutput).to.have.length(2);
expect(validationOutput[0]).to.deep.equal('/remote must have required property \'owners\'');
expect(validationOutput[1]).to.deep.equal('/remote must NOT have additional properties');
});
});