mirror of
https://github.com/MichMich/MagicMirror.git
synced 2026-07-23 15:54:34 -07:00
4b4a59534f
## Release Notes Thanks to: @angeldeejay, @egeekial, @khassel, @KristjanESPERANTO, @MikeBishop, @rejas > ⚠️ This release needs nodejs version >=22.21.1 <23 || >=24 (no change to previous release) [Compare to previous Release v2.36.0](https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.36.0...develop) ### [core] - Prepare Release 2.37.0 (#4193) - fix(electron): map IPv6 :: wildcard to localhost (#4188) - refactor(main): modernize DOM update flow with async/await (#4186) - refactor(main): simplify _updateDom with async/await (#4185) - fix(security): prevent unauthorized secret expansion in socket payloads (#4184) - refactor(main): simplify updateDomWithContent async flow (#4182) - fix: modules losing data after HTTP 304 responses (#4180) - chore: add missing core defaults (#4181) - fix(server): enforce ipWhitelist for Socket.IO too (#4169) - feat(systeminfo): include Git hash and branch in system information log (#4167) - feat(electron): support object-based electronSwitches (#4161) - systeminformation thread not ending: move error handling from utils to app (#4160) - fix systeminformation thread not ending (#4155) - refactor: use ES module imports in browser core (#4158) - refactor(core): remove old Object.assign polyfill (#4157) - refactor: rewrite Module as an ES6 class (#4151) - refactor: rewrite NodeHelper as an ES6 class (#4147) - update eletron to v42 (#4144) - refactor(utils): drop ajv dependency (#4142) - fix(systeminformation): output right 'used node' version (from parent process) (#4141) - fix: skip postinstall git clean when not in a git repository (#4139) - Remove unnecessary conditionals and fix falsy property check in imperial conversion (#4135) - update version in package.json ### [dependencies] - update dependencies (#4191) - Bump actions/checkout from 6 to 7 (#4190) - chore: update dependencies and adjust import path for SunCalc (#4189) - update dependencies incl. electron and revert yauzl-electron-install-fix (#4183) - update dependencies, add electron fix in package.json (#4175) - chore: update dependencies (#4162) - Bump actions/dependency-review-action from 4 to 5 (#4152) - Unify linting: replace Stylelint and markdownlint with ESLint (#4148) - update dependencies and workflows to node v26 (#4140) ### [modules/alert] - CodeQL cleanup for alerts #18, #19, #20 (#4153) - fix: resolve CodeQL alerts #24 and #26 (#4145) - fix(electron): resolve CodeQL alerts #22 and #25 in electron.js (#4136) ### [modules/calendar] - perf(calendar): pre-filter ICS data before parsing (#4168) - perf(calendar): use async ICS parsing to avoid blocking event loop (#4143) ### [modules/newsfeed] - [newsfeed] add allowBasicHtmlTags option for basic emphasis (#4176) ### [modules/updatenotification] - fix(updatenotification): don't spawn a child process when running under PM2 (#4166) - fix(updatenotification): use process.argv[0] as restart binary (#4163) - fix(updatenotification): preserve start mode on restart (#4156) - fix(updatenotification): fix ref diff parsing for fetch --dry-run (#4138) - refactor(updatenotification): replace pm2 usage with node logic (#4134) ### [modules/weather] - feat(weather): add Buienradar provider (#4164) ### [testing] - remove warning in unit tests (for nodejs >= v25) (#4149) - polish HTTP 304 docs/test/handling (#4129) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Co-authored-by: BugHaver <43462320+bughaver@users.noreply.github.com> Co-authored-by: BugHaver <43462320+lsaadeh@users.noreply.github.com> Co-authored-by: Karsten Hassel <hassel@gmx.de> Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com> Co-authored-by: Koen Konst <koenspero@gmail.com> Co-authored-by: Koen Konst <c.h.konst@avisi.nl> Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr> Co-authored-by: dathbe <github@beffa.us> Co-authored-by: veeck <gitkraken@veeck.de> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marcel <m-idler@users.noreply.github.com> Co-authored-by: sam detweiler <sdetweil@gmail.com> Co-authored-by: Kevin G. <crazylegstoo@gmail.com> Co-authored-by: Jboucly <33218155+jboucly@users.noreply.github.com> Co-authored-by: Jboucly <contact@jboucly.fr> Co-authored-by: Jarno <54169345+jarnoml@users.noreply.github.com> Co-authored-by: Jordan Welch <JordanHWelch@gmail.com> Co-authored-by: Blackspirits <blackspirits@gmail.com> Co-authored-by: Samed Ozdemir <samed@xsor.io> Co-authored-by: in-voker <58696565+in-voker@users.noreply.github.com> Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com> Co-authored-by: cgillinger <christian.gillinger@gmail.com> Co-authored-by: Sonny B <43247590+sonnyb9@users.noreply.github.com> Co-authored-by: sonnyb9 <sonnyb9@users.noreply.github.com> Co-authored-by: Morgan McBee <egeekial@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Mike Bishop <mbishop@evequefou.be>
120 lines
3.8 KiB
JavaScript
120 lines
3.8 KiB
JavaScript
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
describe("UpdateHelper", () => {
|
|
const originalEnv = { ...process.env };
|
|
const tempRoots = [];
|
|
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
process.env = { ...originalEnv };
|
|
global.version = "test";
|
|
global.root_path = process.cwd();
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = originalEnv;
|
|
vi.useRealTimers();
|
|
vi.restoreAllMocks();
|
|
for (const tempRoot of tempRoots) {
|
|
rmSync(tempRoot, { recursive: true, force: true });
|
|
}
|
|
tempRoots.length = 0;
|
|
});
|
|
|
|
/**
|
|
* Creates a temporary MagicMirror-like root with a module directory.
|
|
* @param {string} moduleName - Name of the module directory to create.
|
|
* @returns {{ root: string, modulePath: string }} Created paths.
|
|
*/
|
|
function createTempModuleRoot (moduleName) {
|
|
const root = mkdtempSync(join(tmpdir(), "mm-updater-"));
|
|
const modulePath = join(root, "modules", moduleName);
|
|
mkdirSync(modulePath, { recursive: true });
|
|
tempRoots.push(root);
|
|
return { root, modulePath };
|
|
}
|
|
|
|
/**
|
|
* Creates a fresh UpdateHelper instance for testing.
|
|
* @param {object} config - Optional config overrides.
|
|
* @returns {Promise<object>} Resolved UpdateHelper instance.
|
|
*/
|
|
async function createUpdater (config = {}) {
|
|
const updateHelperModule = await import("../../../defaultmodules/updatenotification/update_helper");
|
|
const UpdateHelper = updateHelperModule.default || updateHelperModule;
|
|
return new UpdateHelper({ updates: [], updateTimeout: 1000, updateAutorestart: false, ...config });
|
|
}
|
|
|
|
it("marks update as requiring manual restart when autoRestart is disabled", async () => {
|
|
const moduleName = "MMM-Test";
|
|
const { root } = createTempModuleRoot(moduleName);
|
|
global.root_path = root;
|
|
|
|
const updater = await createUpdater({ updateAutorestart: false });
|
|
const result = await updater.updateProcess({
|
|
name: moduleName,
|
|
updateCommand: `"${process.execPath}" -p 1`
|
|
});
|
|
|
|
expect(result.error).toBe(false);
|
|
expect(result.updated).toBe(true);
|
|
expect(result.needRestart).toBe(true);
|
|
});
|
|
|
|
it("schedules node restart when autoRestart is enabled", async () => {
|
|
vi.useFakeTimers();
|
|
|
|
const moduleName = "MMM-Test";
|
|
const { root } = createTempModuleRoot(moduleName);
|
|
global.root_path = root;
|
|
|
|
const updater = await createUpdater({ updateAutorestart: true });
|
|
const nodeRestartSpy = vi.spyOn(updater, "nodeRestart").mockImplementation(() => {});
|
|
|
|
const result = await updater.updateProcess({
|
|
name: moduleName,
|
|
updateCommand: `"${process.execPath}" -p 1`
|
|
});
|
|
|
|
expect(result.error).toBe(false);
|
|
expect(result.updated).toBe(true);
|
|
expect(result.needRestart).toBe(false);
|
|
expect(nodeRestartSpy).not.toHaveBeenCalled();
|
|
|
|
vi.advanceTimersByTime(3000);
|
|
expect(nodeRestartSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
describe("nodeRestart", () => {
|
|
it("exits without spawning when running under PM2", async () => {
|
|
process.env.pm_id = "0";
|
|
|
|
const updater = await createUpdater();
|
|
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => {});
|
|
const spawnSpy = vi.spyOn(updater, "_spawnDetachedSelf").mockImplementation(() => {});
|
|
|
|
updater.nodeRestart();
|
|
|
|
expect(exitSpy).toHaveBeenCalledWith(0);
|
|
expect(spawnSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("spawns a detached child process when not running under PM2", async () => {
|
|
delete process.env.pm_id;
|
|
|
|
const updater = await createUpdater();
|
|
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => {});
|
|
const spawnSpy = vi.spyOn(updater, "_spawnDetachedSelf").mockImplementation(() => {});
|
|
|
|
updater.nodeRestart();
|
|
|
|
expect(spawnSpy).toHaveBeenCalledOnce();
|
|
expect(exitSpy).toHaveBeenCalledOnce();
|
|
expect(exitSpy).not.toHaveBeenCalledWith(0);
|
|
});
|
|
});
|
|
});
|