fix: Switch to biome & vitest (#726)

This commit is contained in:
Nerivec
2025-03-23 21:30:14 +01:00
committed by GitHub
parent 44c5bbcc1c
commit 94b76a910a
48 changed files with 1762 additions and 4075 deletions

View File

@@ -5,23 +5,24 @@
*
*/
import type {ExtraMetas, RepoImageMeta} from '../src/types';
import type {ExtraMetas, RepoImageMeta} from "../src/types";
import {copyFileSync, existsSync, mkdirSync} from 'fs';
import path from 'path';
import {copyFileSync, existsSync, mkdirSync} from "node:fs";
import path from "node:path";
import * as common from '../src/common';
import {it} from "vitest";
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 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 = "test-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);
/**
@@ -44,8 +45,8 @@ export const IMAGE_V14_1_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images/${IMAGES_TEST_DIR}/${IMAGE_V14_1}`,
imageType: 1,
manufacturerCode: 4151,
sha512: 'cc69b0745c72daf8deda935ba47aa7abd34dfcaaa4bc35bfa0605cd7937b0ecd8582ba0c08110df4f620c8aa87798d201f407d3d7e17198cfef1a4aa13c5013d',
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
sha512: "cc69b0745c72daf8deda935ba47aa7abd34dfcaaa4bc35bfa0605cd7937b0ecd8582ba0c08110df4f620c8aa87798d201f407d3d7e17198cfef1a4aa13c5013d",
otaHeaderString: "OM15081-RTR-JN5189-0000000000000",
};
/**
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
@@ -67,8 +68,8 @@ export const IMAGE_V14_2_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images/${IMAGES_TEST_DIR}/${IMAGE_V14_2}`,
imageType: 2,
manufacturerCode: 4151,
sha512: 'f851cbff7297ba6223a969ba8da5182f9ef199cf9c8459c8408432e48485c1a8f018f6e1703a42f40143cccd3bf460c0acd92117d899e507a36845f24e970595',
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
sha512: "f851cbff7297ba6223a969ba8da5182f9ef199cf9c8459c8408432e48485c1a8f018f6e1703a42f40143cccd3bf460c0acd92117d899e507a36845f24e970595",
otaHeaderString: "OM15081-RTR-LIMITED-JN5189-00000",
};
/**
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
@@ -90,8 +91,8 @@ export const IMAGE_V13_1_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images1/${IMAGES_TEST_DIR}/${IMAGE_V13_1}`,
imageType: 1,
manufacturerCode: 4151,
sha512: '4d7ab47dcb24e478e0abb35e691222b7691e77ed5a56de3f9c82e8682730649b1a154110b7207d4391c32eae53a869e20878e880fc153dbe046690b870be8486',
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
sha512: "4d7ab47dcb24e478e0abb35e691222b7691e77ed5a56de3f9c82e8682730649b1a154110b7207d4391c32eae53a869e20878e880fc153dbe046690b870be8486",
otaHeaderString: "OM15081-RTR-JN5189-0000000000000",
};
/**
@@ -121,8 +122,8 @@ export const IMAGE_V13_2_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images1/${IMAGES_TEST_DIR}/${IMAGE_V13_2}`,
imageType: 2,
manufacturerCode: 4151,
sha512: 'dd77b28a3b4664e7ad944fcffaa9eca9f3adb0bbe598e12bdd6eece8070a8cdda6792bed378d173dd5b4532b4cdb88cebda0ef0c432c4c4d6581aa9f2bbba54d',
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
sha512: "dd77b28a3b4664e7ad944fcffaa9eca9f3adb0bbe598e12bdd6eece8070a8cdda6792bed378d173dd5b4532b4cdb88cebda0ef0c432c4c4d6581aa9f2bbba54d",
otaHeaderString: "OM15081-RTR-LIMITED-JN5189-00000",
};
/**
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
@@ -144,8 +145,8 @@ export const IMAGE_V12_1_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images1/${IMAGES_TEST_DIR}/${IMAGE_V12_1}`,
imageType: 1,
manufacturerCode: 4151,
sha512: '5d7e0a20141b78b85b4b046e623bc2bba24b28563464fe70227e79d0acdd5c0bde2adbd9d2557bd6cdfef2036d964c35c9e1746a8f1356af3325dd96f7a80e56',
otaHeaderString: 'OM15081-RTR-JN5189-0000000000000',
sha512: "5d7e0a20141b78b85b4b046e623bc2bba24b28563464fe70227e79d0acdd5c0bde2adbd9d2557bd6cdfef2036d964c35c9e1746a8f1356af3325dd96f7a80e56",
otaHeaderString: "OM15081-RTR-JN5189-0000000000000",
};
/**
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
@@ -167,8 +168,8 @@ export const IMAGE_V12_2_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images1/${IMAGES_TEST_DIR}/${IMAGE_V12_2}`,
imageType: 2,
manufacturerCode: 4151,
sha512: '4e178e56c1559e11734c07abbb95110675df7738f3ca3e5dbc99393325295ff6c66bd63ba55c0ef6043a80608dbec2be7a1e845f31ffd94f1cb63f32f0d48c6e',
otaHeaderString: 'OM15081-RTR-LIMITED-JN5189-00000',
sha512: "4e178e56c1559e11734c07abbb95110675df7738f3ca3e5dbc99393325295ff6c66bd63ba55c0ef6043a80608dbec2be7a1e845f31ffd94f1cb63f32f0d48c6e",
otaHeaderString: "OM15081-RTR-LIMITED-JN5189-00000",
};
/** obviously bogus, just for mocking */
export const IMAGE_INVALID_METAS = {
@@ -179,8 +180,8 @@ export const IMAGE_INVALID_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/images/${IMAGES_TEST_DIR}/${IMAGE_INVALID}`,
imageType: 1,
manufacturerCode: 65535,
sha512: 'abcd',
otaHeaderString: 'nothing',
sha512: "abcd",
otaHeaderString: "nothing",
};
/**
* - otaUpgradeFileIdentifier: <Buffer 1e f1 ee 0b>,
@@ -202,12 +203,13 @@ export const IMAGE_TAR_METAS = {
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/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',
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);
// allow running in vitest explorer
return path.join(path.resolve().endsWith("tests") ? "." : "tests", common.BASE_IMAGES_DIR, imageName);
};
export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DIR_PATH): {filename: string} => {
@@ -220,7 +222,7 @@ export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DI
copyFileSync(getImageOriginalDirPath(imageName), realPath);
// return as posix for github match
return {filename: path.posix.join(outDir.replaceAll('\\', '/'), imageName)};
return {filename: path.posix.join(outDir.replaceAll("\\", "/"), imageName)};
};
export const withExtraMetas = (meta: RepoImageMeta, extraMetas: ExtraMetas): RepoImageMeta => {
@@ -234,7 +236,9 @@ export const getAdjustedContent = (fileName: string, content: RepoImageMeta[]):
// @ts-expect-error override
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/${common.BASE_IMAGES_DIR}/${IMAGES_TEST_DIR}/${c.fileName}`,
});
} else if (fileName === common.PREV_INDEX_MANIFEST_FILENAME && c.url.includes(`${common.BASE_IMAGES_DIR}`)) {
}
if (fileName === common.PREV_INDEX_MANIFEST_FILENAME && c.url.includes(`${common.BASE_IMAGES_DIR}`)) {
return withExtraMetas(c, {
// @ts-expect-error override
url: `${common.BASE_REPO_URL}${common.REPO_BRANCH}/${common.PREV_IMAGES_DIR}/${IMAGES_TEST_DIR}/${c.fileName}`,
@@ -246,4 +250,4 @@ export const getAdjustedContent = (fileName: string, content: RepoImageMeta[]):
};
// required to consider as a 'test suite'
it('passes', () => {});
it("passes", () => {});

View File

@@ -1,17 +1,18 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
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 type {RepoImageMeta} from "../src/types";
import {existsSync, readFileSync, rmSync} from 'fs';
import path from 'path';
import {existsSync, readFileSync, rmSync} from "node:fs";
import path from "node:path";
import * as common from '../src/common';
import {checkOtaPR} from '../src/ghw_check_ota_pr';
import {type MockInstance, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import * as common from "../src/common";
import {checkOtaPR} from "../src/ghw_check_ota_pr";
import {
BASE_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
IMAGES_TEST_DIR,
IMAGE_INVALID,
IMAGE_V12_1,
IMAGE_V12_1_METAS,
@@ -22,20 +23,21 @@ import {
IMAGE_V14_1_METAS,
IMAGE_V14_2,
IMAGE_V14_2_METAS,
IMAGES_TEST_DIR,
PREV_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
useImage,
withExtraMetas,
} from './data.test';
} from "./data.test";
const github = {
rest: {
repos: {
compareCommitsWithBasehead: jest.fn<
ReturnType<Octokit['rest']['repos']['compareCommitsWithBasehead']>,
Parameters<Octokit['rest']['repos']['compareCommitsWithBasehead']>,
unknown
>(),
compareCommitsWithBasehead:
vi.fn<
(
...args: Parameters<Octokit["rest"]["repos"]["compareCommitsWithBasehead"]>
) => ReturnType<Octokit["rest"]["repos"]["compareCommitsWithBasehead"]>
>(),
},
},
};
@@ -45,49 +47,51 @@ const core: Partial<typeof CoreApi> = {
warning: console.warn,
error: console.error,
notice: console.log,
startGroup: jest.fn(),
endGroup: jest.fn(),
startGroup: vi.fn(),
endGroup: vi.fn(),
};
const context: Partial<Context> = {
payload: {
pull_request: {
number: 1,
head: {
sha: 'abcd',
sha: "abcd",
},
base: {
sha: 'zyxw',
sha: "zyxw",
},
},
},
issue: {
owner: 'Koenkk',
repo: 'zigbee-OTA',
owner: "Koenkk",
repo: "zigbee-OTA",
number: 1,
},
repo: {
owner: 'Koenkk',
repo: 'zigbee-OTA',
owner: "Koenkk",
repo: "zigbee-OTA",
},
};
describe('Github Workflow: Check OTA PR', () => {
describe("Github Workflow: Check OTA PR", () => {
let baseManifest: RepoImageMeta[];
let prevManifest: RepoImageMeta[];
let readManifestSpy: jest.SpyInstance;
let writeManifestSpy: jest.SpyInstance;
let addImageToBaseSpy: jest.SpyInstance;
let addImageToPrevSpy: jest.SpyInstance;
let readManifestSpy: MockInstance;
let writeManifestSpy: MockInstance;
let addImageToBaseSpy: MockInstance;
let addImageToPrevSpy: MockInstance;
let filePaths: ReturnType<typeof useImage>[] = [];
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`);
}
if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
return prevManifest;
}
throw new Error(`${fileName} not supported`);
};
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
@@ -115,7 +119,7 @@ describe('Github Workflow: Check OTA PR', () => {
return newContext;
};
const expectNoChanges = (noReadManifest: boolean = false): void => {
const expectNoChanges = (noReadManifest = false): void => {
if (noReadManifest) {
expect(readManifestSpy).toHaveBeenCalledTimes(0);
} else {
@@ -136,16 +140,17 @@ describe('Github Workflow: Check OTA PR', () => {
addImageToPrevSpy.mockRestore();
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
rmSync(IMAGES_TEST_DIR, {recursive: true, force: true});
});
beforeEach(() => {
resetManifests();
filePaths = [];
readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest);
writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest);
addImageToBaseSpy = jest.spyOn(common, 'addImageToBase');
addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev');
readManifestSpy = vi.spyOn(common, "readManifest").mockImplementation(getManifest);
writeManifestSpy = vi.spyOn(common, "writeManifest").mockImplementation(setManifest);
addImageToBaseSpy = vi.spyOn(common, "addImageToBase");
addImageToPrevSpy = vi.spyOn(common, "addImageToPrev");
github.rest.repos.compareCommitsWithBasehead.mockImplementation(
// @ts-expect-error mock
() => ({data: {files: filePaths}}),
@@ -167,92 +172,92 @@ describe('Github Workflow: Check OTA PR', () => {
// console.log(`SHA512: ${common.computeSHA512(firmwareBuffer)}`);
// })
it('hard failure from outside PR context', async () => {
it("hard failure from outside PR context", async () => {
filePaths = [useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, {payload: {}});
}).rejects.toThrow(`Not a pull request`);
}).rejects.toThrow("Not a pull request");
expectNoChanges(true);
});
it('hard failure from merged PR context', async () => {
it("hard failure from merged PR context", async () => {
filePaths = [useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, {payload: {pull_request: {merged: true}}});
}).rejects.toThrow(`Should not be executed on a merged pull request`);
}).rejects.toThrow("Should not be executed on a merged pull request");
expectNoChanges(true);
});
it('hard failure with no file changed', async () => {
it("hard failure with no file changed", async () => {
filePaths = [];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(`No file`);
}).rejects.toThrow("No file");
expectNoChanges(false);
expect(existsSync(common.PR_ARTIFACT_NUMBER_FILEPATH)).toStrictEqual(true);
expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, 'utf8')).toStrictEqual(`${context.payload?.pull_request?.number}`);
expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, "utf8")).toStrictEqual(`${context.payload?.pull_request?.number}`);
expect(existsSync(common.PR_ARTIFACT_DIFF_FILEPATH)).toStrictEqual(false);
expect(existsSync(common.PR_ARTIFACT_ERROR_FILEPATH)).toStrictEqual(true);
expect(readFileSync(common.PR_ARTIFACT_ERROR_FILEPATH, 'utf8')).toStrictEqual(`No file`);
expect(readFileSync(common.PR_ARTIFACT_ERROR_FILEPATH, "utf8")).toStrictEqual("No file");
});
it('failure with file outside of images directory', async () => {
it("failure with file outside of images directory", async () => {
filePaths = [useImage(IMAGE_V13_1, PREV_IMAGES_TEST_DIR_PATH), useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected changes in files outside`)}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("Detected changes in files outside")}));
expectNoChanges(false);
});
it('failure when no manufacturer subfolder', async () => {
it("failure when no manufacturer subfolder", async () => {
filePaths = [useImage(IMAGE_V14_1, common.BASE_IMAGES_DIR)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`File should be in its associated manufacturer subfolder`)}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("File should be in its associated manufacturer subfolder")}));
expectNoChanges(false);
rmSync(path.join(common.BASE_IMAGES_DIR, IMAGE_V14_1), {force: true});
});
it('failure with invalid OTA file', async () => {
it("failure with invalid OTA file", async () => {
filePaths = [useImage(IMAGE_INVALID)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Not a valid OTA file`)}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("Not a valid OTA file")}));
expectNoChanges(false);
});
it('failure with identical OTA file', async () => {
it("failure with identical OTA file", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
filePaths = [useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Conflict with image at index \`0\``)}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("Conflict with image at index `0`")}));
expectNoChanges(false);
});
it('failure with older OTA file that has identical in prev', async () => {
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]);
filePaths = [useImage(IMAGE_V13_1)];
@@ -261,13 +266,13 @@ describe('Github Workflow: Check OTA PR', () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(
expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}),
expect.objectContaining({message: expect.stringContaining("an equal or better match is already present in prev manifest")}),
);
expectNoChanges(false);
});
it('failure with older OTA file that has newer in prev', async () => {
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]);
filePaths = [useImage(IMAGE_V12_1)];
@@ -276,13 +281,13 @@ describe('Github Workflow: Check OTA PR', () => {
// @ts-expect-error mock
await checkOtaPR(github, core, context);
}).rejects.toThrow(
expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}),
expect.objectContaining({message: expect.stringContaining("an equal or better match is already present in prev manifest")}),
);
expectNoChanges(false);
});
it('success into base', async () => {
it("success into base", async () => {
filePaths = [useImage(IMAGE_V14_1)];
// @ts-expect-error mock
@@ -295,12 +300,12 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
expect(existsSync(common.PR_ARTIFACT_NUMBER_FILEPATH)).toStrictEqual(true);
expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, 'utf8')).toStrictEqual(`${context.payload?.pull_request?.number}`);
expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, "utf8")).toStrictEqual(`${context.payload?.pull_request?.number}`);
expect(existsSync(common.PR_ARTIFACT_DIFF_FILEPATH)).toStrictEqual(true);
expect(existsSync(common.PR_ARTIFACT_ERROR_FILEPATH)).toStrictEqual(false);
});
it('success into prev', async () => {
it("success into prev", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
filePaths = [useImage(IMAGE_V13_1)];
@@ -316,7 +321,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
});
it('success with newer than current without existing prev', async () => {
it("success with newer than current without existing prev", async () => {
filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
// @ts-expect-error mock
@@ -330,7 +335,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
});
it('success with newer than current with existing prev', async () => {
it("success with newer than current with existing prev", async () => {
filePaths = [useImage(IMAGE_V12_1), useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
// @ts-expect-error mock
@@ -344,7 +349,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
});
it('success with older that is newer than prev', async () => {
it("success with older that is newer than prev", async () => {
setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]);
filePaths = [useImage(IMAGE_V14_1), useImage(IMAGE_V13_1)];
@@ -359,7 +364,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
});
it('success with newer with missing file', async () => {
it("success with newer with missing file", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]);
filePaths = [useImage(IMAGE_V14_1)];
@@ -374,7 +379,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
});
it('success with multiple different files', async () => {
it("success with multiple different files", async () => {
filePaths = [useImage(IMAGE_V14_2), useImage(IMAGE_V14_1)];
// @ts-expect-error mock
@@ -388,7 +393,7 @@ describe('Github Workflow: Check OTA PR', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
});
it('success with extra metas', async () => {
it("success with extra metas", async () => {
filePaths = [useImage(IMAGE_V14_1)];
const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": ["lixee"]} \`\`\` Text after end tag`);
@@ -401,11 +406,11 @@ describe('Github Workflow: Check OTA PR', () => {
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee']}),
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ["lixee"]}),
]);
});
it('success with all extra metas', async () => {
it("success with all extra metas", async () => {
filePaths = [useImage(IMAGE_V14_1)];
const newContext = withBody(`Text before start tag
\`\`\`json
@@ -435,16 +440,16 @@ Text after end tag`);
force: false,
hardwareVersionMax: 2,
hardwareVersionMin: 1,
manufacturerName: ['lixee'],
manufacturerName: ["lixee"],
maxFileVersion: 5,
minFileVersion: 3,
modelId: 'bogus',
releaseNotes: 'bugfixes',
modelId: "bogus",
releaseNotes: "bugfixes",
}),
]);
});
it('success with newer than current but minFileVersion keeps both', async () => {
it("success with newer than current but minFileVersion keeps both", async () => {
filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
const newContext = withBody(
`Text before start tag \`\`\`json [{"fileName":"ZLinky_router_v14.ota", "minFileVersion": 16783874}] \`\`\` Text after end tag`,
@@ -467,7 +472,7 @@ Text after end tag`);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
});
it('success with newer than current but maxFileVersion keeps both', async () => {
it("success with newer than current but maxFileVersion keeps both", async () => {
filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
const newContext = withBody(
`Text before start tag \`\`\`json [{"fileName":"ZLinky_router_v13.ota", "maxFileVersion": 16783873}] \`\`\` Text after end tag`,
@@ -491,7 +496,7 @@ Text after end tag`);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
});
it('success with newer than current but maxFileVersion/minFileVersion keeps both', async () => {
it("success with newer than current but maxFileVersion/minFileVersion keeps both", async () => {
filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
const newContext = withBody(
`Text before start tag \`\`\`json [{"fileName":"ZLinky_router_v13.ota", "maxFileVersion": 16783873},{"fileName":"ZLinky_router_v14.ota", "minFileVersion": 16783874}] \`\`\` Text after end tag`,
@@ -515,7 +520,7 @@ Text after end tag`);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, []);
});
it('failure with invalid extra metas', async () => {
it("failure with invalid extra metas", async () => {
filePaths = [useImage(IMAGE_V14_1)];
const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": "myManuf"} \`\`\` Text after end tag`);
@@ -530,17 +535,17 @@ Text after end tag`);
});
it.each([
['fileName'],
['originalUrl'],
['force'],
['hardwareVersionMax'],
['hardwareVersionMin'],
['manufacturerName'],
['maxFileVersion'],
['minFileVersion'],
['modelId'],
['releaseNotes'],
])('failure with invalid type for extra meta %s', async (metaName) => {
["fileName"],
["originalUrl"],
["force"],
["hardwareVersionMax"],
["hardwareVersionMin"],
["manufacturerName"],
["maxFileVersion"],
["minFileVersion"],
["modelId"],
["releaseNotes"],
])("failure with invalid type for extra meta %s", async (metaName) => {
filePaths = [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`);
@@ -553,7 +558,7 @@ Text after end tag`);
expectNoChanges(false);
});
it('success with multiple files and specific extra metas', async () => {
it("success with multiple files and specific extra metas", async () => {
filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1), useImage(IMAGE_V12_1)];
const newContext = withBody(`Text before start tag
\`\`\`json
@@ -574,15 +579,15 @@ Text after end tag`);
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
withExtraMetas(IMAGE_V13_1_METAS_MAIN, {manufacturerName: ['lixee']}),
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee'], hardwareVersionMin: 2}),
withExtraMetas(IMAGE_V13_1_METAS_MAIN, {manufacturerName: ["lixee"]}),
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ["lixee"], hardwareVersionMin: 2}),
]);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [
withExtraMetas(IMAGE_V12_1_METAS, {manufacturerName: ['lixee']}),
withExtraMetas(IMAGE_V12_1_METAS, {manufacturerName: ["lixee"]}),
]);
});
it('success with multiple files and specific extra metas, ignore without fileName', async () => {
it("success with multiple files and specific extra metas, ignore without fileName", async () => {
filePaths = [useImage(IMAGE_V12_1), useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)];
const newContext = withBody(`Text before start tag
\`\`\`json
@@ -603,7 +608,7 @@ Text after end tag`);
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
IMAGE_V13_1_METAS_MAIN,
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee'], hardwareVersionMin: 2}),
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ["lixee"], hardwareVersionMin: 2}),
]);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]);
});

View File

@@ -1,29 +1,28 @@
// import type CoreApi from '@actions/core';
// import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type {Octokit} from "@octokit/rest";
import {describe, it, vi} from "vitest";
const github = {
rest: {
issues: {
createComment: jest.fn<
ReturnType<Octokit['rest']['issues']['createComment']>,
Parameters<Octokit['rest']['issues']['createComment']>,
unknown
>(),
createComment:
vi.fn<(...args: Parameters<Octokit["rest"]["issues"]["createComment"]>) => ReturnType<Octokit["rest"]["issues"]["createComment"]>>(),
},
pulls: {
createReviewComment: jest.fn<
ReturnType<Octokit['rest']['pulls']['createReviewComment']>,
Parameters<Octokit['rest']['pulls']['createReviewComment']>,
unknown
>(),
createReviewComment:
vi.fn<
(
...args: Parameters<Octokit["rest"]["pulls"]["createReviewComment"]>
) => ReturnType<Octokit["rest"]["pulls"]["createReviewComment"]>
>(),
},
},
};
describe('Github Workflow: Report OTA PR', () => {
it('passes', async () => {
describe("Github Workflow: Report OTA PR", () => {
it("passes", () => {
console.log(github);
});
});

View File

@@ -1,22 +1,22 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {RepoImageMeta} from '../src/types';
import type {RepoImageMeta} from "../src/types";
import {copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync} from 'fs';
import path from 'path';
import {copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync} from "node:fs";
import path from "node:path";
import * as common from '../src/common';
import {type MockInstance, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
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';
} from "../src/ghw_reprocess_all_images";
import {
BASE_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
getImageOriginalDirPath,
IMAGES_TEST_DIR,
IMAGE_INVALID,
IMAGE_INVALID_METAS,
IMAGE_V12_1,
@@ -24,11 +24,12 @@ import {
IMAGE_V13_1_METAS,
IMAGE_V14_1,
IMAGE_V14_1_METAS,
IMAGES_TEST_DIR,
PREV_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
getImageOriginalDirPath,
useImage,
withExtraMetas,
} from './data.test';
} from "./data.test";
/** not used */
const github = {};
@@ -38,14 +39,14 @@ const core: Partial<typeof CoreApi> = {
warning: console.warn,
error: console.error,
notice: console.log,
startGroup: jest.fn(),
endGroup: jest.fn(),
startGroup: vi.fn(),
endGroup: vi.fn(),
};
const context: Partial<Context> = {
payload: {},
repo: {
owner: 'Koenkk',
repo: 'zigbee-OTA',
owner: "Koenkk",
repo: "zigbee-OTA",
},
};
@@ -56,7 +57,7 @@ const OLD_META_3RD_PARTY_1_METAS = {
fileSize: 258104,
manufacturerCode: 4107,
imageType: 256,
sha512: 'c63a1eb02ac030f3a76d9e81a4d48695796457d263bb1dae483688134e550d9846c37a3fd0eab2d4670f12f11b79691a5cf2789af0dbd90d703512496190a0a5',
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}`,
};
@@ -67,8 +68,8 @@ const OLD_META_3RD_PARTY_2_METAS = {
fileSize: 307682,
manufacturerCode: 4417,
imageType: 54179,
modelId: 'TS011F',
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
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}`,
};
@@ -77,8 +78,8 @@ const OLD_META_3RD_PARTY_IGNORED_METAS = {
fileSize: 693230,
manufacturerCode: 13379,
imageType: 4113,
sha512: '66040fb2b2787bf8ebfc75bc3c7356c7d8b966b4c82282bd7393783b8dc453ec2c8dcb4d7c9fe7c0a83d87739bd3677f205d79edddfa4fa2749305ca987887b1',
url: 'https://github.com/xyzroe/ZigUSB_C6/releases/download/317/ZigUSB_C6.ota',
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);
@@ -88,30 +89,36 @@ const NOT_IN_PREV_MANIFEST_FILEPATH = path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR,
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', () => {
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;
let readManifestSpy: MockInstance;
let writeManifestSpy: MockInstance;
let addImageToBaseSpy: MockInstance;
let addImageToPrevSpy: MockInstance;
let coreWarningSpy: MockInstance;
let coreErrorSpy: MockInstance;
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`);
}
if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
return prevManifest;
}
if (fileName === path.join(NOT_IN_BASE_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
return notInBaseManifest;
}
if (fileName === path.join(NOT_IN_PREV_MANIFEST_IMAGES_DIR, NOT_IN_MANIFEST_FILENAME)) {
return notInPrevManifest;
}
throw new Error(`${fileName} not supported`);
};
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
@@ -137,8 +144,10 @@ describe('Github Workflow: Re-Process All Images', () => {
const withOldMetas = (metas: RepoImageMeta): RepoImageMeta => {
const oldMetas = structuredClone(metas);
// biome-ignore lint/performance/noDelete: <explanation>
delete oldMetas.originalUrl;
// @ts-expect-error mock
// biome-ignore lint/performance/noDelete: <explanation>
delete oldMetas.sha512;
return oldMetas;
@@ -179,17 +188,19 @@ describe('Github Workflow: Re-Process All Images', () => {
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);
}
rmSync(IMAGES_TEST_DIR, {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');
coreWarningSpy = jest.spyOn(core, 'warning');
coreErrorSpy = jest.spyOn(core, 'error');
readManifestSpy = vi.spyOn(common, "readManifest").mockImplementation(getManifest);
writeManifestSpy = vi.spyOn(common, "writeManifest").mockImplementation(setManifest);
addImageToBaseSpy = vi.spyOn(common, "addImageToBase");
addImageToPrevSpy = vi.spyOn(common, "addImageToPrev");
coreWarningSpy = vi.spyOn(core, "warning");
coreErrorSpy = vi.spyOn(core, "error");
});
afterEach(() => {
@@ -199,27 +210,27 @@ describe('Github Workflow: Re-Process All Images', () => {
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 () => {
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')}));
}).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 () => {
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')}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("is not empty")}));
});
it('failure when image not in subdirectory', async () => {
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);
@@ -239,7 +250,7 @@ describe('Github Workflow: Re-Process All Images', () => {
rmSync(outPath, {force: true});
});
it('removes image not in manifest', async () => {
it("removes image not in manifest", async () => {
const imagePath = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
// @ts-expect-error mocked as needed
@@ -250,10 +261,10 @@ describe('Github Workflow: Re-Process All Images', () => {
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:`));
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining("Not found in base manifest:"));
});
it('removes multiple images not in manifest', async () => {
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);
@@ -269,12 +280,12 @@ describe('Github Workflow: Re-Process All Images', () => {
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:`));
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 () => {
it("moves image not in manifest", async () => {
const oldPath = useImage(IMAGE_V12_1, BASE_IMAGES_TEST_DIR_PATH);
// @ts-expect-error mocked as needed
@@ -290,7 +301,7 @@ describe('Github Workflow: Re-Process All Images', () => {
expectWriteNoChange(3, common.BASE_INDEX_MANIFEST_FILENAME);
});
it('moves multiple images not in manifest', async () => {
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);
@@ -315,7 +326,7 @@ describe('Github Workflow: Re-Process All Images', () => {
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
});
it('removes invalid not in manifest even if remove disabled', async () => {
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
@@ -326,12 +337,12 @@ describe('Github Workflow: Re-Process All Images', () => {
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`));
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 () => {
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);
@@ -343,11 +354,11 @@ describe('Github Workflow: Re-Process All Images', () => {
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(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Removing"));
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Not a valid OTA file"));
});
it('keeps image and rewrites manifest', async () => {
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);
@@ -361,11 +372,11 @@ describe('Github Workflow: Re-Process All Images', () => {
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
});
it('keeps image with escaped url and rewrites manifest', async () => {
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, '');
const fileName = oldMetas.url.split("/").pop()!;
const newName = fileName.replace(".ota", "(%1).ota");
const baseUrl = oldMetas.url.replace(fileName, "");
oldMetas.url = baseUrl + encodeURIComponent(newName);
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas]);
const imagePath = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH);
@@ -385,11 +396,12 @@ describe('Github Workflow: Re-Process All Images', () => {
// @ts-expect-error override
{fileName: newName, url: `${baseUrl}${encodeURIComponent(newName)}`},
);
// biome-ignore lint/performance/noDelete: <explanation>
delete outManifestMetas.originalUrl;
expect(writeManifestSpy).toHaveBeenNthCalledWith(2, common.BASE_INDEX_MANIFEST_FILENAME, [outManifestMetas]);
});
it('ignores when same images referenced multiple times in manifest', async () => {
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]);
@@ -408,9 +420,9 @@ describe('Github Workflow: Re-Process All Images', () => {
);
});
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'});
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);
@@ -422,28 +434,30 @@ describe('Github Workflow: Re-Process All Images', () => {
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'}),
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;
describe("downloads", () => {
let fetchSpy: MockInstance;
let setTimeoutSpy: MockInstance;
let fetchReturnedStatus: {ok: boolean; status: number; body?: object} = {ok: true, status: 200, body: {}};
const get3rdPartyDir = jest.fn().mockReturnValue(IMAGES_TEST_DIR);
const get3rdPartyDir = vi.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}`);
}
if (manifestName === common.PREV_INDEX_MANIFEST_FILENAME) {
return originalUrl.replace(`/${common.BASE_IMAGES_DIR}/`, `/${common.PREV_IMAGES_DIR}/`);
}
throw new Error(`Not supported: ${manifestName}`);
};
afterAll(() => {
@@ -452,9 +466,12 @@ describe('Github Workflow: Re-Process All Images', () => {
});
beforeEach(() => {
process.env.NODE_EXTRA_CA_CERTS = 'cacerts.pem';
process.env.NODE_EXTRA_CA_CERTS = "cacerts.pem";
get3rdPartyDir.mockClear();
fetchReturnedStatus = {ok: true, status: 200, body: {}};
fetchSpy = jest.spyOn(global, 'fetch').mockImplementation(
fetchSpy = vi.spyOn(global, "fetch").mockImplementation(
// @ts-expect-error mocked as needed
(input) => {
return {
@@ -462,11 +479,11 @@ describe('Github Workflow: Re-Process All Images', () => {
status: fetchReturnedStatus.status,
body: fetchReturnedStatus.body,
// @ts-expect-error Buffer <> ArrayBuffer (props not used)
arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath((input as string).split('/').pop()!)),
arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath((input as string).split("/").pop()!)),
};
},
);
setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(
setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation(
// @ts-expect-error mock
(fn) => {
fn();
@@ -474,18 +491,18 @@ describe('Github Workflow: Re-Process All Images', () => {
);
});
it('failure without CA Certificates ENV', async () => {
process.env.NODE_EXTRA_CA_CERTS = '';
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\``)}),
expect.objectContaining({message: expect.stringContaining("Download 3rd Parties requires `NODE_EXTRA_CA_CERTS=cacerts.pem`")}),
);
});
it('failure with malformed metas', async () => {
it("failure with malformed metas", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
// @ts-expect-error old metas
{
@@ -493,9 +510,9 @@ describe('Github Workflow: Re-Process All Images', () => {
fileSize: 307682,
manufacturerCode: 4417,
imageType: 54179,
modelId: 'TS011F',
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
url: '', // not undefined to pass setManifest
modelId: "TS011F",
sha512: "01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb",
url: "", // not undefined to pass setManifest
},
]);
@@ -509,10 +526,10 @@ describe('Github Workflow: Re-Process All Images', () => {
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`));
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Ignoring malformed"));
});
it('failure from fetch ok', async () => {
it("failure from fetch ok", async () => {
setManifest(
common.BASE_INDEX_MANIFEST_FILENAME,
// @ts-expect-error old metas
@@ -535,7 +552,7 @@ describe('Github Workflow: Re-Process All Images', () => {
);
});
it('ignores urls from this repo', async () => {
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);
@@ -552,7 +569,7 @@ describe('Github Workflow: Re-Process All Images', () => {
expectWriteNoChange(4, common.BASE_INDEX_MANIFEST_FILENAME);
});
it('ignores urls with no out dir specified', async () => {
it("ignores urls with no out dir specified", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
// @ts-expect-error old metas
{
@@ -560,9 +577,9 @@ describe('Github Workflow: Re-Process All Images', () => {
fileSize: 307682,
manufacturerCode: 4417,
imageType: 54179,
modelId: 'TS011F',
sha512: '01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb',
url: 'https://www.elektroimportoren.no/docs/lib/4512772-Firmware-35.ota',
modelId: "TS011F",
sha512: "01939ca4fc790432d2c233e19b2440c1e0248d2ce85c9299e0b88928cb2341de675350ac7b78187a25f06a2768f93db0a17c4ba950b60c82c072e0c0833cfcfb",
url: "https://www.elektroimportoren.no/docs/lib/4512772-Firmware-35.ota",
},
]);
@@ -576,10 +593,10 @@ describe('Github Workflow: Re-Process All Images', () => {
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`));
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining("no out dir specified"));
});
it('ignores invalid OTA file', async () => {
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}`,
@@ -596,11 +613,11 @@ describe('Github Workflow: Re-Process All Images', () => {
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`));
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Ignoring"));
expect(coreErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Not a valid OTA file"));
});
it('ignores identical image', async () => {
it("ignores identical image", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
IMAGE_V14_1_METAS,
Object.assign({}, withOldMetas(IMAGE_V14_1_METAS), {
@@ -618,10 +635,10 @@ describe('Github Workflow: Re-Process All Images', () => {
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\``));
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining("Conflict with image at index `0`"));
});
it('success without mocked get3rdPartyDir', async () => {
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,
@@ -641,14 +658,14 @@ describe('Github Workflow: Re-Process All Images', () => {
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'),
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));
rmSync(path.join(common.BASE_IMAGES_DIR, "Hue", OLD_META_3RD_PARTY_1_REAL_IMAGE));
});
it('success with add different metas and ignored', async () => {
it("success with add different metas and ignored", async () => {
setManifest(
common.BASE_INDEX_MANIFEST_FILENAME,
// @ts-expect-error old metas
@@ -683,7 +700,7 @@ describe('Github Workflow: Re-Process All Images', () => {
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing ignored '${OLD_META_3RD_PARTY_IGNORED_METAS.url}'`));
});
it('success with add+move same and ignored', async () => {
it("success with add+move same and ignored", async () => {
setManifest(
common.BASE_INDEX_MANIFEST_FILENAME,
// @ts-expect-error old metas
@@ -715,7 +732,7 @@ describe('Github Workflow: Re-Process All Images', () => {
expect(coreWarningSpy).toHaveBeenCalledWith(expect.stringContaining(`Removing ignored '${OLD_META_3RD_PARTY_IGNORED_METAS.url}'`));
});
it('success with add to prev', async () => {
it("success with add to prev", async () => {
setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [
IMAGE_V14_1_METAS,
// @ts-expect-error old metas
@@ -746,16 +763,16 @@ describe('Github Workflow: Re-Process All Images', () => {
expect(writeManifestSpy).toHaveBeenNthCalledWith(4, common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
});
it('success with escaped', async () => {
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, '');
const fileName = oldMetas.url.split("/").pop()!;
const newName = fileName.replace(".ota", "(%1).ota");
const baseUrl = oldMetas.url.replace(fileName, "");
oldMetas.url = baseUrl + encodeURIComponent(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(
fetchSpy = vi.spyOn(global, "fetch").mockImplementationOnce(
// @ts-expect-error mocked as needed
() => {
return {

View File

@@ -1,35 +1,37 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
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 type {RepoImageMeta} from "../src/types";
import {rmSync} from 'fs';
import {rmSync} from "node:fs";
import * as common from '../src/common';
import {updateManifests} from '../src/ghw_update_manifests';
import {type MockInstance, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import * as common from "../src/common";
import {updateManifests} from "../src/ghw_update_manifests";
import {
BASE_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
IMAGE_V13_1,
IMAGE_V14_1,
IMAGE_V14_1_METAS,
PREV_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
useImage,
withExtraMetas,
} from './data.test';
} from "./data.test";
const github = {
rest: {
pulls: {
get: jest.fn<ReturnType<Octokit['rest']['pulls']['get']>, Parameters<Octokit['rest']['pulls']['get']>, unknown>(),
get: vi.fn<(...args: Parameters<Octokit["rest"]["pulls"]["get"]>) => ReturnType<Octokit["rest"]["pulls"]["get"]>>(),
},
repos: {
compareCommitsWithBasehead: jest.fn<
ReturnType<Octokit['rest']['repos']['compareCommitsWithBasehead']>,
Parameters<Octokit['rest']['repos']['compareCommitsWithBasehead']>,
unknown
>(),
compareCommitsWithBasehead:
vi.fn<
(
...args: Parameters<Octokit["rest"]["repos"]["compareCommitsWithBasehead"]>
) => ReturnType<Octokit["rest"]["repos"]["compareCommitsWithBasehead"]>
>(),
},
},
};
@@ -39,40 +41,42 @@ const core: Partial<typeof CoreApi> = {
warning: console.warn,
error: console.error,
notice: console.log,
startGroup: jest.fn(),
endGroup: jest.fn(),
startGroup: vi.fn(),
endGroup: vi.fn(),
};
const context: Partial<Context> = {
eventName: 'push',
eventName: "push",
payload: {
head_commit: {
message: 'push from pr (#213)',
message: "push from pr (#213)",
},
},
repo: {
owner: 'Koenkk',
repo: 'zigbee-OTA',
owner: "Koenkk",
repo: "zigbee-OTA",
},
};
describe('Github Workflow: Update manifests', () => {
describe("Github Workflow: Update manifests", () => {
let baseManifest: RepoImageMeta[];
let prevManifest: RepoImageMeta[];
let readManifestSpy: jest.SpyInstance;
let writeManifestSpy: jest.SpyInstance;
let addImageToBaseSpy: jest.SpyInstance;
let addImageToPrevSpy: jest.SpyInstance;
let readManifestSpy: MockInstance;
let writeManifestSpy: MockInstance;
let addImageToBaseSpy: MockInstance;
let addImageToPrevSpy: MockInstance;
let filePaths: ReturnType<typeof useImage>[] = [];
let prBody: string | undefined;
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`);
}
if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
return prevManifest;
}
throw new Error(`${fileName} not supported`);
};
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
@@ -92,7 +96,7 @@ describe('Github Workflow: Update manifests', () => {
prevManifest = [];
};
const expectNoChanges = (noReadManifest: boolean = false): void => {
const expectNoChanges = (noReadManifest = false): void => {
if (noReadManifest) {
expect(readManifestSpy).toHaveBeenCalledTimes(0);
} else {
@@ -119,10 +123,10 @@ describe('Github Workflow: Update manifests', () => {
resetManifests();
filePaths = [];
readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest);
writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest);
addImageToBaseSpy = jest.spyOn(common, 'addImageToBase');
addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev');
readManifestSpy = vi.spyOn(common, "readManifest").mockImplementation(getManifest);
writeManifestSpy = vi.spyOn(common, "writeManifest").mockImplementation(setManifest);
addImageToBaseSpy = vi.spyOn(common, "addImageToBase");
addImageToPrevSpy = vi.spyOn(common, "addImageToPrev");
github.rest.pulls.get.mockImplementation(
// @ts-expect-error mock
() => ({data: {body: prBody}}),
@@ -139,29 +143,29 @@ describe('Github Workflow: Update manifests', () => {
rmSync(common.PR_ARTIFACT_DIR, {recursive: true, force: true});
});
it('hard failure from outside push context', async () => {
it("hard failure from outside push context", async () => {
filePaths = [useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await updateManifests(github, core, {payload: {}});
}).rejects.toThrow(`Not a push`);
}).rejects.toThrow("Not a push");
expectNoChanges(true);
});
it('failure with file outside of images directory', async () => {
it("failure with file outside of images directory", async () => {
filePaths = [useImage(IMAGE_V13_1, PREV_IMAGES_TEST_DIR_PATH), useImage(IMAGE_V14_1)];
await expect(async () => {
// @ts-expect-error mock
await updateManifests(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Cannot run with files outside`)}));
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining("Cannot run with files outside")}));
expectNoChanges(true);
});
it('success into base', async () => {
it("success into base", async () => {
filePaths = [useImage(IMAGE_V14_1)];
// @ts-expect-error mock
@@ -175,7 +179,7 @@ describe('Github Workflow: Update manifests', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]);
});
it('success with extra metas', async () => {
it("success with extra metas", async () => {
filePaths = [useImage(IMAGE_V14_1)];
prBody = `Text before start tag \`\`\`json {"manufacturerName": ["lixee"]} \`\`\` Text after end tag`;
@@ -188,18 +192,18 @@ describe('Github Workflow: Update manifests', () => {
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
expect(writeManifestSpy).toHaveBeenCalledTimes(2);
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ['lixee']}),
withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ["lixee"]}),
]);
});
it('fails to get PR for extra metas', async () => {
it("fails to get PR for extra metas", async () => {
filePaths = [useImage(IMAGE_V14_1)];
github.rest.pulls.get.mockRejectedValueOnce('403');
github.rest.pulls.get.mockRejectedValueOnce("403");
await expect(async () => {
// @ts-expect-error mock
await updateManifests(github, core, context);
}).rejects.toThrow(expect.objectContaining({message: `Failed to get PR#213 for extra metas: 403`}));
}).rejects.toThrow(expect.objectContaining({message: "Failed to get PR#213 for extra metas: 403"}));
expectNoChanges(false);
});

View File

@@ -1,222 +0,0 @@
/**
* 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_check_ota_pr.ts',
'src/ghw_get_changed_ota_files.ts',
'src/ghw_process_ota_files.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;

View File

@@ -1,13 +1,13 @@
import type {RepoImageMeta} from '../src/types';
import type {RepoImageMeta} from "../src/types";
import {existsSync, mkdirSync, readFileSync, rmSync} from 'fs';
import {existsSync, mkdirSync, readFileSync, rmSync} from "node:fs";
import * as common from '../src/common';
import {processFirmwareImage, ProcessFirmwareImageStatus} from '../src/process_firmware_image';
import {type MockInstance, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import * as common from "../src/common";
import {ProcessFirmwareImageStatus, processFirmwareImage} from "../src/process_firmware_image";
import {
BASE_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
getImageOriginalDirPath,
IMAGES_TEST_DIR,
IMAGE_INVALID,
IMAGE_TAR,
IMAGE_TAR_METAS,
@@ -17,33 +17,36 @@ import {
IMAGE_V13_1_METAS,
IMAGE_V14_1,
IMAGE_V14_1_METAS,
IMAGES_TEST_DIR,
PREV_IMAGES_TEST_DIR_PATH,
getAdjustedContent,
getImageOriginalDirPath,
useImage,
withExtraMetas,
} from './data.test';
} from "./data.test";
describe('Process Firmware Image', () => {
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 consoleErrorSpy: MockInstance;
let consoleLogSpy: MockInstance;
let readManifestSpy: MockInstance;
let writeManifestSpy: MockInstance;
let addImageToBaseSpy: MockInstance;
let addImageToPrevSpy: MockInstance;
let fetchSpy: MockInstance;
let setTimeoutSpy: MockInstance;
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`);
}
if (fileName === common.PREV_INDEX_MANIFEST_FILENAME) {
return prevManifest;
}
throw new Error(`${fileName} not supported`);
};
const setManifest = (fileName: string, content: RepoImageMeta[]): void => {
@@ -71,7 +74,7 @@ describe('Process Firmware Image', () => {
return newMeta;
};
const expectNoChanges = (noReadManifest: boolean = false): void => {
const expectNoChanges = (noReadManifest = false): void => {
if (noReadManifest) {
expect(readManifestSpy).toHaveBeenCalledTimes(0);
} else {
@@ -84,7 +87,7 @@ describe('Process Firmware Image', () => {
expect(writeManifestSpy).toHaveBeenCalledTimes(0);
};
const expectWriteNoChanges = (inBase: boolean = true, inPrev: boolean = true): void => {
const expectWriteNoChanges = (inBase = true, inPrev = true): void => {
if (inBase) {
expect(writeManifestSpy).toHaveBeenNthCalledWith(
1,
@@ -115,19 +118,20 @@ describe('Process Firmware Image', () => {
setTimeoutSpy.mockRestore();
rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
rmSync(IMAGES_TEST_DIR, {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(
consoleErrorSpy = vi.spyOn(console, "error");
consoleLogSpy = vi.spyOn(console, "log");
readManifestSpy = vi.spyOn(common, "readManifest").mockImplementation(getManifest);
writeManifestSpy = vi.spyOn(common, "writeManifest").mockImplementation(setManifest);
addImageToBaseSpy = vi.spyOn(common, "addImageToBase");
addImageToPrevSpy = vi.spyOn(common, "addImageToPrev");
fetchSpy = vi.spyOn(global, "fetch").mockImplementation(
// @ts-expect-error mocked as needed
(input) => {
return {
@@ -139,7 +143,7 @@ describe('Process Firmware Image', () => {
};
},
);
setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(
setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation(
// @ts-expect-error mock
(fn) => {
fn();
@@ -152,72 +156,72 @@ describe('Process Firmware Image', () => {
rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true});
});
it('failure with fetch ok', async () => {
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(status).toStrictEqual(ProcessFirmwareImageStatus.RequestFailed);
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining(`Invalid response from ${IMAGE_V14_1} status=${fetchReturnedStatus.status}.`),
);
expectNoChanges(false);
});
it('failure with fetch body', async () => {
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(status).toStrictEqual(ProcessFirmwareImageStatus.RequestFailed);
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining(`Invalid response from ${IMAGE_V14_1} status=${fetchReturnedStatus.status}.`),
);
expectNoChanges(false);
});
it('failure with invalid OTA file', async () => {
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`));
expect(status).toStrictEqual(ProcessFirmwareImageStatus.Error);
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Not a valid OTA fil"));
expectNoChanges(false);
});
it('failure with identical OTA file', async () => {
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(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 () => {
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`));
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 () => {
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`));
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 () => {
it("success into base", async () => {
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
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);
@@ -226,12 +230,12 @@ describe('Process Firmware Image', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V14_1, IMAGE_V14_1_METAS)]);
});
it('success into prev', async () => {
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(status).toStrictEqual(ProcessFirmwareImageStatus.Success);
expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME);
expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME);
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
@@ -241,13 +245,13 @@ describe('Process Firmware Image', () => {
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 () => {
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(status).toStrictEqual(ProcessFirmwareImageStatus.Success);
expect(readManifestSpy).toHaveBeenCalledTimes(2);
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
@@ -256,7 +260,7 @@ describe('Process Firmware Image', () => {
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 () => {
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);
@@ -264,7 +268,7 @@ describe('Process Firmware Image', () => {
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V14_1, IMAGE_V14_1);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.Success);
expect(readManifestSpy).toHaveBeenCalledTimes(2);
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
@@ -273,7 +277,7 @@ describe('Process Firmware Image', () => {
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 () => {
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);
@@ -281,7 +285,7 @@ describe('Process Firmware Image', () => {
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_V13_1, IMAGE_V13_1);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.Success);
expect(readManifestSpy).toHaveBeenCalledTimes(2);
expect(addImageToBaseSpy).toHaveBeenCalledTimes(0);
expect(addImageToPrevSpy).toHaveBeenCalledTimes(1);
@@ -290,13 +294,13 @@ describe('Process Firmware Image', () => {
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [withOriginalUrl(IMAGE_V13_1, IMAGE_V13_1_METAS)]);
});
it('success with newer with missing file', async () => {
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(status).toStrictEqual(ProcessFirmwareImageStatus.Success);
expect(readManifestSpy).toHaveBeenCalledTimes(2);
expect(addImageToBaseSpy).toHaveBeenCalledTimes(1);
expect(addImageToPrevSpy).toHaveBeenCalledTimes(0);
@@ -305,34 +309,34 @@ describe('Process Firmware Image', () => {
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']});
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(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']})),
withOriginalUrl(IMAGE_V14_1, withExtraMetas(IMAGE_V14_1_METAS, {manufacturerName: ["lixee"]})),
]);
});
it('success with all extra metas', async () => {
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'],
manufacturerName: ["lixee"],
maxFileVersion: 5,
minFileVersion: 3,
modelId: 'bogus',
releaseNotes: 'bugfixes',
modelId: "bogus",
releaseNotes: "bugfixes",
});
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
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);
@@ -345,24 +349,24 @@ describe('Process Firmware Image', () => {
force: false,
hardwareVersionMax: 2,
hardwareVersionMin: 1,
manufacturerName: ['lixee'],
manufacturerName: ["lixee"],
maxFileVersion: 5,
minFileVersion: 3,
modelId: 'bogus',
releaseNotes: 'bugfixes',
modelId: "bogus",
releaseNotes: "bugfixes",
}),
),
]);
});
it('success with tar', async () => {
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'));
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_TAR, IMAGE_TAR, {}, true, (f) => f.endsWith(".ota"));
expect(status).toStrictEqual(ProcessFirmwareImageStatus.SUCCESS);
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);
@@ -373,28 +377,28 @@ describe('Process Firmware Image', () => {
rmSync(common.TMP_DIR, {recursive: true, force: true});
});
it('failure with invalid tar', async () => {
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'));
const status = await processFirmwareImage(IMAGES_TEST_DIR, IMAGE_INVALID, IMAGE_INVALID, {}, true, (f) => f.endsWith(".ota"));
expect(status).toStrictEqual(ProcessFirmwareImageStatus.TAR_NO_IMAGE);
expect(status).toStrictEqual(ProcessFirmwareImageStatus.TarNoImage);
expectNoChanges(true);
rmSync(common.TMP_DIR, {recursive: true, force: true});
});
it('failure with extract tar (missing dir)', async () => {
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'));
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'}));
expect(status).toStrictEqual(ProcessFirmwareImageStatus.TarNoImage);
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.objectContaining({syscall: "chdir", code: "ENOENT"}));
expectNoChanges(false);
rmSync(common.TMP_DIR, {recursive: true, force: true});

View File

@@ -1,10 +1,9 @@
{
"extends": "../tsconfig",
"include": ["./**/*", "jest.config.ts"],
"include": ["./**/*", "vitest.config.mts"],
"compilerOptions": {
"types": ["jest"],
"rootDir": "..",
"noEmit": true
},
"references": [{"path": ".."}]
"references": [{ "path": ".." }]
}

32
tests/vitest.config.mts Normal file
View File

@@ -0,0 +1,32 @@
import {defineConfig} from "vitest/config";
export default defineConfig({
test: {
onConsoleLog() {
return true;
},
coverage: {
enabled: false,
provider: "v8",
include: [
"src/ghw_check_ota_pr.ts",
"src/ghw_get_changed_ota_files.ts",
"src/ghw_process_ota_files.ts",
"src/process_firmware_image.ts",
"src/ghw_reprocess_all_images.ts",
],
extension: [".ts"],
// exclude: [],
clean: true,
cleanOnRerun: true,
reportsDirectory: "coverage",
reporter: ["text", "html"],
reportOnFailure: false,
thresholds: {
100: true,
},
},
clearMocks: true,
fileParallelism: false,
},
});