Skip to content

Commit

Permalink
test: Add test for "needCheckForUpdates"
Browse files Browse the repository at this point in the history
#1144

This commit adds a test for `needCheckForUpdates`.

For adding unit tests, this commit abstract away the logic that needs
access to vscode APIs, becuase we need to run an integration test for
accessing vscode APIs.
see: microsoft/vscode-wordcount#5 (comment)
microsoft/vscode#82471 (comment)
  • Loading branch information
tanishiking committed Sep 2, 2022
1 parent dacf72c commit da8ac81
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 53 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ module.exports = {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ varsIgnorePattern: "_" },
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-non-null-assertion": "error",
"guard-for-in": "error",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@
"compile": "tsc -p ./",
"clean": "rimraf out/",
"watch": "tsc -watch -p ./",
"test-unit": "ts-mocha src/*.test.ts",
"test": "ts-mocha src/test/unit/*.test.ts",
"test-extension": "rimraf out/ && tsc -p ./ && node out/test/extension/runTest.js",
"build": "yarn clean && vsce package --yarn",
Expand All @@ -994,6 +995,7 @@
"@types/node": "18.7.14",
"@types/remarkable": "^2.0.3",
"@types/semver": "^7.3.12",
"@types/sinon": "^10.0.13",
"@types/vscode": "1.59.0",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
Expand All @@ -1007,6 +1009,7 @@
"ovsx": "0.5.1",
"prettier": "2.7.1",
"rimraf": "^3.0.2",
"sinon": "^14.0.0",
"ts-mocha": "^10.0.0",
"typescript": "4.8.2",
"vsce": "2.11.0"
Expand Down
4 changes: 4 additions & 0 deletions src/ConfigurationTarget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ConfigurationTarget {
Global,
Workspace,
}
83 changes: 32 additions & 51 deletions src/getServerVersion.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import {
commands,
ConfigurationTarget,
ExtensionContext,
window,
WorkspaceConfiguration,
ConfigurationTarget as VConfigurationTarget,
} from "vscode";
import * as metalsLanguageClient from "metals-languageclient";
import * as workbenchCommands from "./workbenchCommands";
import http from "https";
import { getConfigValue } from "./util";
import { DefaultCheckForUpdateRepo } from "./repository/CheckForUpdateRepo";
import { needCheckForUpdates } from "./service/checkForUpdate";
import { ConfigurationTarget } from "./ConfigurationTarget";

const serverVersionSection = "serverVersion";
const suggestLatestUpgrade = "suggestLatestUpgrade";

const currentVersionKey = "currentVersion";
const lastUpdatedAtKey = "lastUpdatedAt";

export function getServerVersion(
config: WorkspaceConfiguration,
context: ExtensionContext
Expand All @@ -40,12 +40,15 @@ async function validateCurrentVersion(
suggestLatestUpgrade
);

const checkForUpdateRepo = new DefaultCheckForUpdateRepo(context);

const checkForUpdate = async () => {
if (suggestUpgradeSetting?.value) {
return needCheckForUpdates(
serverVersion,
suggestUpgradeSetting.target,
context
todayString(),
fromVSCode(suggestUpgradeSetting.target),
checkForUpdateRepo
);
} else {
return false;
Expand All @@ -68,13 +71,17 @@ async function validateCurrentVersion(
nextVersion,
suggestUpgradeSetting.target
);
saveVersionDate(nextVersion, suggestUpgradeSetting.target, context);
checkForUpdateRepo.saveLastUpdated(
nextVersion,
todayString(),
fromVSCode(suggestUpgradeSetting.target)
);
} else if (result == ignoreChoice) {
// extend the current version expiration date
saveVersionDate(
checkForUpdateRepo.saveLastUpdated(
serverVersion,
suggestUpgradeSetting.target,
context
todayString(),
fromVSCode(suggestUpgradeSetting.target)
);
}
});
Expand All @@ -101,46 +108,6 @@ async function fetchLatest(): Promise<string> {
return sorted[sorted.length - 1];
}

/**
* The logic is the following:
* - if version was set more than a day ago - update is needed
* - if version is seen in a first time (user changed version in config by it self) - the update will be delayed for a day
*/
async function needCheckForUpdates(
currentVersion: string,
target: ConfigurationTarget,
context: ExtensionContext
): Promise<boolean> {
const state =
target === ConfigurationTarget.Global
? context.globalState
: context.workspaceState;
const prevVersion = state.get<string>(currentVersionKey);
const lastUpdated = state.get<string>(lastUpdatedAtKey);

const today = todayString();
if (prevVersion !== currentVersion) {
saveVersionDate(currentVersion, target, context);
return false;
} else {
return lastUpdated !== today;
}
}

function saveVersionDate(
version: string,
target: ConfigurationTarget,
context: ExtensionContext
): void {
const state =
target === ConfigurationTarget.Global
? context.globalState
: context.workspaceState;

state.update(currentVersionKey, version);
state.update(lastUpdatedAtKey, todayString());
}

function warnIfIsOutdated(config: WorkspaceConfiguration): void {
metalsLanguageClient.checkServerVersion({
config,
Expand Down Expand Up @@ -175,10 +142,24 @@ function warnIfIsOutdated(config: WorkspaceConfiguration): void {
});
}

function todayString(): string {
/**
* @returns YYYY-MM-DD in a local date
*/
export function todayString(): string {
const date = new Date();
const year = date.getFullYear().toString();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
return [year, month, day].join("-");
}

function fromVSCode(target: VConfigurationTarget): ConfigurationTarget {
switch (target) {
case VConfigurationTarget.Global:
return ConfigurationTarget.Global;
case VConfigurationTarget.Workspace:
return ConfigurationTarget.Workspace;
case VConfigurationTarget.WorkspaceFolder:
return ConfigurationTarget.Workspace;
}
}
50 changes: 50 additions & 0 deletions src/repository/CheckForUpdateRepo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ExtensionContext, Memento } from "vscode";
import { ConfigurationTarget } from "../ConfigurationTarget";

type LastUpdated = {
prevVersion?: string;
lastUpdatedAt?: string;
};

export interface CheckForUpdateRepo {
getLastUpdated(target: ConfigurationTarget): LastUpdated;
saveLastUpdated(
serverVersion: string,
lastUpdatedAt: string,
target: ConfigurationTarget
): void;
}

export class DefaultCheckForUpdateRepo implements CheckForUpdateRepo {
constructor(private context: ExtensionContext) {}

private CurrentVersionKey = "currentVersion";
private LastUpdatedAtKey = "lastUpdatedAt";

getLastUpdated(target: ConfigurationTarget): LastUpdated {
const state = this.storage(target);
const prevVersion = state.get<string>(this.CurrentVersionKey);
const lastUpdatedAt = state.get<string>(this.LastUpdatedAtKey);
return {
prevVersion,
lastUpdatedAt,
};
}

saveLastUpdated(
serverVersion: string,
lastUpdatedAt: string,
target: ConfigurationTarget
): void {
const state = this.storage(target);
state.update(this.CurrentVersionKey, serverVersion);
state.update(this.LastUpdatedAtKey, lastUpdatedAt);
return;
}

private storage(target: ConfigurationTarget): Memento {
return target === ConfigurationTarget.Global
? this.context.globalState
: this.context.workspaceState;
}
}
23 changes: 23 additions & 0 deletions src/service/checkForUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ConfigurationTarget } from "../ConfigurationTarget";
import { CheckForUpdateRepo } from "../repository/CheckForUpdateRepo";

/**
* The logic is the following:
* - if version was set more than a day ago - update is needed
* - if version is seen for the first time (user changed version in config by it self) - the update will be delayed for a day
*/
export async function needCheckForUpdates(
currentVersion: string,
today: string,
target: ConfigurationTarget,
repo: CheckForUpdateRepo
): Promise<boolean> {
const { prevVersion, lastUpdatedAt } = repo.getLastUpdated(target);

if (prevVersion !== currentVersion) {
repo.saveLastUpdated(currentVersion, today, target);
return false;
} else {
return lastUpdatedAt !== today;
}
}
89 changes: 89 additions & 0 deletions src/test/unit/checkForUpdate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import assert from "assert";
import { ConfigurationTarget } from "../../ConfigurationTarget";
import { needCheckForUpdates } from "../../service/checkForUpdate";
import { CheckForUpdateRepo } from "../../repository/CheckForUpdateRepo";
import sinon from "sinon";

class MockRepo implements CheckForUpdateRepo {
constructor(private prevVersion?: string, private lastUpdatedAt?: string) {}
getLastUpdated(_target: ConfigurationTarget): {
prevVersion?: string | undefined;
lastUpdatedAt?: string | undefined;
} {
return {
prevVersion: this.prevVersion,
lastUpdatedAt: this.lastUpdatedAt,
};
}
saveLastUpdated(
_serverVersion: string,
_lastUpdatedAt: string,
_target: ConfigurationTarget
): void {
return;
}
}

describe("needCheckForUpdates", () => {
it("should false if nothing has saved / save current versions", async () => {
const currentVersion = "0.11.8";
const today = "2022-01-01";
const repo = new MockRepo(undefined, undefined);
const spy = sinon.spy(repo, "saveLastUpdated");
const actual = await needCheckForUpdates(
currentVersion,
today,
ConfigurationTarget.Global,
repo
);
assert(actual === false);
assert(spy.calledWith(currentVersion, today, ConfigurationTarget.Global));
});

it("should false if currentVersion was seen for the first time / save current versions", async () => {
const prevVersion = "0.11.8";
const currentVersion = "0.11.9";
const today = "2022-01-01";
const repo = new MockRepo(prevVersion, today);
const spy = sinon.spy(repo, "saveLastUpdated");
const actual = await needCheckForUpdates(
currentVersion,
today,
ConfigurationTarget.Global,
repo
);
assert(actual === false);
assert(spy.calledWith(currentVersion, today, ConfigurationTarget.Global));
});

it("should false if currentVersion is set today", async () => {
const currentVersion = "0.11.8";
const today = "2022-01-01";
const repo = new MockRepo(currentVersion, today);
const spy = sinon.spy(repo, "saveLastUpdated");
const actual = await needCheckForUpdates(
currentVersion,
today,
ConfigurationTarget.Global,
repo
);
assert(actual === false);
assert(spy.notCalled);
});

it("should true if currentVersion is set more than a day ago", async () => {
const currentVersion = "0.11.8";
const lastUpdatedAt = "2022-01-01";
const today = "2022-01-02";
const repo = new MockRepo(currentVersion, lastUpdatedAt);
const spy = sinon.spy(repo, "saveLastUpdated");
const actual = await needCheckForUpdates(
currentVersion,
today,
ConfigurationTarget.Global,
repo
);
assert(actual === true);
assert(spy.notCalled);
});
});
Loading

0 comments on commit da8ac81

Please sign in to comment.