Add tests for distribution updates

This commit is contained in:
Koen Vlaswinkel
2024-10-15 14:40:56 +02:00
parent 4b3a4f9d11
commit 87be96e07b
2 changed files with 371 additions and 7 deletions

View File

@@ -67,7 +67,7 @@ const NIGHTLY_DISTRIBUTION_REPOSITORY_NWO = "dsp-testing/codeql-cli-nightlies";
*/
export const DEFAULT_DISTRIBUTION_VERSION_RANGE: Range = new Range("2.x");
interface DistributionState {
export interface DistributionState {
folderIndex: number;
release: Release | null;
}
@@ -329,10 +329,11 @@ class ExtensionSpecificDistributionManager {
// If the file doesn't exist, that just means we need to create it
this.distributionState = {
folderIndex: this.extensionContext.globalState.get(
folderIndex:
this.extensionContext.globalState.get(
"distributionFolderIndex",
0,
),
) ?? 0,
release: (this.extensionContext.globalState.get(
"distributionRelease",
) ?? null) as Release | null,
@@ -647,7 +648,7 @@ class ExtensionSpecificDistributionManager {
await this.updateState((oldState) => {
return {
...oldState,
folderIndex: oldState.folderIndex + 1,
folderIndex: (oldState.folderIndex ?? 0) + 1,
};
});
}

View File

@@ -1,12 +1,21 @@
import * as log from "../../../../src/common/logging/notifications";
import { extLogger } from "../../../../src/common/logging/vscode";
import { writeFile } from "fs-extra";
import {
outputFile,
outputJson,
readFile,
readJson,
writeFile,
} from "fs-extra";
import { join } from "path";
import * as os from "os";
import type { DirectoryResult } from "tmp-promise";
import { dir } from "tmp-promise";
import type { DistributionState } from "../../../../src/codeql-cli/distribution";
import {
DEFAULT_DISTRIBUTION_VERSION_RANGE,
DistributionManager,
DistributionUpdateCheckResultKind,
getExecutableFromDirectory,
} from "../../../../src/codeql-cli/distribution";
import type {
@@ -14,6 +23,19 @@ import type {
showAndLogWarningMessage,
} from "../../../../src/common/logging";
import { createMockLogger } from "../../../__mocks__/loggerMock";
import { mockedObject } from "../../../mocked-object";
import type { DistributionConfig } from "../../../../src/config";
import type { ExtensionContext } from "vscode";
import { Uri } from "vscode";
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";
import {
codeQlLauncherName,
getRequiredAssetName,
} from "../../../../src/common/distribution";
import type { GithubRelease } from "../../../../src/codeql-cli/distribution/releases-api-consumer";
import type { Release } from "../../../../src/codeql-cli/distribution/release";
import { zip } from "zip-a-folder";
jest.mock("os", () => {
const original = jest.requireActual("os");
@@ -155,3 +177,344 @@ describe("Launcher path", () => {
expect(errorSpy).toHaveBeenCalledTimes(1);
});
});
describe("Distribution updates", () => {
const server = setupServer();
beforeAll(() =>
server.listen({
onUnhandledRequest: "error",
}),
);
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
let manager: DistributionManager;
let globalStorageDirectory: DirectoryResult;
beforeEach(async () => {
globalStorageDirectory = await dir({
unsafeCleanup: true,
});
manager = new DistributionManager(
mockedObject<DistributionConfig>({
customCodeQlPath: undefined,
channel: "stable",
includePrerelease: false,
personalAccessToken: undefined,
downloadTimeout: 100,
onDidChangeConfiguration: () => {},
}),
DEFAULT_DISTRIBUTION_VERSION_RANGE,
mockedObject<ExtensionContext>({
globalState: {
get: () => {},
update: () => {},
},
globalStorageUri: Uri.file(globalStorageDirectory.path),
}),
createMockLogger(),
);
await manager.initialize();
});
afterEach(async () => {
await globalStorageDirectory.cleanup();
});
it("should have an empty distribution.json file after initialization", async () => {
expect(
await readJson(join(globalStorageDirectory.path, "distribution.json")),
).toEqual({
folderIndex: 0,
release: null,
} satisfies DistributionState);
});
describe("checkForUpdatesToDistribution", () => {
beforeEach(() => {
server.resetHandlers(
http.get(
"https://api.github.com/repos/github/codeql-cli-binaries/releases",
async () => {
return HttpResponse.json([
{
id: 1335,
name: "v2.2.0",
tag_name: "v2.2.0",
created_at: "2024-02-02T02:02:02Z",
prerelease: false,
assets: [
{
id: 783,
name: getRequiredAssetName(),
size: 2378,
},
],
},
{
id: 1,
name: "v2.1.0",
tag_name: "v2.1.0",
created_at: "2022-02-02T02:02:02Z",
prerelease: false,
assets: [
{
id: 1,
name: getRequiredAssetName(),
size: 100,
},
],
},
] satisfies GithubRelease[]);
},
),
);
});
it("should have an update when no distribution is installed", async () => {
expect(
await manager.checkForUpdatesToExtensionManagedDistribution(0),
).toEqual({
kind: DistributionUpdateCheckResultKind.UpdateAvailable,
updatedRelease: {
id: 1335,
name: "v2.2.0",
createdAt: "2024-02-02T02:02:02Z",
assets: [
{
id: 783,
name: getRequiredAssetName(),
size: 2378,
},
],
},
} satisfies Awaited<
ReturnType<typeof manager.checkForUpdatesToExtensionManagedDistribution>
>);
});
it("should not have an update when the latest distribution is installed", async () => {
await outputJson(join(globalStorageDirectory.path, "distribution.json"), {
folderIndex: 1,
release: {
id: 1335,
name: "v2.2.0",
createdAt: "2024-02-02T02:02:02Z",
assets: [
{
id: 783,
name: getRequiredAssetName(),
size: 2378,
},
],
},
} satisfies DistributionState);
await outputFile(
join(
globalStorageDirectory.path,
"distribution1",
"codeql",
codeQlLauncherName(),
),
"",
);
// Re-initialize manager to read the state from the file
await manager.initialize();
expect(
await manager.checkForUpdatesToExtensionManagedDistribution(0),
).toEqual({
kind: DistributionUpdateCheckResultKind.AlreadyUpToDate,
} satisfies Awaited<
ReturnType<typeof manager.checkForUpdatesToExtensionManagedDistribution>
>);
});
it("should have an update when an older distribution is installed", async () => {
await outputJson(join(globalStorageDirectory.path, "distribution.json"), {
folderIndex: 1,
release: {
id: 1,
name: "v2.1.0",
createdAt: "2022-02-02T02:02:02Z",
assets: [
{
id: 1,
name: getRequiredAssetName(),
size: 100,
},
],
},
} satisfies DistributionState);
await outputFile(
join(
globalStorageDirectory.path,
"distribution1",
"codeql",
codeQlLauncherName(),
),
"",
);
// Re-initialize manager to read the state from the file
await manager.initialize();
expect(
await manager.checkForUpdatesToExtensionManagedDistribution(0),
).toEqual({
kind: DistributionUpdateCheckResultKind.UpdateAvailable,
updatedRelease: {
id: 1335,
name: "v2.2.0",
createdAt: "2024-02-02T02:02:02Z",
assets: [
{
id: 783,
name: getRequiredAssetName(),
size: 2378,
},
],
},
} satisfies Awaited<
ReturnType<typeof manager.checkForUpdatesToExtensionManagedDistribution>
>);
});
});
describe("installExtensionManagedDistributionRelease", () => {
const release: Release = {
id: 1335,
name: "v2.2.0",
createdAt: "2024-02-02T02:02:02Z",
assets: [
{
id: 783,
name: getRequiredAssetName(),
size: 2378,
},
],
};
let codeqlReleaseZipTempDir: DirectoryResult;
let codeqlReleaseZipPath: string;
beforeAll(async () => {
codeqlReleaseZipTempDir = await dir({
unsafeCleanup: true,
});
await outputFile(
join(
codeqlReleaseZipTempDir.path,
"distribution",
"codeql",
codeQlLauncherName(),
),
"launcher!",
);
codeqlReleaseZipPath = join(codeqlReleaseZipTempDir.path, "codeql.zip");
await zip(
join(codeqlReleaseZipTempDir.path, "distribution"),
codeqlReleaseZipPath,
);
server.resetHandlers(
http.get(
"https://api.github.com/repos/github/codeql-cli-binaries/releases/assets/783",
async () => {
const file = await readFile(codeqlReleaseZipPath);
return HttpResponse.arrayBuffer(file, {
headers: {
"Content-Type": "application/octet-stream",
},
});
},
),
);
});
afterAll(async () => {
await codeqlReleaseZipTempDir?.cleanup();
});
it("installs a distribution when no distribution exists", async () => {
await manager.installExtensionManagedDistributionRelease(release);
expect(
await readJson(join(globalStorageDirectory.path, "distribution.json")),
).toEqual({
folderIndex: 1,
release,
} satisfies DistributionState);
expect(
await readFile(
join(
globalStorageDirectory.path,
"distribution1",
"codeql",
codeQlLauncherName(),
),
"utf-8",
),
).toEqual("launcher!");
});
it("installs a distribution when a distribution already exists", async () => {
await outputJson(join(globalStorageDirectory.path, "distribution.json"), {
folderIndex: 78,
release: {
id: 1,
name: "v2.1.0",
createdAt: "2022-02-02T02:02:02Z",
assets: [
{
id: 1,
name: getRequiredAssetName(),
size: 100,
},
],
},
} satisfies DistributionState);
await outputFile(
join(
globalStorageDirectory.path,
"distribution78",
"codeql",
codeQlLauncherName(),
),
"",
);
// Re-initialize manager to read the state from the file
await manager.initialize();
await manager.installExtensionManagedDistributionRelease(release);
expect(
await readJson(join(globalStorageDirectory.path, "distribution.json")),
).toEqual({
folderIndex: 79,
release,
} satisfies DistributionState);
expect(
await readFile(
join(
globalStorageDirectory.path,
"distribution79",
"codeql",
codeQlLauncherName(),
),
"utf-8",
),
).toEqual("launcher!");
});
});
});