Skip to content

Commit

Permalink
Add unit tests for queryUpdateResponder
Browse files Browse the repository at this point in the history
Signed-off-by: Adam Pickering <[email protected]>
  • Loading branch information
adamkpickering committed Apr 19, 2023
1 parent f412ad3 commit 58deb61
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 73 deletions.
11 changes: 6 additions & 5 deletions pkg/rancher-desktop/components/UpdateStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,25 @@
<span v-else></span>
</template>
</card>
<card v-else-if="unsupportedUpgradeAvailable" ref="osUpdateRequired" :show-highlight-border="false">
<card v-else-if="unsupportedUpgradeAvailable" :show-highlight-border="false">
<template #title>
<div class="type-title">
<h3>Operating System Upgrade Required</h3>
<h3>Latest Version Not Supported</h3>
</div>
</template>
<template #body>
<p>
A newer version of Rancher Desktop is available, but not supported on the current version
of your operating system. Please upgrade your operating system to run it.
A newer version of Rancher Desktop is available, but not supported on your system.
</p>
<br>
<p>
For more information please see
<a href="https://docs.rancherdesktop.io/getting-started/installation">the installation documentation</a>.
</p>
</template>
<template #actions><div></div></template>
<template #actions>
<div></div>
</template>
</card>
</div>
</template>
Expand Down
12 changes: 6 additions & 6 deletions pkg/rancher-desktop/components/__tests__/UpdateStatus.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ describe('UpdateStatus.vue', () => {
available: true,
downloaded: false,
info: {
version: 'v1.2.3',
files: [],
path: '',
sha512: '',
releaseDate: '',
nextUpdateTime: 12345,
version: 'v1.2.3',
files: [],
path: '',
sha512: '',
releaseDate: '',
nextUpdateTime: 12345,
unsupportedUpgradeAvailable: false,
},
progress: {
Expand Down
130 changes: 70 additions & 60 deletions pkg/rancher-desktop/main/update/LonghornProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,26 @@ export interface LonghornUpdateInfo extends UpdateInfo {
* responder service.
*/
interface LonghornUpgraderResponse {
versions: {
Name: string;
ReleaseDate: Date;
Supported: boolean | undefined;
Tags: string[];
}[];
versions: UpgradeResponderVersion[];
/**
* The number of minutes before the next upgrade check should be performed.
*/
requestIntervalInMinutes: number;
}

type UpgradeResponderVersion = {
Name: string;
ReleaseDate: Date;
Supported?: boolean;
Tags: string[];
};

type UpgradeResponderQueryResult = {
latest: UpgradeResponderVersion;
requestIntervalInMinutes: number,
unsupportedUpgradeAvailable: boolean,
};

export interface GithubReleaseAsset {
url: string;

Expand Down Expand Up @@ -199,9 +207,7 @@ export async function setHasQueuedUpdate(isQueued: boolean): Promise<void> {
}

async function getPlatformVersion(): Promise<string> {
const platform = os.platform();

switch (platform) {
switch (process.platform) {
case 'win32':
return os.release();
case 'darwin': {
Expand All @@ -219,7 +225,58 @@ async function getPlatformVersion(): Promise<string> {
// should suffice.
return 'unknown';
}
throw new Error(`Platform "${ platform }" is not supported`);
throw new Error(`Platform "${ process.platform }" is not supported`);
}

/**
* Fetch info on available versions of Rancher Desktop, as well as other
* things, from the Upgrade Responder server.
*/
export async function queryUpgradeResponder(url: string, currentVersion: semver.SemVer): Promise<UpgradeResponderQueryResult> {
const requestPayload = {
appVersion: currentVersion,
extraInfo: {
platform: `${ process.platform }-${ os.arch() }`,
platformVersion: await getPlatformVersion(),
},
};
// If we are using anything on `github.io` as the update server, we're
// trying to run a simplified test. In that case, break the protocol and do
// a HTTP GET instead of the HTTP POST with data we should do for actual
// longhorn upgrade-responder servers.
const requestOptions = /^https?:\/\/[^/]+\.github\.io\//.test(url) ? { method: 'GET' } : {
method: 'POST',
body: JSON.stringify(requestPayload),
};

console.debug(`Checking for upgrades from ${ url }`);
const responseRaw = await fetch(url, requestOptions);
const response = await responseRaw.json() as LonghornUpgraderResponse;

console.debug(`Upgrade server response:`, util.inspect(response, true, null));

const allVersions = response.versions;

// If the upgrade responder does not send the Supported field,
// assume that the version is supported.
for (const version of allVersions) {
version.Supported ??= true;
}

allVersions.sort((version1, version2) => semver.rcompare(version1.Name, version2.Name));
const supportedVersions = allVersions.filter(version => version.Supported);

if (supportedVersions.length === 0) {
throw newError('Could not find latest version', 'ERR_UPDATER_LATEST_VERSION_NOT_FOUND');
}
const latest = supportedVersions[0];
const unsupportedUpgradeAvailable = allVersions[0].Name !== latest.Name;

return {
latest,
requestIntervalInMinutes: response.requestIntervalInMinutes,
unsupportedUpgradeAvailable,
};
}

/**
Expand Down Expand Up @@ -257,43 +314,6 @@ export default class LonghornProvider extends Provider<LonghornUpdateInfo> {
return buffer.toString('base64');
}

/**
* Fetch info on available versions of Rancher Desktop from the Upgrade
* Responder server.
* @returns Array of available versions.
*/
async fetchUpgraderResponse(): Promise<LonghornUpgraderResponse> {
const requestPayload = {
appVersion: this.updater.currentVersion.format(),
extraInfo: {
platform: `${ os.platform() }-${ os.arch() }`,
platformVersion: await getPlatformVersion(),
},
};
// If we are using anything on `github.io` as the update server, we're
// trying to run a simplified test. In that case, break the protocol and do
// a HTTP GET instead of the HTTP POST with data we should do for actual
// longhorn upgrade-responder servers.
const requestOptions = /^https?:\/\/[^/]+\.github\.io\//.test(this.configuration.upgradeServer) ? { method: 'GET' } : {
method: 'POST',
body: JSON.stringify(requestPayload),
};

console.debug(`Checking for upgrades from ${ this.configuration.upgradeServer }`);
const responseRaw = await fetch(this.configuration.upgradeServer, requestOptions);
const response = await responseRaw.json() as LonghornUpgraderResponse;

console.debug(`Upgrade server response:`, util.inspect(response, true, null));

// If the upgrade responder does not send the Supported field,
// assume that the version is supported.
for (const version of response.versions) {
version.Supported = version.Supported ?? true;
}

return response;
}

/**
* Check for updates, possibly returning the cached information if it is still
* applicable.
Expand All @@ -313,19 +333,9 @@ export default class LonghornProvider extends Provider<LonghornUpdateInfo> {
}
}

const response = await this.fetchUpgraderResponse();
const allVersions = response.versions;

allVersions.sort((version1, version2) => semver.rcompare(version1.Name, version2.Name));
const supportedVersions = allVersions.filter(version => version.Supported);

if (supportedVersions.length === 0) {
throw newError('Could not find latest version', 'ERR_UPDATER_LATEST_VERSION_NOT_FOUND');
}
const latest = supportedVersions[0];
const unsupportedUpgradeAvailable = allVersions[0].Name !== latest.Name;

const requestIntervalInMinutes = response.requestIntervalInMinutes || defaultUpdateIntervalInMinutes;
const queryResult = await queryUpgradeResponder(this.configuration.upgradeServer, this.updater.currentVersion);
const { latest, unsupportedUpgradeAvailable } = queryResult;
const requestIntervalInMinutes = queryResult.requestIntervalInMinutes || defaultUpdateIntervalInMinutes;
const requestIntervalInMs = requestIntervalInMinutes * 1000 * 60;
const nextRequestTime = Date.now() + requestIntervalInMs;

Expand Down
109 changes: 109 additions & 0 deletions pkg/rancher-desktop/main/update/__tests__/LonghornProvider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import semver from 'semver';

import { queryUpgradeResponder } from '../LonghornProvider';

import fetch from '@pkg/utils/fetch';

jest.mock('@pkg/utils/fetch', () => {
return {
__esModule: true,
default: jest.fn(),
};
});

describe('queryUpgradeResponder', () => {
it('should return the latest version', async() => {
(fetch as jest.Mock).mockReturnValueOnce({
json: () => Promise.resolve({
requestIntervalInMinutes: 100,
versions: [
{
Name: 'v1.2.3',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
{
Name: 'v3.2.1',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
{
Name: 'v2.1.3',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
],
}),
});
const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3'));

expect(result.latest.Name).toEqual('v3.2.1');
});

it('should set unsupportedUpgradeAvailable to true when a newer-than-latest version is unsupported', async() => {
(fetch as jest.Mock).mockReturnValueOnce({
json: () => Promise.resolve({
requestIntervalInMinutes: 100,
versions: [
{
Name: 'v1.2.3',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
{
Name: 'v3.2.1',
ReleaseDate: 'testreleasedate',
Supported: false,
Tags: [],
},
{
Name: 'v2.1.3',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
],
}),
});
const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3'));

expect(result.unsupportedUpgradeAvailable).toBe(true);
expect(result.latest.Name).toEqual('v2.1.3');
});

it('should set unsupportedUpgradeAvailable to false when no newer-than-latest versions are unsupported', async() => {
(fetch as jest.Mock).mockReturnValueOnce({
json: () => Promise.resolve({
requestIntervalInMinutes: 100,
versions: [
{
Name: 'v1.2.3',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
{
Name: 'v3.2.1',
ReleaseDate: 'testreleasedate',
Supported: true,
Tags: [],
},
{
Name: 'v2.1.3',
ReleaseDate: 'testreleasedate',
Supported: false,
Tags: [],
},
],
}),
});
const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3'));

expect(result.unsupportedUpgradeAvailable).toBe(false);
expect(result.latest.Name).toEqual('v3.2.1');
});
});
2 changes: 1 addition & 1 deletion pkg/rancher-desktop/main/update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ async function getUpdater(): Promise<AppUpdater | undefined> {
});
updater.on('update-downloaded', (info) => {
if (!isLonghornUpdateInfo(info)) {
throw new Error('updater: event update-not-available: info is not of type LonghornUpdateInfo');
throw new Error('updater: event update-downloaded: info is not of type LonghornUpdateInfo');
}
if (state === State.DOWNLOADING) {
state = State.UPDATE_PENDING;
Expand Down
2 changes: 1 addition & 1 deletion pkg/rancher-desktop/utils/osVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Log } from '@pkg/utils/logging';

export async function getMacOsVersion(console: Log): Promise<semver.SemVer | null> {
const { stdout } = await spawnFile('/usr/bin/sw_vers', ['-productVersion'], { stdio: ['ignore', 'pipe', console] });
const currentVersion = semver.coerce(stdout.trim());
const currentVersion = semver.coerce(stdout);

return currentVersion;
}

0 comments on commit 58deb61

Please sign in to comment.