mirror of
https://github.com/Koenkk/zigbee-OTA.git
synced 2026-06-24 11:14:19 +00:00
Automate via workflows. Add auto-archiving for downgrade. (#581)
This commit is contained in:
241
tests/data.test.ts
Normal file
241
tests/data.test.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Notes:
|
||||
*
|
||||
* - URLs are initially set to 'wherever the file should end up based on version'. For tests requiring special moves, they will need to be swapped.
|
||||
*
|
||||
*/
|
||||
|
||||
import type {ExtraMetas, RepoImageMeta} from '../src/types';
|
||||
|
||||
import {copyFileSync, existsSync, mkdirSync} from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import * as common from '../src/common';
|
||||
|
||||
export const IMAGE_V14_1 = 'ZLinky_router_v14.ota';
|
||||
export const IMAGE_V14_2 = 'ZLinky_router_v14_limited.ota';
|
||||
export const IMAGE_V13_1 = 'ZLinky_router_v13.ota';
|
||||
export const IMAGE_V13_2 = 'ZLinky_router_v13_limited.ota';
|
||||
export const IMAGE_V12_1 = 'ZLinky_router_v12.ota';
|
||||
export const IMAGE_V12_2 = 'ZLinky_router_v12_limited.ota';
|
||||
export const IMAGE_INVALID = 'not-a-valid-file.ota';
|
||||
export const IMAGE_TAR = '45856_00000006.tar.gz';
|
||||
export const IMAGE_TAR_OTA = 'Jasco_5_0_1_OnOff_45856_v6.ota';
|
||||
export const IMAGES_TEST_DIR = 'jest-tmp';
|
||||
export const BASE_IMAGES_TEST_DIR_PATH = path.join(common.BASE_IMAGES_DIR, IMAGES_TEST_DIR);
|
||||
export const PREV_IMAGES_TEST_DIR_PATH = path.join(common.PREV_IMAGES_DIR, IMAGES_TEST_DIR);
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 1,
|
||||
* - fileVersion: 14,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
* - totalImageSize: 249694
|
||||
*/
|
||||
export const IMAGE_V14_1_METAS = {
|
||||
fileName: IMAGE_V14_1,
|
||||
fileVersion: 14,
|
||||
fileSize: 249694,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images/${IMAGES_TEST_DIR}/${IMAGE_V14_1}`,
|
||||
imageType: 1,
|
||||
manufacturerCode: 4151,
|
||||
sha512: 'cc69b0745c72daf8deda935ba47aa7abd34dfcaaa4bc35bfa0605cd7937b0ecd8582ba0c08110df4f620c8aa87798d201f407d3d7e17198cfef1a4aa13c5013d',
|
||||
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 2,
|
||||
* - fileVersion: 14,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
* - totalImageSize: 249694
|
||||
*/
|
||||
export const IMAGE_V14_2_METAS = {
|
||||
fileName: IMAGE_V14_2,
|
||||
fileVersion: 14,
|
||||
fileSize: 249694,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images/${IMAGES_TEST_DIR}/${IMAGE_V14_2}`,
|
||||
imageType: 2,
|
||||
manufacturerCode: 4151,
|
||||
sha512: 'f851cbff7297ba6223a969ba8da5182f9ef199cf9c8459c8408432e48485c1a8f018f6e1703a42f40143cccd3bf460c0acd92117d899e507a36845f24e970595',
|
||||
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 1,
|
||||
* - fileVersion: 13,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
* - totalImageSize: 245406
|
||||
*/
|
||||
export const IMAGE_V13_1_METAS = {
|
||||
fileName: IMAGE_V13_1,
|
||||
fileVersion: 13,
|
||||
fileSize: 245406,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images1/${IMAGES_TEST_DIR}/${IMAGE_V13_1}`,
|
||||
imageType: 1,
|
||||
manufacturerCode: 4151,
|
||||
sha512: '4d7ab47dcb24e478e0abb35e691222b7691e77ed5a56de3f9c82e8682730649b1a154110b7207d4391c32eae53a869e20878e880fc153dbe046690b870be8486',
|
||||
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 2,
|
||||
* - fileVersion: 13,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
* - totalImageSize: 245406
|
||||
*/
|
||||
export const IMAGE_V13_2_METAS = {
|
||||
fileName: IMAGE_V13_2,
|
||||
fileVersion: 13,
|
||||
fileSize: 245406,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images1/${IMAGES_TEST_DIR}/${IMAGE_V13_2}`,
|
||||
imageType: 2,
|
||||
manufacturerCode: 4151,
|
||||
sha512: 'dd77b28a3b4664e7ad944fcffaa9eca9f3adb0bbe598e12bdd6eece8070a8cdda6792bed378d173dd5b4532b4cdb88cebda0ef0c432c4c4d6581aa9f2bbba54d',
|
||||
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 1,
|
||||
* - fileVersion: 12,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
* - totalImageSize: 245710
|
||||
*/
|
||||
export const IMAGE_V12_1_METAS = {
|
||||
fileName: IMAGE_V12_1,
|
||||
fileVersion: 12,
|
||||
fileSize: 245710,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images1/${IMAGES_TEST_DIR}/${IMAGE_V12_1}`,
|
||||
imageType: 1,
|
||||
manufacturerCode: 4151,
|
||||
sha512: '5d7e0a20141b78b85b4b046e623bc2bba24b28563464fe70227e79d0acdd5c0bde2adbd9d2557bd6cdfef2036d964c35c9e1746a8f1356af3325dd96f7a80e56',
|
||||
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4151,
|
||||
* - imageType: 2,
|
||||
* - fileVersion: 12,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
* - totalImageSize: 245710
|
||||
*/
|
||||
export const IMAGE_V12_2_METAS = {
|
||||
fileName: IMAGE_V12_2,
|
||||
fileVersion: 12,
|
||||
fileSize: 245710,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images1/${IMAGES_TEST_DIR}/${IMAGE_V12_2}`,
|
||||
imageType: 2,
|
||||
manufacturerCode: 4151,
|
||||
sha512: '4e178e56c1559e11734c07abbb95110675df7738f3ca3e5dbc99393325295ff6c66bd63ba55c0ef6043a80608dbec2be7a1e845f31ffd94f1cb63f32f0d48c6e',
|
||||
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
|
||||
};
|
||||
/** obviously bogus, just for mocking */
|
||||
export const IMAGE_INVALID_METAS = {
|
||||
fileName: IMAGE_INVALID,
|
||||
fileVersion: 0,
|
||||
fileSize: 0,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images/${IMAGES_TEST_DIR}/${IMAGE_INVALID}`,
|
||||
imageType: 1,
|
||||
manufacturerCode: 65535,
|
||||
sha512: 'abcd',
|
||||
otaHeaderString: 'nothing',
|
||||
};
|
||||
/**
|
||||
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
|
||||
* - otaHeaderVersion: 256,
|
||||
* - otaHeaderLength: 56,
|
||||
* - otaHeaderFieldControl: 0,
|
||||
* - manufacturerCode: 4388,
|
||||
* - imageType: 2,
|
||||
* - fileVersion: 6,
|
||||
* - zigbeeStackVersion: 2,
|
||||
* - otaHeaderString: 'Jasco 45856 image\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
* - totalImageSize: 162302
|
||||
*/
|
||||
export const IMAGE_TAR_METAS = {
|
||||
fileName: IMAGE_TAR_OTA,
|
||||
fileVersion: 6,
|
||||
fileSize: 162302,
|
||||
originalUrl: undefined,
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/images/${IMAGES_TEST_DIR}/${IMAGE_TAR_OTA}`,
|
||||
imageType: 2,
|
||||
manufacturerCode: 4388,
|
||||
sha512: '3306332e001eab9d71c9360089d450ea21e2c08bac957b523643c042707887e85db0c510f3480bdbcfcfe2398eeaad88d455f346f1e07841e1d690d8c16dc211',
|
||||
otaHeaderString: 'Jasco 45856 image\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
};
|
||||
|
||||
export const getImageOriginalDirPath = (imageName: string): string => {
|
||||
return path.join('tests', common.BASE_IMAGES_DIR, imageName);
|
||||
};
|
||||
|
||||
export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DIR_PATH): string => {
|
||||
const realPath = path.join(outDir, imageName);
|
||||
|
||||
if (!existsSync(outDir)) {
|
||||
mkdirSync(outDir, {recursive: true});
|
||||
}
|
||||
|
||||
copyFileSync(getImageOriginalDirPath(imageName), realPath);
|
||||
|
||||
// return as posix for github match
|
||||
return path.posix.join(BASE_IMAGES_TEST_DIR_PATH.replaceAll('\\', '/'), imageName);
|
||||
};
|
||||
|
||||
export const withExtraMetas = (meta: RepoImageMeta, extraMetas: ExtraMetas): RepoImageMeta => {
|
||||
return Object.assign({}, structuredClone(meta), extraMetas);
|
||||
};
|
||||
|
||||
export const getAdjustedContent = (fileName: string, content: RepoImageMeta[]): RepoImageMeta[] => {
|
||||
return content.map((c) => {
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME && c.url.includes(`/${common.PREV_IMAGES_DIR}/`)) {
|
||||
return withExtraMetas(c, {
|
||||
// @ts-expect-error override
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/${common.BASE_IMAGES_DIR}/${IMAGES_TEST_DIR}/${c.fileName}`,
|
||||
});
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME && c.url.includes(`${common.BASE_IMAGES_DIR}`)) {
|
||||
return withExtraMetas(c, {
|
||||
// @ts-expect-error override
|
||||
url: `https://github.com/Koenkk/zigbee-OTA/raw/master/${common.PREV_IMAGES_DIR}/${IMAGES_TEST_DIR}/${c.fileName}`,
|
||||
});
|
||||
}
|
||||
|
||||
return c;
|
||||
});
|
||||
};
|
||||
|
||||
// required to consider as a 'test suite'
|
||||
it('passes', () => {});
|
||||
786
tests/ghw_reprocess_all_images.test.ts
Normal file
786
tests/ghw_reprocess_all_images.test.ts
Normal file
@@ -0,0 +1,786 @@
|
||||
import type CoreApi from '@actions/core';
|
||||
import type {Context} from '@actions/github/lib/context';
|
||||
|
||||
import type {RepoImageMeta} from '../src/types';
|
||||
|
||||
import {copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync} from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import * as common from '../src/common';
|
||||
import {
|
||||
NOT_IN_BASE_MANIFEST_IMAGES_DIR,
|
||||
NOT_IN_MANIFEST_FILENAME,
|
||||
NOT_IN_PREV_MANIFEST_IMAGES_DIR,
|
||||
reProcessAllImages,
|
||||
} from '../src/ghw_reprocess_all_images';
|
||||
import {
|
||||
BASE_IMAGES_TEST_DIR_PATH,
|
||||
getAdjustedContent,
|
||||
getImageOriginalDirPath,
|
||||
IMAGE_INVALID,
|
||||
IMAGE_INVALID_METAS,
|
||||
IMAGE_V12_1,
|
||||
IMAGE_V13_1,
|
||||
IMAGE_V13_1_METAS,
|
||||
IMAGE_V14_1,
|
||||
IMAGE_V14_1_METAS,
|
||||
IMAGES_TEST_DIR,
|
||||
PREV_IMAGES_TEST_DIR_PATH,
|
||||
useImage,
|
||||
withExtraMetas,
|
||||
} from './data.test';
|
||||
|
||||
/** not used */
|
||||
const github = {};
|
||||
const core: Partial<typeof CoreApi> = {
|
||||
debug: console.debug,
|
||||
info: console.log,
|
||||
warning: console.warn,
|
||||
error: console.error,
|
||||
notice: console.log,
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
};
|
||||
const context: Partial<Context> = {
|
||||
payload: {},
|
||||
repo: {
|
||||
owner: 'Koenkk',
|
||||
repo: 'zigbee-OTA',
|
||||
},
|
||||
};
|
||||
|
||||
const OLD_META_3RD_PARTY_1_REAL_IMAGE = IMAGE_V13_1;
|
||||
const OLD_META_3RD_PARTY_1_REAL_METAS = IMAGE_V13_1_METAS;
|
||||
const OLD_META_3RD_PARTY_1_METAS = {
|
||||
fileVersion: 1124103171,
|
||||
fileSize: 258104,
|
||||
manufacturerCode: 4107,
|
||||
imageType: 256,
|
||||
sha512: 'c63a1eb02ac030f3a76d9e81a4d48695796457d263bb1dae483688134e550d9846c37a3fd0eab2d4670f12f11b79691a5cf2789af0dbd90d703512496190a0a5',
|
||||
// mock fileName to trigger mocked fetch properly
|
||||
url: `https://otau.meethue.com/storage/ZGB_100B_0100/2dcfe6e6-0177-4c81-a1d9-4d2bd2ea1fb7/${OLD_META_3RD_PARTY_1_REAL_IMAGE}`,
|
||||
};
|
||||
const OLD_META_3RD_PARTY_2_REAL_IMAGE = IMAGE_V14_1;
|
||||
const OLD_META_3RD_PARTY_2_REAL_METAS = IMAGE_V14_1_METAS;
|
||||
const OLD_META_3RD_PARTY_2_METAS = {
|
||||
fileVersion: 192,
|
||||
fileSize: 307682,
|
||||
manufacturerCode: 4417,
|
||||
imageType: 54179,
|
||||
modelId: 'TS011F',
|
||||
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
|
||||
// mock fileName to trigger mocked fetch properly
|
||||
url: `https://images.tuyaeu.com/smart/firmware/upgrade/20220907/${OLD_META_3RD_PARTY_2_REAL_IMAGE}`,
|
||||
};
|
||||
const OLD_META_3RD_PARTY_IGNORED_METAS = {
|
||||
fileVersion: 317,
|
||||
fileSize: 693230,
|
||||
manufacturerCode: 13379,
|
||||
imageType: 4113,
|
||||
sha512: '66040fb2b2787bf8ebfc75bc3c7356c7d8b966b4c82282bd7393783b8dc453ec2c8dcb4d7c9fe7c0a83d87739bd3677f205d79edddfa4fa2749305ca987887b1',
|
||||
url: 'https://github.com/xyzroe/ZigUSB_C6/releases/download/317/ZigUSB_C6.ota',
|
||||
};
|
||||
const NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH = path.join(NOT_IN_BASE_MANIFEST_IMAGES_DIR, IMAGES_TEST_DIR);
|
||||
const NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH = path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR, IMAGES_TEST_DIR);
|
||||
const NOT_IN_BASE_MANIFEST_FILEPATH = path.join(NOT_IN_BASE_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME);
|
||||
const NOT_IN_PREV_MANIFEST_FILEPATH = path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME);
|
||||
// move to tmp dirs in `beforeAll` to allow tests to run (reverted in `afterAll`)
|
||||
const NOT_IN_PREV_MANIFEST_IMAGES_DIR_TMP = `${NOT_IN_PREV_MANIFEST_IMAGES_DIR}-moved-by-jest`;
|
||||
const NOT_IN_BASE_MANIFEST_IMAGES_DIR_TMP = `${NOT_IN_BASE_MANIFEST_IMAGES_DIR}-moved-by-jest`;
|
||||
|
||||
describe('Github Workflow: Re-Process All Images', () => {
|
||||
let baseManifest: RepoImageMeta[];
|
||||
let prevManifest: RepoImageMeta[];
|
||||
let notInBaseManifest: RepoImageMeta[];
|
||||
let notInPrevManifest: RepoImageMeta[];
|
||||
let readManifestSpy: jest.SpyInstance;
|
||||
let writeManifestSpy: jest.SpyInstance;
|
||||
let addImageToBaseSpy: jest.SpyInstance;
|
||||
let addImageToPrevSpy: jest.SpyInstance;
|
||||
let coreWarningSpy: jest.SpyInstance;
|
||||
let coreErrorSpy: jest.SpyInstance;
|
||||
|
||||
const getManifest = (fileName: string): RepoImageMeta[] => {
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
return baseManifest;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
return prevManifest;
|
||||
} else if (fileName === path.join(NOT_IN_BASE_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
|
||||
return notInBaseManifest;
|
||||
} else if (fileName === path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
|
||||
return notInPrevManifest;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
|
||||
const adjustedContent = getAdjustedContent(fileName, content);
|
||||
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
baseManifest = adjustedContent;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
prevManifest = adjustedContent;
|
||||
} else if (fileName === path.join(NOT_IN_BASE_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
|
||||
notInBaseManifest = adjustedContent;
|
||||
} else if (fileName === path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
|
||||
notInPrevManifest = adjustedContent;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const resetManifests = (): void => {
|
||||
baseManifest = [];
|
||||
prevManifest = [];
|
||||
};
|
||||
|
||||
const withOldMetas = (metas: RepoImageMeta): RepoImageMeta => {
|
||||
const oldMetas = structuredClone(metas);
|
||||
delete oldMetas.originalUrl;
|
||||
// @ts-expect-error mock
|
||||
delete oldMetas.sha512;
|
||||
|
||||
return oldMetas;
|
||||
};
|
||||
|
||||
const expectWriteNoChange = (nth: number, fileName: string): void => {
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(nth, fileName, getManifest(fileName));
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
if (existsSync(NOT_IN_PREV_MANIFEST_IMAGES_DIR)) {
|
||||
renameSync(NOT_IN_PREV_MANIFEST_IMAGES_DIR, NOT_IN_PREV_MANIFEST_IMAGES_DIR_TMP);
|
||||
}
|
||||
|
||||
if (existsSync(NOT_IN_BASE_MANIFEST_IMAGES_DIR)) {
|
||||
renameSync(NOT_IN_BASE_MANIFEST_IMAGES_DIR, NOT_IN_BASE_MANIFEST_IMAGES_DIR_TMP);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
readManifestSpy.mockRestore();
|
||||
writeManifestSpy.mockRestore();
|
||||
addImageToBaseSpy.mockRestore();
|
||||
addImageToPrevSpy.mockRestore();
|
||||
coreWarningSpy.mockRestore();
|
||||
coreErrorSpy.mockRestore();
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, {recursive: true, force: true});
|
||||
|
||||
if (existsSync(NOT_IN_PREV_MANIFEST_IMAGES_DIR_TMP)) {
|
||||
rmSync(NOT_IN_PREV_MANIFEST_IMAGES_DIR, {recursive: true, force: true});
|
||||
renameSync(NOT_IN_PREV_MANIFEST_IMAGES_DIR_TMP, NOT_IN_PREV_MANIFEST_IMAGES_DIR);
|
||||
}
|
||||
|
||||
if (existsSync(NOT_IN_BASE_MANIFEST_IMAGES_DIR_TMP)) {
|
||||
rmSync(NOT_IN_BASE_MANIFEST_IMAGES_DIR, {recursive: true, force: true});
|
||||
renameSync(NOT_IN_BASE_MANIFEST_IMAGES_DIR_TMP, NOT_IN_BASE_MANIFEST_IMAGES_DIR);
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetManifests();
|
||||
|
||||
readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest);
|
||||
writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest);
|
||||
addImageToBaseSpy = jest.spyOn(common, 'addImageToBase');
|
||||
addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev');
|
||||
coreWarningSpy = jest.spyOn(core, 'warning');
|
||||
coreErrorSpy = jest.spyOn(core, 'error');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it('failure when moving not in manifest if base out directory is not empty', async () => {
|
||||
mkdirSync(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, {recursive: true});
|
||||
copyFileSync(getImageOriginalDirPath(IMAGE_V12_1), path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1));
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining('is not empty')}));
|
||||
});
|
||||
|
||||
it('failure when moving not in manifest if prev out directory is not empty', async () => {
|
||||
mkdirSync(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, {recursive: true});
|
||||
copyFileSync(getImageOriginalDirPath(IMAGE_V12_1), path.join(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1));
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining('is not empty')}));
|
||||
});
|
||||
|
||||
it('failure when image not in subdirectory', async () => {
|
||||
// this is renaming the image to the same as the test dir name for simplicity in code exclusion
|
||||
const outPath = path.join(common.PREV_IMAGES_DIR, IMAGES_TEST_DIR);
|
||||
|
||||
if (!existsSync(common.PREV_IMAGES_DIR)) {
|
||||
mkdirSync(common.PREV_IMAGES_DIR, {recursive: true});
|
||||
}
|
||||
|
||||
copyFileSync(getImageOriginalDirPath(IMAGE_V12_1), outPath);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`Detected file in ${common.PREV_IMAGES_DIR} not in subdirectory`)}),
|
||||
);
|
||||
|
||||
rmSync(outPath, {force: true});
|
||||
});
|
||||
|
||||
it('removes image not in manifest', async () => {
|
||||
const imagePath = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(imagePath)).toStrictEqual(false);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Not found in base manifest:`));
|
||||
});
|
||||
|
||||
it('removes multiple images not in manifest', async () => {
|
||||
const image1Path = useImage(IMAGE_V13_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
const image2Path = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
const image3Path = useImage(IMAGE_V12_1, PREV_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(image1Path)).toStrictEqual(false);
|
||||
expect(existsSync(image2Path)).toStrictEqual(false);
|
||||
expect(existsSync(image3Path)).toStrictEqual(false);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
// prev first, then alphabetical
|
||||
expect(coreWarningSpy).toHaveBeenNthCalledWith(1, expect.stringContaining(`Not found in base manifest:`));
|
||||
expect(coreWarningSpy).toHaveBeenNthCalledWith(2, expect.stringContaining(`Not found in base manifest:`));
|
||||
expect(coreWarningSpy).toHaveBeenNthCalledWith(3, expect.stringContaining(`Not found in base manifest:`));
|
||||
});
|
||||
|
||||
it('moves image not in manifest', async () => {
|
||||
const oldPath = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
|
||||
const newPath = path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1);
|
||||
expect(existsSync(oldPath)).toStrictEqual(false);
|
||||
expect(existsSync(newPath)).toStrictEqual(true);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(3);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, NOT_IN_BASE_MANIFEST_FILEPATH, expect.any(Array));
|
||||
expectWriteNoChange(3, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
});
|
||||
|
||||
it('moves multiple images not in manifest', async () => {
|
||||
const oldPath1 = useImage(IMAGE_V13_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
const oldPath2 = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
const oldPath3 = useImage(IMAGE_V12_1, PREV_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
|
||||
const newPath1 = path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V13_1);
|
||||
const newPath2 = path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1);
|
||||
const newPath3 = path.join(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1);
|
||||
expect(existsSync(newPath1)).toStrictEqual(true);
|
||||
expect(existsSync(oldPath1)).toStrictEqual(false);
|
||||
expect(existsSync(newPath2)).toStrictEqual(true);
|
||||
expect(existsSync(oldPath2)).toStrictEqual(false);
|
||||
expect(existsSync(newPath3)).toStrictEqual(true);
|
||||
expect(existsSync(oldPath3)).toStrictEqual(false);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, NOT_IN_PREV_MANIFEST_FILEPATH, expect.any(Array));
|
||||
expectWriteNoChange(2, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(3, NOT_IN_BASE_MANIFEST_FILEPATH, expect.any(Array));
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
});
|
||||
|
||||
it('removes invalid not in manifest even if remove disabled', async () => {
|
||||
const oldPath = useImage(IMAGE_INVALID, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, false, true);
|
||||
|
||||
expect(existsSync(oldPath)).toStrictEqual(false);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing`));
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Not a valid OTA file`));
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Not found in base manifest`));
|
||||
});
|
||||
|
||||
it('removes invalid in manifest', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_INVALID_METAS]);
|
||||
const oldPath = useImage(IMAGE_INVALID, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(oldPath)).toStrictEqual(false);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing`));
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Not a valid OTA file`));
|
||||
});
|
||||
|
||||
it('keeps image and rewrites manifest', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOldMetas(IMAGE_V14_1_METAS)]);
|
||||
const imagePath = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(imagePath)).toStrictEqual(true);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
});
|
||||
|
||||
it('keeps image with escaped url and rewrites manifest', async () => {
|
||||
const oldMetas = withOldMetas(IMAGE_V14_1_METAS);
|
||||
const fileName = oldMetas.url.split('/').pop()!;
|
||||
const newName = fileName.replace('.ota', `(%1).ota`);
|
||||
const baseUrl = oldMetas.url.replace(fileName, '');
|
||||
oldMetas.url = baseUrl + escape(newName);
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas]);
|
||||
const imagePath = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
const baseName = path.basename(imagePath);
|
||||
const renamedPath = imagePath.replace(baseName, newName);
|
||||
renameSync(imagePath, renamedPath);
|
||||
console.log(newName, oldMetas.url, renamedPath);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(renamedPath)).toStrictEqual(true);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
const outManifestMetas = withExtraMetas(
|
||||
IMAGE_V14_1_METAS,
|
||||
// @ts-expect-error override
|
||||
{fileName: newName, url: `${baseUrl}${newName}`},
|
||||
);
|
||||
delete outManifestMetas.originalUrl;
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [outManifestMetas]);
|
||||
});
|
||||
|
||||
it('ignores when same images referenced multiple times in manifest', async () => {
|
||||
const oldMetas1 = withOldMetas(IMAGE_V14_1_METAS);
|
||||
const oldMetas2 = withOldMetas(IMAGE_V14_1_METAS);
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas1, oldMetas2]);
|
||||
const image1Path = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(image1Path)).toStrictEqual(true);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`found multiple times in ${common.BASE_INDEX_MANIFEST_FILENAME} manifest`),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps same images referenced multiple times in manifest with different extra metas', async () => {
|
||||
const oldMetas1 = withExtraMetas(withOldMetas(IMAGE_V14_1_METAS), {modelId: 'test1'});
|
||||
const oldMetas2 = withExtraMetas(withOldMetas(IMAGE_V14_1_METAS), {modelId: 'test2'});
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas1, oldMetas2]);
|
||||
const image1Path = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, true);
|
||||
|
||||
expect(existsSync(image1Path)).toStrictEqual(true);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {modelId: 'test1'}),
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {modelId: 'test2'}),
|
||||
]);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`found multiple times in ${common.BASE_INDEX_MANIFEST_FILENAME} manifest`),
|
||||
);
|
||||
});
|
||||
|
||||
describe('downloads', () => {
|
||||
let fetchSpy: jest.SpyInstance;
|
||||
let setTimeoutSpy: jest.SpyInstance;
|
||||
let fetchReturnedStatus: {ok: boolean; status: number; body?: object} = {ok: true, status: 200, body: {}};
|
||||
const get3rdPartyDir = jest.fn().mockReturnValue(IMAGES_TEST_DIR);
|
||||
|
||||
const adaptUrl = (originalUrl: string, manifestName: string): string => {
|
||||
if (manifestName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
return originalUrl.replace(`/${common.PREV_IMAGES_DIR}/`, `/${common.BASE_IMAGES_DIR}/`);
|
||||
} else if (manifestName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
return originalUrl.replace(`/${common.BASE_IMAGES_DIR}/`, `/${common.PREV_IMAGES_DIR}/`);
|
||||
} else {
|
||||
throw new Error(`Not supported: ${manifestName}`);
|
||||
}
|
||||
};
|
||||
|
||||
afterAll(() => {
|
||||
fetchSpy.mockRestore();
|
||||
setTimeoutSpy.mockRestore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.NODE_EXTRA_CA_CERTS = 'cacerts.pem';
|
||||
fetchReturnedStatus = {ok: true, status: 200, body: {}};
|
||||
fetchSpy = jest.spyOn(global, 'fetch').mockImplementation(
|
||||
// @ts-expect-error mocked as needed
|
||||
(input) => {
|
||||
return {
|
||||
ok: fetchReturnedStatus.ok,
|
||||
status: fetchReturnedStatus.status,
|
||||
body: fetchReturnedStatus.body,
|
||||
arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath((input as string).split('/').pop()!)),
|
||||
};
|
||||
},
|
||||
);
|
||||
setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(
|
||||
// @ts-expect-error mock
|
||||
(fn) => {
|
||||
fn();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('failure without CA Certificates ENV', async () => {
|
||||
process.env.NODE_EXTRA_CA_CERTS = '';
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`Download 3rd Parties requires \`NODE_EXTRA_CA_CERTS=cacerts.pem\``)}),
|
||||
);
|
||||
});
|
||||
|
||||
it('failure with malformed metas', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
// @ts-expect-error old metas
|
||||
{
|
||||
fileVersion: 192,
|
||||
fileSize: 307682,
|
||||
manufacturerCode: 4417,
|
||||
imageType: 54179,
|
||||
modelId: 'TS011F',
|
||||
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
|
||||
url: '', // not undefined to pass setManifest
|
||||
},
|
||||
]);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(0);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(3, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Ignoring malformed`));
|
||||
});
|
||||
|
||||
it('failure from fetch ok', async () => {
|
||||
setManifest(
|
||||
common.BASE_INDEX_MANIFEST_FILENAME,
|
||||
// @ts-expect-error old metas
|
||||
[OLD_META_3RD_PARTY_1_METAS],
|
||||
);
|
||||
fetchReturnedStatus.ok = false;
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(3, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(
|
||||
`Invalid response from ${OLD_META_3RD_PARTY_1_METAS.url} status=${fetchReturnedStatus.status}.`,
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores urls from this repo', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
// prevent trigger removal because of missing file
|
||||
useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(0);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(3, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
});
|
||||
|
||||
it('ignores urls with no out dir specified', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
// @ts-expect-error old metas
|
||||
{
|
||||
fileVersion: 192,
|
||||
fileSize: 307682,
|
||||
manufacturerCode: 4417,
|
||||
imageType: 54179,
|
||||
modelId: 'TS011F',
|
||||
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
|
||||
url: 'https://www.elektroimportoren.no/docs/lib/4512772-Firmware-35.ota',
|
||||
},
|
||||
]);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(0);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(3, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`no out dir specified`));
|
||||
});
|
||||
|
||||
it('ignores invalid OTA file', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
Object.assign({}, withOldMetas(IMAGE_INVALID_METAS), {
|
||||
url: `https://images.tuyaeu.com/smart/firmware/upgrade/20220907/${IMAGES_TEST_DIR}/${IMAGE_INVALID}`,
|
||||
}),
|
||||
]);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(2, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(3, common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Ignoring`));
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Not a valid OTA file`));
|
||||
});
|
||||
|
||||
it('ignores identical image', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
IMAGE_V14_1_METAS,
|
||||
Object.assign({}, withOldMetas(IMAGE_V14_1_METAS), {
|
||||
url: `https://images.tuyaeu.com/smart/firmware/upgrade/20220907/${IMAGES_TEST_DIR}/${IMAGE_V14_1}`,
|
||||
}),
|
||||
]);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Conflict with image at index \`0\``));
|
||||
});
|
||||
|
||||
it('success without mocked get3rdPartyDir', async () => {
|
||||
// NOTE: this is using a name (ZLinky_router_v13.ota) and out dir (Hue) that is unlikely to ever be in conflict with actual Hue images
|
||||
setManifest(
|
||||
common.BASE_INDEX_MANIFEST_FILENAME,
|
||||
// @ts-expect-error old metas
|
||||
[OLD_META_3RD_PARTY_1_METAS],
|
||||
);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
originalUrl: OLD_META_3RD_PARTY_1_METAS.url,
|
||||
// @ts-expect-error override
|
||||
url: adaptUrl(OLD_META_3RD_PARTY_1_REAL_METAS.url, common.BASE_INDEX_MANIFEST_FILENAME).replace(IMAGES_TEST_DIR, 'Hue'),
|
||||
}),
|
||||
]);
|
||||
|
||||
rmSync(path.join(common.BASE_IMAGES_DIR, 'Hue', OLD_META_3RD_PARTY_1_REAL_IMAGE));
|
||||
});
|
||||
|
||||
it('success with add different metas and ignored', async () => {
|
||||
setManifest(
|
||||
common.BASE_INDEX_MANIFEST_FILENAME,
|
||||
// @ts-expect-error old metas
|
||||
[OLD_META_3RD_PARTY_1_METAS, OLD_META_3RD_PARTY_2_METAS, OLD_META_3RD_PARTY_IGNORED_METAS],
|
||||
);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(get3rdPartyDir).toHaveBeenCalledTimes(2);
|
||||
expect(get3rdPartyDir).toHaveBeenNthCalledWith(1, OLD_META_3RD_PARTY_1_METAS);
|
||||
expect(get3rdPartyDir).toHaveBeenNthCalledWith(2, OLD_META_3RD_PARTY_2_METAS);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, second process moves first to prev
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
originalUrl: OLD_META_3RD_PARTY_1_METAS.url,
|
||||
// @ts-expect-error override
|
||||
url: adaptUrl(OLD_META_3RD_PARTY_1_REAL_METAS.url, common.BASE_INDEX_MANIFEST_FILENAME),
|
||||
}),
|
||||
withExtraMetas(OLD_META_3RD_PARTY_2_REAL_METAS, {
|
||||
originalUrl: OLD_META_3RD_PARTY_2_METAS.url,
|
||||
// @ts-expect-error override
|
||||
url: adaptUrl(OLD_META_3RD_PARTY_2_REAL_METAS.url, common.BASE_INDEX_MANIFEST_FILENAME),
|
||||
modelId: OLD_META_3RD_PARTY_2_METAS.modelId,
|
||||
}),
|
||||
]);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing ignored '${OLD_META_3RD_PARTY_IGNORED_METAS.url}'`));
|
||||
});
|
||||
|
||||
it('success with add+move same and ignored', async () => {
|
||||
setManifest(
|
||||
common.BASE_INDEX_MANIFEST_FILENAME,
|
||||
// @ts-expect-error old metas
|
||||
[OLD_META_3RD_PARTY_1_METAS, OLD_META_3RD_PARTY_IGNORED_METAS, withExtraMetas(OLD_META_3RD_PARTY_2_METAS, {modelId: undefined})],
|
||||
);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, second process moves first to prev
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
originalUrl: OLD_META_3RD_PARTY_1_METAS.url,
|
||||
// @ts-expect-error override
|
||||
url: adaptUrl(OLD_META_3RD_PARTY_1_REAL_METAS.url, common.PREV_INDEX_MANIFEST_FILENAME),
|
||||
}),
|
||||
]);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_2_REAL_METAS, {
|
||||
originalUrl: OLD_META_3RD_PARTY_2_METAS.url,
|
||||
// @ts-expect-error override
|
||||
url: adaptUrl(OLD_META_3RD_PARTY_2_REAL_METAS.url, common.BASE_INDEX_MANIFEST_FILENAME),
|
||||
}),
|
||||
]);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing ignored '${OLD_META_3RD_PARTY_IGNORED_METAS.url}'`));
|
||||
});
|
||||
|
||||
it('success with add to prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
IMAGE_V14_1_METAS,
|
||||
// @ts-expect-error old metas
|
||||
OLD_META_3RD_PARTY_1_METAS,
|
||||
]);
|
||||
// prevent trigger removal because of missing file
|
||||
useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, get3rdPartyDir);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
originalUrl: adaptUrl(OLD_META_3RD_PARTY_1_METAS.url, common.PREV_INDEX_MANIFEST_FILENAME),
|
||||
}),
|
||||
]);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(3, common.PREV_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
originalUrl: adaptUrl(OLD_META_3RD_PARTY_1_METAS.url, common.PREV_INDEX_MANIFEST_FILENAME),
|
||||
}),
|
||||
]);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(4, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
});
|
||||
|
||||
it('success with escaped', async () => {
|
||||
const oldMetas = structuredClone(OLD_META_3RD_PARTY_1_METAS);
|
||||
const fileName = oldMetas.url.split('/').pop()!;
|
||||
const newName = fileName.replace('.ota', `(%1).ota`);
|
||||
const baseUrl = oldMetas.url.replace(fileName, '');
|
||||
oldMetas.url = baseUrl + escape(newName);
|
||||
// @ts-expect-error old metas
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas]);
|
||||
// link back to existing image from fetch
|
||||
fetchSpy = jest.spyOn(global, 'fetch').mockImplementationOnce(
|
||||
// @ts-expect-error mocked as needed
|
||||
() => {
|
||||
return {
|
||||
ok: fetchReturnedStatus.ok,
|
||||
status: fetchReturnedStatus.status,
|
||||
body: fetchReturnedStatus.body,
|
||||
arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath(fileName)),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// @ts-expect-error mocked as needed
|
||||
await reProcessAllImages(github, core, context, true, false, () => IMAGES_TEST_DIR);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(4);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
const outManifestMetas = withExtraMetas(OLD_META_3RD_PARTY_1_REAL_METAS, {
|
||||
// @ts-expect-error override
|
||||
fileName: newName,
|
||||
originalUrl: oldMetas.url,
|
||||
url: common.getRepoFirmwareFileUrl(IMAGES_TEST_DIR, newName, common.BASE_IMAGES_DIR),
|
||||
});
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [outManifestMetas]);
|
||||
});
|
||||
});
|
||||
});
|
||||
531
tests/ghw_update_ota_pr.test.ts
Normal file
531
tests/ghw_update_ota_pr.test.ts
Normal file
@@ -0,0 +1,531 @@
|
||||
import type CoreApi from '@actions/core';
|
||||
import type {Context} from '@actions/github/lib/context';
|
||||
import type {Octokit} from '@octokit/rest';
|
||||
|
||||
import type {RepoImageMeta} from '../src/types';
|
||||
|
||||
import {rmSync} from 'fs';
|
||||
|
||||
import * as common from '../src/common';
|
||||
import {updateOtaPR} from '../src/ghw_update_ota_pr';
|
||||
import {
|
||||
BASE_IMAGES_TEST_DIR_PATH,
|
||||
getAdjustedContent,
|
||||
IMAGE_INVALID,
|
||||
IMAGE_V12_1,
|
||||
IMAGE_V12_1_METAS,
|
||||
IMAGE_V13_1,
|
||||
IMAGE_V13_1_METAS,
|
||||
IMAGE_V14_1,
|
||||
IMAGE_V14_1_METAS,
|
||||
IMAGE_V14_2,
|
||||
IMAGE_V14_2_METAS,
|
||||
PREV_IMAGES_TEST_DIR_PATH,
|
||||
useImage,
|
||||
withExtraMetas,
|
||||
} from './data.test';
|
||||
|
||||
const github = {
|
||||
rest: {
|
||||
issues: {
|
||||
createComment: jest.fn<
|
||||
ReturnType<Octokit['rest']['issues']['createComment']>,
|
||||
Parameters<Octokit['rest']['issues']['createComment']>,
|
||||
unknown
|
||||
>(),
|
||||
},
|
||||
pulls: {
|
||||
createReviewComment: jest.fn<
|
||||
ReturnType<Octokit['rest']['pulls']['createReviewComment']>,
|
||||
Parameters<Octokit['rest']['pulls']['createReviewComment']>,
|
||||
unknown
|
||||
>(),
|
||||
},
|
||||
},
|
||||
};
|
||||
const core: Partial<typeof CoreApi> = {
|
||||
debug: console.debug,
|
||||
info: console.log,
|
||||
warning: console.warn,
|
||||
error: console.error,
|
||||
notice: console.log,
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
};
|
||||
const context: Partial<Context> = {
|
||||
payload: {
|
||||
pull_request: {
|
||||
number: 1,
|
||||
head: {
|
||||
sha: 'abcd',
|
||||
},
|
||||
},
|
||||
},
|
||||
issue: {
|
||||
owner: 'Koenkk',
|
||||
repo: 'zigbee-OTA',
|
||||
number: 1,
|
||||
},
|
||||
repo: {
|
||||
owner: 'Koenkk',
|
||||
repo: 'zigbee-OTA',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Github Workflow: Update OTA PR', () => {
|
||||
let baseManifest: RepoImageMeta[];
|
||||
let prevManifest: RepoImageMeta[];
|
||||
let readManifestSpy: jest.SpyInstance;
|
||||
let writeManifestSpy: jest.SpyInstance;
|
||||
let addImageToBaseSpy: jest.SpyInstance;
|
||||
let addImageToPrevSpy: jest.SpyInstance;
|
||||
|
||||
const getManifest = (fileName: string): RepoImageMeta[] => {
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
return baseManifest;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
return prevManifest;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
|
||||
const adjustedContent = getAdjustedContent(fileName, content);
|
||||
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
baseManifest = adjustedContent;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
prevManifest = adjustedContent;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const resetManifests = (): void => {
|
||||
baseManifest = [];
|
||||
prevManifest = [];
|
||||
};
|
||||
|
||||
const withBody = (body: string): Partial<Context> => {
|
||||
const newContext = structuredClone(context);
|
||||
|
||||
newContext.payload!.pull_request!.body = body;
|
||||
|
||||
return newContext;
|
||||
};
|
||||
|
||||
const expectNoChanges = (noReadManifest: boolean = false): void => {
|
||||
if (noReadManifest) {
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(0);
|
||||
} else {
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
}
|
||||
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(0);
|
||||
};
|
||||
|
||||
beforeAll(() => {});
|
||||
|
||||
afterAll(() => {
|
||||
readManifestSpy.mockRestore();
|
||||
writeManifestSpy.mockRestore();
|
||||
addImageToBaseSpy.mockRestore();
|
||||
addImageToPrevSpy.mockRestore();
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetManifests();
|
||||
|
||||
readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest);
|
||||
writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest);
|
||||
addImageToBaseSpy = jest.spyOn(common, 'addImageToBase');
|
||||
addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
// XXX: Util
|
||||
// it('Get headers', async () => {
|
||||
// const firmwareBuffer = readFileSync(getImageOriginalDirPath(IMAGE_V14_1));
|
||||
// console.log(IMAGE_V14_1);
|
||||
// console.log(JSON.stringify(common.parseImageHeader(firmwareBuffer)));
|
||||
// console.log(`URL: ${common.getRepoFirmwareFileUrl(IMAGES_TEST_DIR, IMAGE_V14_1, common.BASE_IMAGES_DIR)}`);
|
||||
// console.log(`SHA512: ${common.computeSHA512(firmwareBuffer)}`);
|
||||
// })
|
||||
|
||||
it('hard failure from outside PR context', async () => {
|
||||
const fileParam: string = `images/test.ota`;
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, {payload: {}}, fileParam);
|
||||
}).rejects.toThrow(`Not a pull request`);
|
||||
|
||||
expectNoChanges(true);
|
||||
});
|
||||
|
||||
it('hard failure without fileParam', async () => {
|
||||
// NOTE: this path should always be prevented by workflow `paths` filter
|
||||
const fileParam: string = '';
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(`No file found in pull request.`);
|
||||
|
||||
expectNoChanges(true);
|
||||
});
|
||||
|
||||
it('failure with images in images1', async () => {
|
||||
const fileParam: string = `images1/test2.ota,images/test.ota`;
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected files in 'images1'`)}));
|
||||
|
||||
expectNoChanges(true);
|
||||
expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure with edited manifest', async () => {
|
||||
const fileParam: string = `index.json,images/test.ota`;
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected manual changes in index.json`)}));
|
||||
|
||||
expectNoChanges(true);
|
||||
expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure when no subfolder (manufacturer)', async () => {
|
||||
const fileParam: string = `images/test.ota`;
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`\`images/test.ota\` should be in its associated manufacturer subfolder.`)}),
|
||||
);
|
||||
|
||||
expectNoChanges(false);
|
||||
expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure with invalid OTA file', async () => {
|
||||
const fileParam: string = useImage(IMAGE_INVALID);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Not a valid OTA file`)}));
|
||||
|
||||
expectNoChanges(false);
|
||||
expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure with identical OTA file', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Conflict with image at index \`0\``)}));
|
||||
|
||||
expectNoChanges(false);
|
||||
expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure with older OTA file that has identical in prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
const fileParam: string = useImage(IMAGE_V13_1);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}),
|
||||
);
|
||||
|
||||
expectNoChanges(false);
|
||||
expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('failure with older OTA file that has newer in prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
const fileParam: string = useImage(IMAGE_V12_1);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}),
|
||||
);
|
||||
|
||||
expectNoChanges(false);
|
||||
expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('success into base', async () => {
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
});
|
||||
|
||||
it('success into prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
|
||||
const fileParam: string = useImage(IMAGE_V13_1);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
});
|
||||
|
||||
it('success with newer than current without existing prev', async () => {
|
||||
const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(',');
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, relocates first during second processing
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
});
|
||||
|
||||
it('success with newer than current with existing prev', async () => {
|
||||
const fileParam: string = [useImage(IMAGE_V12_1), useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(',');
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(3); // adds both, relocates first during second processing
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
});
|
||||
|
||||
it('success with older that is newer than prev', async () => {
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]);
|
||||
const fileParam: string = [useImage(IMAGE_V14_1), useImage(IMAGE_V13_1)].join(',');
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
});
|
||||
|
||||
it('success with newer with missing file', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
const fileParam: string = [useImage(IMAGE_V14_1)].join(',');
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
});
|
||||
|
||||
it('success with multiple different files', async () => {
|
||||
const fileParam: string = [useImage(IMAGE_V14_2), useImage(IMAGE_V14_1)].join(',');
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, context, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, relocates first during second processing
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_2_METAS, IMAGE_V14_1_METAS]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
});
|
||||
|
||||
it('success with extra metas', async () => {
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": ["lixee"]} \`\`\` Text after end tag`);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee']}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('success with all extra metas', async () => {
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
const newContext = withBody(`Text before start tag
|
||||
\`\`\`json
|
||||
{
|
||||
"force": false,
|
||||
"hardwareVersionMax": 2,
|
||||
"hardwareVersionMin": 1,
|
||||
"manufacturerName": ["lixee"],
|
||||
"maxFileVersion": 5,
|
||||
"minFileVersion": 3,
|
||||
"modelId": "bogus",
|
||||
"releaseNotes": "bugfixes"
|
||||
}
|
||||
\`\`\`
|
||||
Text after end tag`);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {
|
||||
force: false,
|
||||
hardwareVersionMax: 2,
|
||||
hardwareVersionMin: 1,
|
||||
manufacturerName: ['lixee'],
|
||||
maxFileVersion: 5,
|
||||
minFileVersion: 3,
|
||||
modelId: 'bogus',
|
||||
releaseNotes: 'bugfixes',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('failure with invalid extra metas', async () => {
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": "myManuf"} \`\`\` Text after end tag`);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
}).rejects.toThrow(
|
||||
expect.objectContaining({message: expect.stringContaining(`Invalid format for 'manufacturerName', expected 'array of string' type.`)}),
|
||||
);
|
||||
|
||||
expectNoChanges(true);
|
||||
expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['fileName'],
|
||||
['originalUrl'],
|
||||
['force'],
|
||||
['hardwareVersionMax'],
|
||||
['hardwareVersionMin'],
|
||||
['manufacturerName'],
|
||||
['maxFileVersion'],
|
||||
['minFileVersion'],
|
||||
['modelId'],
|
||||
['releaseNotes'],
|
||||
])('failure with invalid type for extra meta %s', async (metaName) => {
|
||||
const fileParam: string = useImage(IMAGE_V14_1);
|
||||
// use object since no value type is ever expected to be object
|
||||
const newContext = withBody(`Text before start tag \`\`\`json {"${metaName}": {}} \`\`\` Text after end tag`);
|
||||
|
||||
await expect(async () => {
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Invalid format for '${metaName}'`)}));
|
||||
|
||||
expectNoChanges(true);
|
||||
expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('success with multiple files and specific extra metas', async () => {
|
||||
const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(',');
|
||||
const newContext = withBody(`Text before start tag
|
||||
\`\`\`json
|
||||
[
|
||||
{"fileName": "${IMAGE_V14_1}", "manufacturerName": ["lixee"], "hardwareVersionMin": 2},
|
||||
{"fileName": "${IMAGE_V13_1}", "manufacturerName": ["lixee"]}
|
||||
]
|
||||
\`\`\`
|
||||
Text after end tag`);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee'], hardwareVersionMin: 2}),
|
||||
]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V13_1_METAS, {manufacturerName: ['lixee']}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('success with multiple files and specific extra metas, ignore without fileName', async () => {
|
||||
const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(',');
|
||||
const newContext = withBody(`Text before start tag
|
||||
\`\`\`json
|
||||
[
|
||||
{"fileName": "${IMAGE_V14_1}", "manufacturerName": ["lixee"], "hardwareVersionMin": 2},
|
||||
{"manufacturerName": ["lixee"]}
|
||||
]
|
||||
\`\`\`
|
||||
Text after end tag`);
|
||||
|
||||
// @ts-expect-error mock
|
||||
await updateOtaPR(github, core, newContext, fileParam);
|
||||
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee'], hardwareVersionMin: 2}),
|
||||
]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
});
|
||||
});
|
||||
BIN
tests/images/45856_00000006.tar.gz
Normal file
BIN
tests/images/45856_00000006.tar.gz
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v12.ota
Normal file
BIN
tests/images/ZLinky_router_v12.ota
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v12_limited.ota
Normal file
BIN
tests/images/ZLinky_router_v12_limited.ota
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v13.ota
Normal file
BIN
tests/images/ZLinky_router_v13.ota
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v13_limited.ota
Normal file
BIN
tests/images/ZLinky_router_v13_limited.ota
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v14.ota
Normal file
BIN
tests/images/ZLinky_router_v14.ota
Normal file
Binary file not shown.
BIN
tests/images/ZLinky_router_v14_limited.ota
Normal file
BIN
tests/images/ZLinky_router_v14_limited.ota
Normal file
Binary file not shown.
1
tests/images/not-a-valid-file.ota
Normal file
1
tests/images/not-a-valid-file.ota
Normal file
@@ -0,0 +1 @@
|
||||
this is not a valid OTA file
|
||||
216
tests/jest.config.ts
Normal file
216
tests/jest.config.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* For a detailed explanation regarding each configuration property, visit:
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
// import type {Config} from 'jest';
|
||||
|
||||
import {createDefaultEsmPreset, JestConfigWithTsJest} from 'ts-jest';
|
||||
|
||||
const defaultEsmPreset = createDefaultEsmPreset();
|
||||
|
||||
const config: JestConfigWithTsJest = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
cacheDirectory: '.jest-tmp',
|
||||
|
||||
// Automatically clear mock calls, instances, contexts and results before every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
collectCoverageFrom: ['src/ghw_update_ota_pr.ts', 'src/process_firmware_image.ts', 'src/ghw_reprocess_all_images.ts'],
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: 'coverage',
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: 'babel',
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
'lcov',
|
||||
// "clover"
|
||||
],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 100,
|
||||
functions: 100,
|
||||
lines: 100,
|
||||
statements: 100,
|
||||
},
|
||||
},
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// The default configuration for fake timers
|
||||
// fakeTimers: {
|
||||
// "enableGlobally": false
|
||||
// },
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
maxWorkers: '50%',
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
moduleFileExtensions: [
|
||||
// commonly used first
|
||||
'ts',
|
||||
'json',
|
||||
'js',
|
||||
'mjs',
|
||||
'cjs',
|
||||
'jsx',
|
||||
'tsx',
|
||||
'node',
|
||||
],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: undefined,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state before every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state and implementation before every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
rootDir: '..',
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
// testEnvironment: "jest-environment-node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
// testMatch: [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jest-circus/runner",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\",
|
||||
// "\\.pnp\\.[^\\\\]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
|
||||
...defaultEsmPreset,
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
401
tests/process_firmware_image.test.ts
Normal file
401
tests/process_firmware_image.test.ts
Normal file
@@ -0,0 +1,401 @@
|
||||
import type {RepoImageMeta} from '../src/types';
|
||||
|
||||
import {existsSync, mkdirSync, readFileSync, rmSync} from 'fs';
|
||||
|
||||
import * as common from '../src/common';
|
||||
import {processFirmwareImage, ProcessFirmwareImageStatus} from '../src/process_firmware_image';
|
||||
import {
|
||||
BASE_IMAGES_TEST_DIR_PATH,
|
||||
getAdjustedContent,
|
||||
getImageOriginalDirPath,
|
||||
IMAGE_INVALID,
|
||||
IMAGE_TAR,
|
||||
IMAGE_TAR_METAS,
|
||||
IMAGE_V12_1,
|
||||
IMAGE_V12_1_METAS,
|
||||
IMAGE_V13_1,
|
||||
IMAGE_V13_1_METAS,
|
||||
IMAGE_V14_1,
|
||||
IMAGE_V14_1_METAS,
|
||||
IMAGES_TEST_DIR,
|
||||
PREV_IMAGES_TEST_DIR_PATH,
|
||||
useImage,
|
||||
withExtraMetas,
|
||||
} from './data.test';
|
||||
|
||||
describe('Process Firmware Image', () => {
|
||||
let baseManifest: RepoImageMeta[];
|
||||
let prevManifest: RepoImageMeta[];
|
||||
let consoleErrorSpy: jest.SpyInstance;
|
||||
let consoleLogSpy: jest.SpyInstance;
|
||||
let readManifestSpy: jest.SpyInstance;
|
||||
let writeManifestSpy: jest.SpyInstance;
|
||||
let addImageToBaseSpy: jest.SpyInstance;
|
||||
let addImageToPrevSpy: jest.SpyInstance;
|
||||
let fetchSpy: jest.SpyInstance;
|
||||
let setTimeoutSpy: jest.SpyInstance;
|
||||
let fetchReturnedStatus: {ok: boolean; status: number; body?: object} = {ok: true, status: 200, body: {}};
|
||||
|
||||
const getManifest = (fileName: string): RepoImageMeta[] => {
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
return baseManifest;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
return prevManifest;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
|
||||
const adjustedContent = getAdjustedContent(fileName, content);
|
||||
|
||||
if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) {
|
||||
baseManifest = adjustedContent;
|
||||
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
|
||||
prevManifest = adjustedContent;
|
||||
} else {
|
||||
throw new Error(`${fileName} not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
const resetManifests = (): void => {
|
||||
baseManifest = [];
|
||||
prevManifest = [];
|
||||
};
|
||||
|
||||
const withOriginalUrl = (originalUrl: string, meta: RepoImageMeta): RepoImageMeta => {
|
||||
const newMeta = structuredClone(meta);
|
||||
|
||||
newMeta.originalUrl = originalUrl;
|
||||
|
||||
return newMeta;
|
||||
};
|
||||
|
||||
const expectNoChanges = (noReadManifest: boolean = false): void => {
|
||||
if (noReadManifest) {
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(0);
|
||||
} else {
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
}
|
||||
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(0);
|
||||
};
|
||||
|
||||
const expectWriteNoChanges = (inBase: boolean = true, inPrev: boolean = true): void => {
|
||||
if (inBase) {
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
common.PREV_INDEX_MANIFEST_FILENAME,
|
||||
getManifest(common.PREV_INDEX_MANIFEST_FILENAME),
|
||||
);
|
||||
}
|
||||
|
||||
if (inPrev) {
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
common.BASE_INDEX_MANIFEST_FILENAME,
|
||||
getManifest(common.BASE_INDEX_MANIFEST_FILENAME),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(() => {});
|
||||
|
||||
afterAll(() => {
|
||||
consoleErrorSpy.mockRestore();
|
||||
consoleLogSpy.mockRestore();
|
||||
readManifestSpy.mockRestore();
|
||||
writeManifestSpy.mockRestore();
|
||||
addImageToBaseSpy.mockRestore();
|
||||
addImageToPrevSpy.mockRestore();
|
||||
fetchSpy.mockRestore();
|
||||
setTimeoutSpy.mockRestore();
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetManifests();
|
||||
|
||||
fetchReturnedStatus = {ok: true, status: 200, body: {}};
|
||||
consoleErrorSpy = jest.spyOn(console, 'error');
|
||||
consoleLogSpy = jest.spyOn(console, 'log');
|
||||
readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest);
|
||||
writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest);
|
||||
addImageToBaseSpy = jest.spyOn(common, 'addImageToBase');
|
||||
addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev');
|
||||
fetchSpy = jest.spyOn(global, 'fetch').mockImplementation(
|
||||
// @ts-expect-error mocked as needed
|
||||
(input) => {
|
||||
return {
|
||||
ok: fetchReturnedStatus.ok,
|
||||
status: fetchReturnedStatus.status,
|
||||
body: fetchReturnedStatus.body,
|
||||
arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath(input as string)),
|
||||
};
|
||||
},
|
||||
);
|
||||
setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(
|
||||
// @ts-expect-error mock
|
||||
(fn) => {
|
||||
fn();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it('failure with fetch ok', async () => {
|
||||
fetchReturnedStatus.ok = false;
|
||||
fetchReturnedStatus.status = 429;
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.REQUEST_FAILED);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`Invalid response from ${IMAGE_V14_1} status=${fetchReturnedStatus.status}.`),
|
||||
);
|
||||
expectNoChanges(false);
|
||||
});
|
||||
|
||||
it('failure with fetch body', async () => {
|
||||
fetchReturnedStatus.body = undefined;
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.REQUEST_FAILED);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`Invalid response from ${IMAGE_V14_1} status=${fetchReturnedStatus.status}.`),
|
||||
);
|
||||
expectNoChanges(false);
|
||||
});
|
||||
|
||||
it('failure with invalid OTA file', async () => {
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_INVALID, IMAGE_INVALID);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.ERROR);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining(`Not a valid OTA fil`));
|
||||
expectNoChanges(false);
|
||||
});
|
||||
|
||||
it('failure with identical OTA file', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(`Base manifest already has version`));
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, getManifest(common.PREV_INDEX_MANIFEST_FILENAME));
|
||||
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, getManifest(common.BASE_INDEX_MANIFEST_FILENAME));
|
||||
expectWriteNoChanges();
|
||||
});
|
||||
|
||||
it('failure with older OTA file that has identical in prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V13_1, IMAGE_V13_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(`an equal or better match is already present in prev manifest`));
|
||||
expectWriteNoChanges();
|
||||
});
|
||||
|
||||
it('failure with older OTA file that has newer in prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V12_1, IMAGE_V12_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(`an equal or better match is already present in prev manifest`));
|
||||
expectWriteNoChanges();
|
||||
});
|
||||
|
||||
it('success into base', async () => {
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
});
|
||||
|
||||
it('success into prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V13_1, IMAGE_V13_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expectWriteNoChanges(true, false);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
});
|
||||
|
||||
it('success with newer than current without existing prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
useImage(IMAGE_V13_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
});
|
||||
|
||||
it('success with newer than current with existing prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V12_1, IMAGE_V12_1_METAS)]);
|
||||
useImage(IMAGE_V13_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
useImage(IMAGE_V12_1, PREV_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
});
|
||||
|
||||
it('success with older that is newer than prev', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V12_1, IMAGE_V12_1_METAS)]);
|
||||
useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
useImage(IMAGE_V12_1, PREV_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V13_1, IMAGE_V13_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
});
|
||||
|
||||
it('success with newer with missing file', async () => {
|
||||
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
|
||||
// useImage(IMAGE_V13_1, BASE_IMAGES_TEST_DIR_PATH);
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
|
||||
});
|
||||
|
||||
it('success with extra metas', async () => {
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1, {manufacturerName: ['lixee']});
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withOriginalUrl(IMAGE_V14_1, withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee']})),
|
||||
]);
|
||||
});
|
||||
|
||||
it('success with all extra metas', async () => {
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1, {
|
||||
originalUrl: `https://example.com/${IMAGE_V14_1}`,
|
||||
force: false,
|
||||
hardwareVersionMax: 2,
|
||||
hardwareVersionMin: 1,
|
||||
manufacturerName: ['lixee'],
|
||||
maxFileVersion: 5,
|
||||
minFileVersion: 3,
|
||||
modelId: 'bogus',
|
||||
releaseNotes: 'bugfixes',
|
||||
});
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
|
||||
withOriginalUrl(
|
||||
`https://example.com/${IMAGE_V14_1}`,
|
||||
withExtraMetas(IMAGE_V14_1_METAS, {
|
||||
force: false,
|
||||
hardwareVersionMax: 2,
|
||||
hardwareVersionMin: 1,
|
||||
manufacturerName: ['lixee'],
|
||||
maxFileVersion: 5,
|
||||
minFileVersion: 3,
|
||||
modelId: 'bogus',
|
||||
releaseNotes: 'bugfixes',
|
||||
}),
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('success with tar', async () => {
|
||||
if (!existsSync(common.TMP_DIR)) {
|
||||
mkdirSync(common.TMP_DIR, {recursive: true});
|
||||
}
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_TAR, IMAGE_TAR, {}, true, (f) => f.endsWith('.ota'));
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
|
||||
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
|
||||
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
|
||||
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
|
||||
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_TAR, IMAGE_TAR_METAS)]);
|
||||
|
||||
rmSync(common.TMP_DIR, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it('failure with invalid tar', async () => {
|
||||
if (!existsSync(common.TMP_DIR)) {
|
||||
mkdirSync(common.TMP_DIR, {recursive: true});
|
||||
}
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_INVALID, IMAGE_INVALID, {}, true, (f) => f.endsWith('.ota'));
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.TAR_NO_IMAGE);
|
||||
expectNoChanges(true);
|
||||
|
||||
rmSync(common.TMP_DIR, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it('failure with extract tar (missing dir)', async () => {
|
||||
// if (!existsSync(common.TMP_DIR)) {
|
||||
// mkdirSync(common.TMP_DIR, {recursive: true});
|
||||
// }
|
||||
|
||||
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_TAR, IMAGE_TAR, {}, true, (f) => f.endsWith('.ota'));
|
||||
|
||||
expect(status).toStrictEqual(ProcessFirmwareImageStatus.TAR_NO_IMAGE);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.objectContaining({syscall: 'chdir', code: 'ENOENT'}));
|
||||
expectNoChanges(false);
|
||||
|
||||
rmSync(common.TMP_DIR, {recursive: true, force: true});
|
||||
});
|
||||
});
|
||||
10
tests/tsconfig.json
Normal file
10
tests/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../tsconfig",
|
||||
"include": ["./**/*", "./jest.config.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["jest"],
|
||||
"rootDir": "..",
|
||||
"noEmit": true
|
||||
},
|
||||
"references": [{"path": ".."}]
|
||||
}
|
||||
Reference in New Issue
Block a user