Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b660d31a29 | ||
|
|
4e342889d9 | ||
|
|
f184b213d3 | ||
|
|
c09077b91f | ||
|
|
b25def9584 | ||
|
|
77602b3d71 | ||
|
|
bbcffb5762 | ||
|
|
926c75ffbf | ||
|
|
479edd7d9e | ||
|
|
dd318f5d07 | ||
|
|
473d289da1 | ||
|
|
116a70ed8b | ||
|
|
d16fbfd60e | ||
|
|
2ba8f3fd82 | ||
|
|
fc38557b41 | ||
|
|
80a31e65e2 | ||
|
|
481a9db14e | ||
|
|
a1033eb498 | ||
|
|
fc27999ba9 | ||
|
|
f5742f786a | ||
|
|
85db205d2d | ||
|
|
b6e466f4d6 | ||
|
|
53d358f6c3 | ||
|
|
62096cbc92 | ||
|
|
ef34b212d0 | ||
|
|
ab6777f276 | ||
|
|
9a5b0ef202 | ||
|
|
646d702a80 | ||
|
|
03a7a51504 | ||
|
|
dfb007aeab | ||
|
|
b66ad69345 | ||
|
|
3b5bd45675 | ||
|
|
2103fc1af7 | ||
|
|
54782b9539 | ||
|
|
6632958131 |
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -127,6 +127,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
|
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: extensions/ql-vscode/.nvmrc
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -144,6 +151,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
OPEN_VSX_TOKEN: ${{ secrets.OPEN_VSX_TOKEN }}
|
OPEN_VSX_TOKEN: ${{ secrets.OPEN_VSX_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: extensions/ql-vscode/.nvmrc
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v20.15.1
|
v20.16.0
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# CodeQL for Visual Studio Code: Changelog
|
# CodeQL for Visual Studio Code: Changelog
|
||||||
|
|
||||||
|
## 1.16.0 - 10 October 2024
|
||||||
|
|
||||||
|
- Increase the required version of VS Code to 1.90.0. [#3737](https://github.com/github/vscode-codeql/pull/3737)
|
||||||
|
- Fix a bug where some variant analysis results failed to download. [#3750](https://github.com/github/vscode-codeql/pull/3750)
|
||||||
|
|
||||||
## 1.15.0 - 26 September 2024
|
## 1.15.0 - 26 September 2024
|
||||||
|
|
||||||
- Update results view to display the length of the shortest path for path queries. [#3687](https://github.com/github/vscode-codeql/pull/3687)
|
- Update results view to display the length of the shortest path for path queries. [#3687](https://github.com/github/vscode-codeql/pull/3687)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"chromiumVersion": "114",
|
"chromiumVersion": "122",
|
||||||
"electronVersion": "25.8.0"
|
"electronVersion": "29.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
1065
extensions/ql-vscode/package-lock.json
generated
1065
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
"description": "CodeQL for Visual Studio Code",
|
"description": "CodeQL for Visual Studio Code",
|
||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.15.0",
|
"version": "1.16.0",
|
||||||
"publisher": "GitHub",
|
"publisher": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
"url": "https://github.com/github/vscode-codeql"
|
"url": "https://github.com/github/vscode-codeql"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.82.0",
|
"vscode": "^1.90.0",
|
||||||
"node": "^20.15.1",
|
"node": "^20.16.0",
|
||||||
"npm": ">=7.20.6"
|
"npm": ">=7.20.6"
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
@@ -1965,16 +1965,16 @@
|
|||||||
"generate:chromium-version": "vite-node scripts/generate-chromium-version.ts",
|
"generate:chromium-version": "vite-node scripts/generate-chromium-version.ts",
|
||||||
"check-types": "find . -type f -name \"tsconfig.json\" -not -path \"./node_modules/*\" | sed -r 's|/[^/]+$||' | sort | uniq | xargs -I {} sh -c \"echo Checking types in {} && cd {} && npx tsc --noEmit\"",
|
"check-types": "find . -type f -name \"tsconfig.json\" -not -path \"./node_modules/*\" | sed -r 's|/[^/]+$||' | sort | uniq | xargs -I {} sh -c \"echo Checking types in {} && cd {} && npx tsc --noEmit\"",
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"prepare": "cd ../.. && husky install"
|
"prepare": "cd ../.. && husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.26.12",
|
"@floating-ui/react": "^0.26.24",
|
||||||
"@octokit/plugin-retry": "^7.1.2",
|
"@octokit/plugin-retry": "^7.1.2",
|
||||||
"@octokit/plugin-throttling": "^9.3.1",
|
"@octokit/plugin-throttling": "^9.3.1",
|
||||||
"@octokit/rest": "^21.0.2",
|
"@octokit/rest": "^21.0.2",
|
||||||
"@vscode/codicons": "^0.0.36",
|
"@vscode/codicons": "^0.0.36",
|
||||||
"@vscode/debugadapter": "^1.59.0",
|
"@vscode/debugadapter": "^1.59.0",
|
||||||
"@vscode/debugprotocol": "^1.65.0",
|
"@vscode/debugprotocol": "^1.68.0",
|
||||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||||
"ajv": "^8.11.0",
|
"ajv": "^8.11.0",
|
||||||
"child-process-promise": "^2.2.1",
|
"child-process-promise": "^2.2.1",
|
||||||
@@ -1985,7 +1985,6 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"msw": "^2.2.13",
|
"msw": "^2.2.13",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"node-fetch": "^3.3.2",
|
|
||||||
"p-queue": "^8.0.1",
|
"p-queue": "^8.0.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -2013,18 +2012,18 @@
|
|||||||
"@github/markdownlint-github": "^0.6.2",
|
"@github/markdownlint-github": "^0.6.2",
|
||||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||||
"@playwright/test": "^1.40.1",
|
"@playwright/test": "^1.40.1",
|
||||||
"@storybook/addon-a11y": "^8.3.1",
|
"@storybook/addon-a11y": "^8.3.4",
|
||||||
"@storybook/addon-actions": "^8.3.1",
|
"@storybook/addon-actions": "^8.3.4",
|
||||||
"@storybook/addon-essentials": "^8.3.1",
|
"@storybook/addon-essentials": "^8.3.4",
|
||||||
"@storybook/addon-interactions": "^8.3.1",
|
"@storybook/addon-interactions": "^8.3.4",
|
||||||
"@storybook/addon-links": "^8.3.1",
|
"@storybook/addon-links": "^8.3.4",
|
||||||
"@storybook/blocks": "^8.0.2",
|
"@storybook/blocks": "^8.0.2",
|
||||||
"@storybook/components": "^8.3.1",
|
"@storybook/components": "^8.3.4",
|
||||||
"@storybook/csf": "^0.1.11",
|
"@storybook/csf": "^0.1.11",
|
||||||
"@storybook/icons": "^1.2.12",
|
"@storybook/icons": "^1.2.12",
|
||||||
"@storybook/manager-api": "^8.3.1",
|
"@storybook/manager-api": "^8.3.4",
|
||||||
"@storybook/react": "^8.3.1",
|
"@storybook/react": "^8.3.4",
|
||||||
"@storybook/react-vite": "^8.3.1",
|
"@storybook/react-vite": "^8.3.4",
|
||||||
"@storybook/theming": "^8.2.4",
|
"@storybook/theming": "^8.2.4",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.5.0",
|
"@testing-library/jest-dom": "^6.5.0",
|
||||||
@@ -2040,7 +2039,7 @@
|
|||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/js-yaml": "^4.0.6",
|
"@types/js-yaml": "^4.0.6",
|
||||||
"@types/nanoid": "^3.0.0",
|
"@types/nanoid": "^3.0.0",
|
||||||
"@types/node": "20.15.*",
|
"@types/node": "20.16.*",
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "^18.3.1",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/sarif": "^2.1.2",
|
"@types/sarif": "^2.1.2",
|
||||||
@@ -2050,10 +2049,10 @@
|
|||||||
"@types/tar-stream": "^3.1.3",
|
"@types/tar-stream": "^3.1.3",
|
||||||
"@types/through2": "^2.0.36",
|
"@types/through2": "^2.0.36",
|
||||||
"@types/tmp": "^0.2.6",
|
"@types/tmp": "^0.2.6",
|
||||||
"@types/vscode": "^1.82.0",
|
"@types/vscode": "1.90.0",
|
||||||
"@types/yauzl": "^2.10.3",
|
"@types/yauzl": "^2.10.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.6.0",
|
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||||
"@typescript-eslint/parser": "^8.6.0",
|
"@typescript-eslint/parser": "^8.8.0",
|
||||||
"@vscode/test-electron": "^2.3.9",
|
"@vscode/test-electron": "^2.3.9",
|
||||||
"@vscode/vsce": "^2.24.0",
|
"@vscode/vsce": "^2.24.0",
|
||||||
"ansi-colors": "^4.1.1",
|
"ansi-colors": "^4.1.1",
|
||||||
@@ -2068,12 +2067,12 @@
|
|||||||
"eslint-plugin-etc": "^2.0.2",
|
"eslint-plugin-etc": "^2.0.2",
|
||||||
"eslint-plugin-github": "^5.0.1",
|
"eslint-plugin-github": "^5.0.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jest-dom": "^5.2.0",
|
"eslint-plugin-jest-dom": "^5.4.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.8.0",
|
||||||
"glob": "^10.0.0",
|
"glob": "^11.0.0",
|
||||||
"gulp": "^5.0.0",
|
"gulp": "^5.0.0",
|
||||||
"gulp-esbuild": "^0.12.1",
|
"gulp-esbuild": "^0.12.1",
|
||||||
"gulp-replace": "^1.1.3",
|
"gulp-replace": "^1.1.3",
|
||||||
@@ -2088,10 +2087,10 @@
|
|||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"storybook": "^8.3.1",
|
"storybook": "^8.3.4",
|
||||||
"tar-stream": "^3.1.7",
|
"tar-stream": "^3.1.7",
|
||||||
"through2": "^4.0.2",
|
"through2": "^4.0.2",
|
||||||
"ts-jest": "^29.1.4",
|
"ts-jest": "^29.2.5",
|
||||||
"ts-json-schema-generator": "^2.1.1",
|
"ts-json-schema-generator": "^2.1.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"ts-unused-exports": "^10.1.0",
|
"ts-unused-exports": "^10.1.0",
|
||||||
|
|||||||
@@ -1,29 +1,3 @@
|
|||||||
diff --git a/node_modules/jest-runner-vscode/dist/child/environment.js b/node_modules/jest-runner-vscode/dist/child/environment.js
|
|
||||||
index 1ac28d5..f91f216 100644
|
|
||||||
--- a/node_modules/jest-runner-vscode/dist/child/environment.js
|
|
||||||
+++ b/node_modules/jest-runner-vscode/dist/child/environment.js
|
|
||||||
@@ -10,6 +10,21 @@ const wrap_io_1 = __importDefault(require("./wrap-io"));
|
|
||||||
const load_pnp_1 = __importDefault(require("./load-pnp"));
|
|
||||||
const ipc = new ipc_client_1.default('env');
|
|
||||||
class VSCodeEnvironment extends jest_environment_node_1.default {
|
|
||||||
+ constructor(config, context) {
|
|
||||||
+ super(config, context);
|
|
||||||
+ // The _VSCODE_NODE_MODULES is a proxy which will require a module if any property
|
|
||||||
+ // on it is accessed. This is a workaround for the fact that jest will call
|
|
||||||
+ // _isMockFunction on the module, which will cause that function to be required.
|
|
||||||
+ this.global._VSCODE_NODE_MODULES = new Proxy(this.global._VSCODE_NODE_MODULES, {
|
|
||||||
+ get(target, prop) {
|
|
||||||
+ if (prop === '_isMockFunction') {
|
|
||||||
+ return undefined;
|
|
||||||
+ }
|
|
||||||
+ return target[prop];
|
|
||||||
+ },
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
async setup() {
|
|
||||||
await super.setup();
|
|
||||||
await (0, load_pnp_1.default)();
|
|
||||||
diff --git a/node_modules/jest-runner-vscode/dist/child/runner.js b/node_modules/jest-runner-vscode/dist/child/runner.js
|
diff --git a/node_modules/jest-runner-vscode/dist/child/runner.js b/node_modules/jest-runner-vscode/dist/child/runner.js
|
||||||
index 0663c5c..bdf4a8b 100644
|
index 0663c5c..bdf4a8b 100644
|
||||||
--- a/node_modules/jest-runner-vscode/dist/child/runner.js
|
--- a/node_modules/jest-runner-vscode/dist/child/runner.js
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { reportUnzipProgress } from "../common/vscode/unzip-progress";
|
|||||||
import type { Release } from "./distribution/release";
|
import type { Release } from "./distribution/release";
|
||||||
import { ReleasesApiConsumer } from "./distribution/releases-api-consumer";
|
import { ReleasesApiConsumer } from "./distribution/releases-api-consumer";
|
||||||
import { createTimeoutSignal } from "../common/fetch-stream";
|
import { createTimeoutSignal } from "../common/fetch-stream";
|
||||||
import { AbortError } from "node-fetch";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* distribution.ts
|
* distribution.ts
|
||||||
@@ -416,24 +415,40 @@ class ExtensionSpecificDistributionManager {
|
|||||||
const totalNumBytes = contentLength
|
const totalNumBytes = contentLength
|
||||||
? parseInt(contentLength, 10)
|
? parseInt(contentLength, 10)
|
||||||
: undefined;
|
: undefined;
|
||||||
reportStreamProgress(
|
|
||||||
body,
|
const reportProgress = reportStreamProgress(
|
||||||
`Downloading CodeQL CLI ${release.name}…`,
|
`Downloading CodeQL CLI ${release.name}…`,
|
||||||
totalNumBytes,
|
totalNumBytes,
|
||||||
progressCallback,
|
progressCallback,
|
||||||
);
|
);
|
||||||
|
|
||||||
body.on("data", onData);
|
const reader = body.getReader();
|
||||||
|
for (;;) {
|
||||||
await new Promise((resolve, reject) => {
|
const { done, value } = await reader.read();
|
||||||
if (!archiveFile) {
|
if (done) {
|
||||||
throw new Error("Invariant violation: archiveFile not set");
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.pipe(archiveFile).on("finish", resolve).on("error", reject);
|
onData();
|
||||||
|
reportProgress(value?.length ?? 0);
|
||||||
|
|
||||||
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
await new Promise((resolve, reject) => {
|
||||||
body.on("error", reject);
|
archiveFile?.write(value, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
archiveFile?.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
disposeTimeout();
|
disposeTimeout();
|
||||||
@@ -454,8 +469,8 @@ class ExtensionSpecificDistributionManager {
|
|||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof AbortError) {
|
if (e instanceof DOMException && e.name === "AbortError") {
|
||||||
const thrownError = new AbortError("The download timed out.");
|
const thrownError = new Error("The download timed out.");
|
||||||
thrownError.stack = e.stack;
|
thrownError.stack = e.stack;
|
||||||
throw thrownError;
|
throw thrownError;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { Response } from "node-fetch";
|
|
||||||
import { default as fetch } from "node-fetch";
|
|
||||||
import type { Range } from "semver";
|
import type { Range } from "semver";
|
||||||
import { compare, parse, satisfies } from "semver";
|
import { compare, parse, satisfies } from "semver";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { join, resolve } from "path";
|
|||||||
import { pathExists } from "fs-extra";
|
import { pathExists } from "fs-extra";
|
||||||
import type { SetupServer } from "msw/node";
|
import type { SetupServer } from "msw/node";
|
||||||
import { setupServer } from "msw/node";
|
import { setupServer } from "msw/node";
|
||||||
|
import type { UnhandledRequestStrategy } from "msw/lib/core/utils/request/onUnhandledRequest";
|
||||||
|
|
||||||
import { DisposableObject } from "../disposable-object";
|
import { DisposableObject } from "../disposable-object";
|
||||||
|
|
||||||
@@ -26,12 +27,14 @@ export class MockGitHubApiServer extends DisposableObject {
|
|||||||
this.recorder = this.push(new Recorder(this.server));
|
this.recorder = this.push(new Recorder(this.server));
|
||||||
}
|
}
|
||||||
|
|
||||||
public startServer(): void {
|
public startServer(
|
||||||
|
onUnhandledRequest: UnhandledRequestStrategy = "bypass",
|
||||||
|
): void {
|
||||||
if (this._isListening) {
|
if (this._isListening) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.server.listen({ onUnhandledRequest: "bypass" });
|
this.server.listen({ onUnhandledRequest });
|
||||||
this._isListening = true;
|
this._isListening = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +57,7 @@ export class MockGitHubApiServer extends DisposableObject {
|
|||||||
const scenarioPath = join(scenariosPath, scenarioName);
|
const scenarioPath = join(scenariosPath, scenarioName);
|
||||||
|
|
||||||
const handlers = await createRequestHandlers(scenarioPath);
|
const handlers = await createRequestHandlers(scenarioPath);
|
||||||
this.server.resetHandlers();
|
this.server.resetHandlers(...handlers);
|
||||||
this.server.use(...handlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveScenario(
|
public async saveScenario(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ensureDir, writeFile } from "fs-extra";
|
import { ensureDir, writeFile } from "fs-extra";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import fetch from "node-fetch";
|
|
||||||
import type { SetupServer } from "msw/node";
|
import type { SetupServer } from "msw/node";
|
||||||
|
|
||||||
import { DisposableObject } from "../disposable-object";
|
import { DisposableObject } from "../disposable-object";
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import { retry } from "@octokit/plugin-retry";
|
import { retry } from "@octokit/plugin-retry";
|
||||||
import fetch from "node-fetch";
|
|
||||||
|
|
||||||
export const AppOctokit = Octokit.defaults({
|
export const AppOctokit = Octokit.defaults({
|
||||||
request: {
|
request: {
|
||||||
fetch,
|
// MSW replaces the global fetch object, so we can't just pass a reference to the
|
||||||
|
// fetch object at initialization time. Instead, we pass a function that will
|
||||||
|
// always call the global fetch object.
|
||||||
|
fetch: (input: string | URL | Request, init?: RequestInit) =>
|
||||||
|
fetch(input, init),
|
||||||
},
|
},
|
||||||
retry,
|
retry,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -97,17 +97,15 @@ export function withProgress<R>(
|
|||||||
* Displays a progress monitor that indicates how much progess has been made
|
* Displays a progress monitor that indicates how much progess has been made
|
||||||
* reading from a stream.
|
* reading from a stream.
|
||||||
*
|
*
|
||||||
* @param readable The stream to read progress from
|
|
||||||
* @param messagePrefix A prefix for displaying the message
|
* @param messagePrefix A prefix for displaying the message
|
||||||
* @param totalNumBytes Total number of bytes in this stream
|
* @param totalNumBytes Total number of bytes in this stream
|
||||||
* @param progress The progress callback used to set messages
|
* @param progress The progress callback used to set messages
|
||||||
*/
|
*/
|
||||||
export function reportStreamProgress(
|
export function reportStreamProgress(
|
||||||
readable: NodeJS.ReadableStream,
|
|
||||||
messagePrefix: string,
|
messagePrefix: string,
|
||||||
totalNumBytes?: number,
|
totalNumBytes?: number,
|
||||||
progress?: ProgressCallback,
|
progress?: ProgressCallback,
|
||||||
) {
|
): (bytesRead: number) => void {
|
||||||
if (progress && totalNumBytes) {
|
if (progress && totalNumBytes) {
|
||||||
let numBytesDownloaded = 0;
|
let numBytesDownloaded = 0;
|
||||||
const updateProgress = () => {
|
const updateProgress = () => {
|
||||||
@@ -123,10 +121,10 @@ export function reportStreamProgress(
|
|||||||
// Display the progress straight away rather than waiting for the first chunk.
|
// Display the progress straight away rather than waiting for the first chunk.
|
||||||
updateProgress();
|
updateProgress();
|
||||||
|
|
||||||
readable.on("data", (data) => {
|
return (bytesRead: number) => {
|
||||||
numBytesDownloaded += data.length;
|
numBytesDownloaded += bytesRead;
|
||||||
updateProgress();
|
updateProgress();
|
||||||
});
|
};
|
||||||
} else if (progress) {
|
} else if (progress) {
|
||||||
progress({
|
progress({
|
||||||
step: 1,
|
step: 1,
|
||||||
@@ -134,4 +132,6 @@ export function reportStreamProgress(
|
|||||||
message: `${messagePrefix} (Size unknown)`,
|
message: `${messagePrefix} (Size unknown)`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { Response } from "node-fetch";
|
|
||||||
import fetch, { AbortError } from "node-fetch";
|
|
||||||
import type { InputBoxOptions } from "vscode";
|
import type { InputBoxOptions } from "vscode";
|
||||||
import { Uri, window } from "vscode";
|
import { Uri, window } from "vscode";
|
||||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
@@ -536,8 +534,8 @@ export class DatabaseFetcher {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
disposeTimeout();
|
disposeTimeout();
|
||||||
|
|
||||||
if (e instanceof AbortError) {
|
if (e instanceof DOMException && e.name === "AbortError") {
|
||||||
const thrownError = new AbortError("The request timed out.");
|
const thrownError = new Error("The request timed out.");
|
||||||
thrownError.stack = e.stack;
|
thrownError.stack = e.stack;
|
||||||
throw thrownError;
|
throw thrownError;
|
||||||
}
|
}
|
||||||
@@ -556,16 +554,41 @@ export class DatabaseFetcher {
|
|||||||
const totalNumBytes = contentLength
|
const totalNumBytes = contentLength
|
||||||
? parseInt(contentLength, 10)
|
? parseInt(contentLength, 10)
|
||||||
: undefined;
|
: undefined;
|
||||||
reportStreamProgress(body, "Downloading database", totalNumBytes, progress);
|
|
||||||
|
|
||||||
body.on("data", onData);
|
const reportProgress = reportStreamProgress(
|
||||||
|
"Downloading database",
|
||||||
|
totalNumBytes,
|
||||||
|
progress,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
const reader = body.getReader();
|
||||||
body.pipe(archiveFileStream).on("finish", resolve).on("error", reject);
|
for (;;) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
onData();
|
||||||
body.on("error", reject);
|
reportProgress(value?.length ?? 0);
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
archiveFileStream.write(value, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
archiveFileStream.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Close and remove the file if an error occurs
|
// Close and remove the file if an error occurs
|
||||||
@@ -573,8 +596,8 @@ export class DatabaseFetcher {
|
|||||||
void remove(archivePath);
|
void remove(archivePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (e instanceof AbortError) {
|
if (e instanceof DOMException && e.name === "AbortError") {
|
||||||
const thrownError = new AbortError("The download timed out.");
|
const thrownError = new Error("The download timed out.");
|
||||||
thrownError.stack = e.stack;
|
thrownError.stack = e.stack;
|
||||||
throw thrownError;
|
throw thrownError;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ import {
|
|||||||
REPO_STATES_FILENAME,
|
REPO_STATES_FILENAME,
|
||||||
writeRepoStates,
|
writeRepoStates,
|
||||||
} from "./repo-states-store";
|
} from "./repo-states-store";
|
||||||
import { FetchError } from "node-fetch";
|
|
||||||
import {
|
import {
|
||||||
showAndLogExceptionWithTelemetry,
|
showAndLogExceptionWithTelemetry,
|
||||||
showAndLogInformationMessage,
|
showAndLogInformationMessage,
|
||||||
@@ -859,7 +858,9 @@ export class VariantAnalysisManager
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (
|
if (
|
||||||
retry++ < maxRetryCount &&
|
retry++ < maxRetryCount &&
|
||||||
e instanceof FetchError &&
|
e &&
|
||||||
|
typeof e === "object" &&
|
||||||
|
"code" in e &&
|
||||||
(e.code === "ETIMEDOUT" || e.code === "ECONNRESET")
|
(e.code === "ETIMEDOUT" || e.code === "ECONNRESET")
|
||||||
) {
|
) {
|
||||||
void this.app.logger.log(
|
void this.app.logger.log(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { appendFile, pathExists, rm } from "fs-extra";
|
import { appendFile, pathExists, rm } from "fs-extra";
|
||||||
import fetch from "node-fetch";
|
|
||||||
import { EOL } from "os";
|
import { EOL } from "os";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
@@ -94,17 +93,23 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
|
|
||||||
const response = await fetch(repoTask.artifactUrl);
|
const response = await fetch(repoTask.artifactUrl);
|
||||||
|
|
||||||
let responseSize = parseInt(response.headers.get("content-length") || "0");
|
const responseSize = parseInt(
|
||||||
if (responseSize === 0 && response.size > 0) {
|
response.headers.get("content-length") || "1",
|
||||||
responseSize = response.size;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
throw new Error("No response body found");
|
throw new Error("No response body found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
|
||||||
let amountDownloaded = 0;
|
let amountDownloaded = 0;
|
||||||
for await (const chunk of response.body) {
|
for (;;) {
|
||||||
|
const { value: chunk, done } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
await appendFile(zipFilePath, Buffer.from(chunk));
|
await appendFile(zipFilePath, Buffer.from(chunk));
|
||||||
amountDownloaded += chunk.length;
|
amountDownloaded += chunk.length;
|
||||||
await onDownloadPercentageChanged(
|
await onDownloadPercentageChanged(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[
|
[
|
||||||
"v2.19.0",
|
"v2.19.1",
|
||||||
"v2.18.4",
|
"v2.18.4",
|
||||||
"v2.17.6",
|
"v2.17.6",
|
||||||
"v2.16.6",
|
"v2.16.6",
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type { VariantAnalysisRepoTask } from "../../../../src/variant-analysis/g
|
|||||||
import { VariantAnalysisRepoStatus } from "../../../../src/variant-analysis/shared/variant-analysis";
|
import { VariantAnalysisRepoStatus } from "../../../../src/variant-analysis/shared/variant-analysis";
|
||||||
import { createMockRepository } from "./repository";
|
import { createMockRepository } from "./repository";
|
||||||
|
|
||||||
export function createMockVariantAnalysisRepoTask(): VariantAnalysisRepoTask {
|
export function createMockVariantAnalysisRepoTask(): VariantAnalysisRepoTask &
|
||||||
|
Required<Pick<VariantAnalysisRepoTask, "artifact_url">> {
|
||||||
return {
|
return {
|
||||||
repository: {
|
repository: {
|
||||||
...createMockRepository(),
|
...createMockRepository(),
|
||||||
@@ -12,6 +13,6 @@ export function createMockVariantAnalysisRepoTask(): VariantAnalysisRepoTask {
|
|||||||
analysis_status: VariantAnalysisRepoStatus.Succeeded,
|
analysis_status: VariantAnalysisRepoStatus.Succeeded,
|
||||||
result_count: faker.number.int(),
|
result_count: faker.number.int(),
|
||||||
artifact_size_in_bytes: faker.number.int(),
|
artifact_size_in_bytes: faker.number.int(),
|
||||||
artifact_url: "https://www.pickles.com",
|
artifact_url: faker.internet.url(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { createMockRepositoryWithMetadata } from "./repository";
|
|||||||
|
|
||||||
export function createMockVariantAnalysisRepositoryTask(
|
export function createMockVariantAnalysisRepositoryTask(
|
||||||
data?: Partial<VariantAnalysisRepositoryTask>,
|
data?: Partial<VariantAnalysisRepositoryTask>,
|
||||||
): VariantAnalysisRepositoryTask {
|
): VariantAnalysisRepositoryTask &
|
||||||
|
Required<Pick<VariantAnalysisRepositoryTask, "artifactUrl">> {
|
||||||
return {
|
return {
|
||||||
repository: createMockRepositoryWithMetadata(),
|
repository: createMockRepositoryWithMetadata(),
|
||||||
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
analysisStatus: VariantAnalysisRepoStatus.Pending,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const transformPackages = [
|
|||||||
"formdata-polyfill",
|
"formdata-polyfill",
|
||||||
"internmap",
|
"internmap",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
"node-fetch",
|
|
||||||
"p-queue",
|
"p-queue",
|
||||||
"p-timeout",
|
"p-timeout",
|
||||||
"robust-predicates",
|
"robust-predicates",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Response } from "node-fetch";
|
|
||||||
import { Range } from "semver";
|
import { Range } from "semver";
|
||||||
|
|
||||||
import type { GithubRelease } from "../../../../src/codeql-cli/distribution/releases-api-consumer";
|
import type { GithubRelease } from "../../../../src/codeql-cli/distribution/releases-api-consumer";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { response as variantAnalysisRepoJson_response } from "../../../../src/co
|
|||||||
import { testCredentialsWithRealOctokit } from "../../../factories/authentication";
|
import { testCredentialsWithRealOctokit } from "../../../factories/authentication";
|
||||||
|
|
||||||
const mockServer = new MockGitHubApiServer();
|
const mockServer = new MockGitHubApiServer();
|
||||||
beforeAll(() => mockServer.startServer());
|
beforeAll(() => mockServer.startServer("error"));
|
||||||
afterEach(() => mockServer.unloadScenario());
|
afterEach(() => mockServer.unloadScenario());
|
||||||
afterAll(() => mockServer.stopServer());
|
afterAll(() => mockServer.stopServer());
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import {
|
|||||||
remove,
|
remove,
|
||||||
} from "fs-extra";
|
} from "fs-extra";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import * as fetchModule from "node-fetch";
|
|
||||||
import { Response } from "node-fetch";
|
|
||||||
|
|
||||||
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
|
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
|
||||||
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
||||||
@@ -49,10 +47,35 @@ import {
|
|||||||
} from "../../../../src/variant-analysis/repo-states-store";
|
} from "../../../../src/variant-analysis/repo-states-store";
|
||||||
import { permissiveFilterSortState } from "../../../unit-tests/variant-analysis-filter-sort.test";
|
import { permissiveFilterSortState } from "../../../unit-tests/variant-analysis-filter-sort.test";
|
||||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||||
|
import { setupServer } from "msw/node";
|
||||||
|
import type { RequestHandler } from "msw";
|
||||||
|
import { http } from "msw";
|
||||||
|
|
||||||
// up to 3 minutes per test
|
// up to 3 minutes per test
|
||||||
jest.setTimeout(3 * 60 * 1000);
|
jest.setTimeout(3 * 60 * 1000);
|
||||||
|
|
||||||
|
const server = setupServer();
|
||||||
|
|
||||||
|
beforeAll(() =>
|
||||||
|
server.listen({
|
||||||
|
onUnhandledRequest: "error",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
afterEach(() => server.resetHandlers());
|
||||||
|
afterAll(() => server.close());
|
||||||
|
|
||||||
|
let requests: Request[] = [];
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
server.events.on("request:start", ({ request }) => {
|
||||||
|
requests.push(request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
requests = [];
|
||||||
|
});
|
||||||
|
|
||||||
describe("Variant Analysis Manager", () => {
|
describe("Variant Analysis Manager", () => {
|
||||||
let app: App;
|
let app: App;
|
||||||
let variantAnalysisManager: VariantAnalysisManager;
|
let variantAnalysisManager: VariantAnalysisManager;
|
||||||
@@ -175,9 +198,6 @@ describe("Variant Analysis Manager", () => {
|
|||||||
let getVariantAnalysisRepoStub: jest.SpiedFunction<
|
let getVariantAnalysisRepoStub: jest.SpiedFunction<
|
||||||
typeof ghApiClient.getVariantAnalysisRepo
|
typeof ghApiClient.getVariantAnalysisRepo
|
||||||
>;
|
>;
|
||||||
let getVariantAnalysisRepoResultStub: jest.SpiedFunction<
|
|
||||||
typeof fetchModule.default
|
|
||||||
>;
|
|
||||||
|
|
||||||
let repoStatesPath: string;
|
let repoStatesPath: string;
|
||||||
|
|
||||||
@@ -186,7 +206,6 @@ describe("Variant Analysis Manager", () => {
|
|||||||
ghApiClient,
|
ghApiClient,
|
||||||
"getVariantAnalysisRepo",
|
"getVariantAnalysisRepo",
|
||||||
);
|
);
|
||||||
getVariantAnalysisRepoResultStub = jest.spyOn(fetchModule, "default");
|
|
||||||
|
|
||||||
repoStatesPath = join(
|
repoStatesPath = join(
|
||||||
storagePath,
|
storagePath,
|
||||||
@@ -197,7 +216,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
|
|
||||||
describe("when the artifact_url is missing", () => {
|
describe("when the artifact_url is missing", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const dummyRepoTask = createMockVariantAnalysisRepoTask();
|
const dummyRepoTask: VariantAnalysisRepoTask =
|
||||||
|
createMockVariantAnalysisRepoTask();
|
||||||
delete dummyRepoTask.artifact_url;
|
delete dummyRepoTask.artifact_url;
|
||||||
|
|
||||||
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
|
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
|
||||||
@@ -209,25 +229,30 @@ describe("Variant Analysis Manager", () => {
|
|||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(getVariantAnalysisRepoResultStub).not.toHaveBeenCalled();
|
expect(requests).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when the artifact_url is present", () => {
|
describe("when the artifact_url is present", () => {
|
||||||
let dummyRepoTask: VariantAnalysisRepoTask;
|
let dummyRepoTask: ReturnType<typeof createMockVariantAnalysisRepoTask>;
|
||||||
|
let handlers: RequestHandler[];
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
dummyRepoTask = createMockVariantAnalysisRepoTask();
|
dummyRepoTask = createMockVariantAnalysisRepoTask();
|
||||||
|
|
||||||
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
|
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
|
||||||
|
|
||||||
const sourceFilePath = join(
|
handlers = [
|
||||||
__dirname,
|
http.get(dummyRepoTask.artifact_url, async () => {
|
||||||
"data/variant-analysis-results.zip",
|
const sourceFilePath = join(
|
||||||
);
|
__dirname,
|
||||||
const fileContents = await readFile(sourceFilePath);
|
"data/variant-analysis-results.zip",
|
||||||
const response = new Response(fileContents);
|
);
|
||||||
getVariantAnalysisRepoResultStub.mockResolvedValue(response);
|
const fileContents = await readFile(sourceFilePath);
|
||||||
|
return new Response(fileContents);
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
server.resetHandlers(...handlers);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fetch a repo task", async () => {
|
it("should fetch a repo task", async () => {
|
||||||
@@ -245,7 +270,7 @@ describe("Variant Analysis Manager", () => {
|
|||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(getVariantAnalysisRepoResultStub).toHaveBeenCalled();
|
expect(requests).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should skip the download if the repository has already been downloaded", async () => {
|
it("should skip the download if the repository has already been downloaded", async () => {
|
||||||
@@ -281,8 +306,10 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not write the repo state when the download fails", async () => {
|
it("should not write the repo state when the download fails", async () => {
|
||||||
getVariantAnalysisRepoResultStub.mockRejectedValue(
|
server.resetHandlers(
|
||||||
new Error("Failed to download"),
|
http.get(dummyRepoTask.artifact_url, async () => {
|
||||||
|
return new Response(JSON.stringify({}), { status: 500 });
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -329,8 +356,10 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should have a failed repo state when the download fails", async () => {
|
it("should have a failed repo state when the download fails", async () => {
|
||||||
getVariantAnalysisRepoResultStub.mockRejectedValueOnce(
|
server.resetHandlers(
|
||||||
new Error("Failed to download"),
|
http.get(dummyRepoTask.artifact_url, async () => {
|
||||||
|
return new Response(JSON.stringify({}), { status: 500 });
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -342,6 +371,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
|
|
||||||
await expect(pathExists(repoStatesPath)).resolves.toBe(false);
|
await expect(pathExists(repoStatesPath)).resolves.toBe(false);
|
||||||
|
|
||||||
|
server.resetHandlers(...handlers);
|
||||||
|
|
||||||
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
|
await variantAnalysisManager.autoDownloadVariantAnalysisResult(
|
||||||
scannedRepos[1],
|
scannedRepos[1],
|
||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { extLogger } from "../../../../src/common/logging/vscode";
|
import { extLogger } from "../../../../src/common/logging/vscode";
|
||||||
import { readFile, pathExists, remove, outputJson, readJson } from "fs-extra";
|
import { outputJson, pathExists, readFile, readJson, remove } from "fs-extra";
|
||||||
import { join, resolve } from "path";
|
import { join, resolve } from "path";
|
||||||
import { Readable } from "stream";
|
|
||||||
import * as fetchModule from "node-fetch";
|
|
||||||
import type { RequestInfo, RequestInit } from "node-fetch";
|
|
||||||
import { Response } from "node-fetch";
|
|
||||||
|
|
||||||
import { VariantAnalysisResultsManager } from "../../../../src/variant-analysis/variant-analysis-results-manager";
|
import { VariantAnalysisResultsManager } from "../../../../src/variant-analysis/variant-analysis-results-manager";
|
||||||
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
||||||
@@ -17,9 +13,21 @@ import type {
|
|||||||
} from "../../../../src/variant-analysis/shared/variant-analysis";
|
} from "../../../../src/variant-analysis/shared/variant-analysis";
|
||||||
import { mockedObject } from "../../utils/mocking.helpers";
|
import { mockedObject } from "../../utils/mocking.helpers";
|
||||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||||
|
import { setupServer } from "msw/node";
|
||||||
|
import { http } from "msw";
|
||||||
|
|
||||||
jest.setTimeout(10_000);
|
jest.setTimeout(10_000);
|
||||||
|
|
||||||
|
const server = setupServer();
|
||||||
|
|
||||||
|
beforeAll(() =>
|
||||||
|
server.listen({
|
||||||
|
onUnhandledRequest: "error",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
afterEach(() => server.resetHandlers());
|
||||||
|
afterAll(() => server.close());
|
||||||
|
|
||||||
describe(VariantAnalysisResultsManager.name, () => {
|
describe(VariantAnalysisResultsManager.name, () => {
|
||||||
let variantAnalysisId: number;
|
let variantAnalysisId: number;
|
||||||
let variantAnalysisResultsManager: VariantAnalysisResultsManager;
|
let variantAnalysisResultsManager: VariantAnalysisResultsManager;
|
||||||
@@ -37,7 +45,9 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("download", () => {
|
describe("download", () => {
|
||||||
let dummyRepoTask: VariantAnalysisRepositoryTask;
|
let dummyRepoTask: ReturnType<
|
||||||
|
typeof createMockVariantAnalysisRepositoryTask
|
||||||
|
>;
|
||||||
let variantAnalysisStoragePath: string;
|
let variantAnalysisStoragePath: string;
|
||||||
let repoTaskStorageDirectory: string;
|
let repoTaskStorageDirectory: string;
|
||||||
|
|
||||||
@@ -76,7 +86,8 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
|
|
||||||
describe("when the artifact_url is missing", () => {
|
describe("when the artifact_url is missing", () => {
|
||||||
it("should not try to download the result", async () => {
|
it("should not try to download the result", async () => {
|
||||||
const dummyRepoTask = createMockVariantAnalysisRepositoryTask();
|
const dummyRepoTask: VariantAnalysisRepositoryTask =
|
||||||
|
createMockVariantAnalysisRepositoryTask();
|
||||||
delete dummyRepoTask.artifactUrl;
|
delete dummyRepoTask.artifactUrl;
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -91,10 +102,8 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when the artifact_url is present", () => {
|
describe("when the artifact_url is present", () => {
|
||||||
let getVariantAnalysisRepoResultStub: jest.SpiedFunction<
|
|
||||||
typeof fetchModule.default
|
|
||||||
>;
|
|
||||||
let fileContents: Buffer;
|
let fileContents: Buffer;
|
||||||
|
let artifactRequest: Request | undefined;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const sourceFilePath = join(
|
const sourceFilePath = join(
|
||||||
@@ -103,14 +112,19 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
);
|
);
|
||||||
fileContents = await readFile(sourceFilePath);
|
fileContents = await readFile(sourceFilePath);
|
||||||
|
|
||||||
getVariantAnalysisRepoResultStub = jest
|
artifactRequest = undefined;
|
||||||
.spyOn(fetchModule, "default")
|
|
||||||
.mockImplementation((url: URL | RequestInfo, _init?: RequestInit) => {
|
server.resetHandlers(
|
||||||
if (url === dummyRepoTask.artifactUrl) {
|
http.get(dummyRepoTask.artifactUrl, ({ request }) => {
|
||||||
return Promise.resolve(new Response(fileContents));
|
if (artifactRequest) {
|
||||||
|
throw new Error("Unexpected artifact request");
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error("Unexpected artifact URL"));
|
|
||||||
});
|
artifactRequest = request;
|
||||||
|
|
||||||
|
return new Response(fileContents);
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call the API to download the results", async () => {
|
it("should call the API to download the results", async () => {
|
||||||
@@ -121,7 +135,7 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
() => Promise.resolve(),
|
() => Promise.resolve(),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(getVariantAnalysisRepoResultStub).toHaveBeenCalledTimes(1);
|
expect(artifactRequest).not.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should save the results zip file to disk", async () => {
|
it("should save the results zip file to disk", async () => {
|
||||||
@@ -151,28 +165,29 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should report download progress", async () => {
|
it("should report download progress", async () => {
|
||||||
// This generates a "fake" stream which "downloads" the file in 5 chunks,
|
server.resetHandlers(
|
||||||
// rather than in 1 chunk. This is used for testing that we actually get
|
http.get(dummyRepoTask.artifactUrl, () => {
|
||||||
// multiple progress reports.
|
// This generates a "fake" stream which "downloads" the file in 5 chunks,
|
||||||
async function* generateInParts() {
|
// rather than in 1 chunk. This is used for testing that we actually get
|
||||||
const partLength = fileContents.length / 5;
|
// multiple progress reports.
|
||||||
for (let i = 0; i < 5; i++) {
|
const stream = new ReadableStream({
|
||||||
yield fileContents.subarray(i * partLength, (i + 1) * partLength);
|
start(controller) {
|
||||||
}
|
const partLength = fileContents.length / 5;
|
||||||
}
|
for (let i = 0; i < 5; i++) {
|
||||||
|
controller.enqueue(
|
||||||
|
fileContents.subarray(i * partLength, (i + 1) * partLength),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
getVariantAnalysisRepoResultStub.mockImplementation(
|
return new Response(stream, {
|
||||||
(url: URL | RequestInfo, _init?: RequestInit) => {
|
headers: {
|
||||||
if (url === dummyRepoTask.artifactUrl) {
|
"Content-Length": fileContents.length.toString(),
|
||||||
const response = new Response(Readable.from(generateInParts()));
|
},
|
||||||
response.headers.set(
|
});
|
||||||
"Content-Length",
|
}),
|
||||||
fileContents.length.toString(),
|
|
||||||
);
|
|
||||||
return Promise.resolve(response);
|
|
||||||
}
|
|
||||||
return Promise.reject(new Error("Unexpected artifact URL"));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const downloadPercentageChanged = jest
|
const downloadPercentageChanged = jest
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
import { createWriteStream, existsSync, mkdirpSync } from "fs-extra";
|
import { createWriteStream, existsSync, mkdirpSync } from "fs-extra";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
import { DB_URL, dbLoc, testprojLoc } from "../global.helper";
|
import { DB_URL, dbLoc, testprojLoc } from "../global.helper";
|
||||||
import fetch from "node-fetch";
|
|
||||||
import { renameSync } from "fs";
|
import { renameSync } from "fs";
|
||||||
import { unzipToDirectoryConcurrently } from "../../../src/common/unzip-concurrently";
|
import { unzipToDirectoryConcurrently } from "../../../src/common/unzip-concurrently";
|
||||||
import { platform } from "os";
|
import { platform } from "os";
|
||||||
@@ -21,22 +20,42 @@ beforeAll(async () => {
|
|||||||
if (!existsSync(dbLoc)) {
|
if (!existsSync(dbLoc)) {
|
||||||
console.log(`Downloading test database to ${dbLoc}`);
|
console.log(`Downloading test database to ${dbLoc}`);
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
const response = await fetch(DB_URL);
|
||||||
return fetch(DB_URL).then((response) => {
|
if (!response.body) {
|
||||||
if (!response.body) {
|
throw new Error("No response body found");
|
||||||
throw new Error("No response body found");
|
}
|
||||||
}
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to download test database: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const dest = createWriteStream(dbLoc);
|
const dest = createWriteStream(dbLoc);
|
||||||
response.body.pipe(dest);
|
|
||||||
|
|
||||||
response.body.on("error", reject);
|
const reader = response.body.getReader();
|
||||||
dest.on("error", reject);
|
for (;;) {
|
||||||
dest.on("close", () => {
|
const { done, value } = await reader.read();
|
||||||
resolve(dbLoc);
|
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
dest.write(value, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) =>
|
||||||
|
dest.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unzip the database from dbLoc to testprojLoc
|
// unzip the database from dbLoc to testprojLoc
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { createVSCodeCommandManager } from "../../../../src/common/vscode/comman
|
|||||||
import type { AllCommands } from "../../../../src/common/commands";
|
import type { AllCommands } from "../../../../src/common/commands";
|
||||||
|
|
||||||
const mockServer = new MockGitHubApiServer();
|
const mockServer = new MockGitHubApiServer();
|
||||||
beforeAll(() => mockServer.startServer());
|
beforeAll(() => mockServer.startServer("bypass"));
|
||||||
afterEach(() => mockServer.unloadScenario());
|
afterEach(() => mockServer.unloadScenario());
|
||||||
afterAll(() => mockServer.stopServer());
|
afterAll(() => mockServer.stopServer());
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ async function showQlDocument(name: string): Promise<TextDocument> {
|
|||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("Variant Analysis Submission Integration", () => {
|
// MSW can't intercept fetch requests made in VS Code, so we are skipping these tests for now
|
||||||
|
describe.skip("Variant Analysis Submission Integration", () => {
|
||||||
const commandManager = createVSCodeCommandManager<AllCommands>();
|
const commandManager = createVSCodeCommandManager<AllCommands>();
|
||||||
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||||
let executeCommandSpy: jest.SpiedFunction<typeof commands.executeCommand>;
|
let executeCommandSpy: jest.SpiedFunction<typeof commands.executeCommand>;
|
||||||
|
|||||||
@@ -3,19 +3,15 @@ import { reportStreamProgress } from "../../../../../src/common/vscode/progress"
|
|||||||
describe("helpers", () => {
|
describe("helpers", () => {
|
||||||
it("should report stream progress", () => {
|
it("should report stream progress", () => {
|
||||||
const progressSpy = jest.fn();
|
const progressSpy = jest.fn();
|
||||||
const mockReadable = {
|
|
||||||
on: jest.fn(),
|
|
||||||
};
|
|
||||||
const max = 1024 * 1024 * 4;
|
const max = 1024 * 1024 * 4;
|
||||||
const firstStep = 1024 * 1024 + 1024 * 600;
|
const firstStep = 1024 * 1024 + 1024 * 600;
|
||||||
const secondStep = 1024 * 1024 * 2;
|
const secondStep = 1024 * 1024 * 2;
|
||||||
|
|
||||||
(reportStreamProgress as any)(mockReadable, "My prefix", max, progressSpy);
|
const reportProgress = reportStreamProgress("My prefix", max, progressSpy);
|
||||||
|
|
||||||
// now pretend that we have received some messages
|
// now pretend that we have received some messages
|
||||||
const listener = mockReadable.on.mock.calls[0][1] as (data: any) => void;
|
reportProgress(firstStep);
|
||||||
listener({ length: firstStep });
|
reportProgress(secondStep);
|
||||||
listener({ length: secondStep });
|
|
||||||
|
|
||||||
expect(progressSpy).toHaveBeenCalledTimes(3);
|
expect(progressSpy).toHaveBeenCalledTimes(3);
|
||||||
expect(progressSpy).toHaveBeenCalledWith({
|
expect(progressSpy).toHaveBeenCalledWith({
|
||||||
@@ -37,18 +33,14 @@ describe("helpers", () => {
|
|||||||
|
|
||||||
it("should report stream progress when total bytes unknown", () => {
|
it("should report stream progress when total bytes unknown", () => {
|
||||||
const progressSpy = jest.fn();
|
const progressSpy = jest.fn();
|
||||||
const mockReadable = {
|
const reportProgress = reportStreamProgress(
|
||||||
on: jest.fn(),
|
|
||||||
};
|
|
||||||
(reportStreamProgress as any)(
|
|
||||||
mockReadable,
|
|
||||||
"My prefix",
|
"My prefix",
|
||||||
undefined,
|
undefined,
|
||||||
progressSpy,
|
progressSpy,
|
||||||
);
|
);
|
||||||
|
|
||||||
// There are no listeners registered to this readable
|
// It should not report progress when calling the callback
|
||||||
expect(mockReadable.on).not.toHaveBeenCalled();
|
reportProgress(100);
|
||||||
|
|
||||||
expect(progressSpy).toHaveBeenCalledTimes(1);
|
expect(progressSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(progressSpy).toHaveBeenCalledWith({
|
expect(progressSpy).toHaveBeenCalledWith({
|
||||||
|
|||||||
Reference in New Issue
Block a user