2026-09-22 18:59:42 +02:00
// Run from AppImage: node tests/storage-temperature-summary.cjs
// Execute the actual summary seam without mounting effects or contacting a host.
const fs = require ( 'node:fs' );
const path = require ( 'node:path' );
const vm = require ( 'node:vm' );
const assert = require ( 'node:assert/strict' );
const ts = require ( 'typescript' );
const root = path . resolve ( __dirname , '..' );
const read = p => fs . readFileSync ( path . join ( root , p ), 'utf8' );
const source = read ( 'components/storage-overview.tsx' );
const seam = source . slice ( source . indexOf ( ' const getDiskHealthBreakdown =' ), source . indexOf ( ' const getDiskTypesBreakdown =' ));
const thresholds = read ( 'lib/health-thresholds.ts' );
const defaults = thresholds . slice ( thresholds . indexOf ( 'export const DEFAULT_DISK_TEMP' ), thresholds . indexOf ( 'const CACHE_TTL_MS' )). replaceAll ( 'export ' , '' );
const program = read ( 'lib/disk-type.ts' ). replaceAll ( 'export ' , '' ) + '\n' + defaults + '\nlet storageData; let dtThresholds = DEFAULT_DISK_TEMP;\n' + seam + '\n globalThis.run = (disks, custom) => { storageData = {disks}; dtThresholds = custom || DEFAULT_DISK_TEMP; return getDiskHealthBreakdown(); };' ;
const context = vm . createContext ({});
vm . runInContext ( ts . transpileModule ( program , { compilerOptions : { module : ts . ModuleKind . CommonJS }}). outputText , context );
function check ( label , disks , expected , custom ) {
assert . deepEqual ( JSON . parse ( JSON . stringify ( context . run ( disks , custom ))), expected , label );
}
check ( 'missing reading is unavailable, not normal' , [{ name : 'sda' , temperature : 0 }], { normal : 0 , warning : 0 , critical : 0 , unavailable : 1 });
console . log ( 'PASS missing reading is unavailable' );
for ( const temperature of [ undefined , null , - 1 , NaN , Infinity , - Infinity , '30' ])
check ( `unavailable ${ temperature } ` , [{ name : 'sda' , temperature }], { normal : 0 , warning : 0 , critical : 0 , unavailable : 1 });
for ( const temperature of [ 30 , 90 ])
check ( 'standby suppresses stale reading' , [{ name : 'sda' , temperature , standby : true }], { normal : 0 , warning : 0 , critical : 0 , unavailable : 1 });
check ( 'empty' , [], { normal : 0 , warning : 0 , critical : 0 , unavailable : 0 });
check ( 'mixed coverage and alerts' , [{ name : 'sda' , rotation_rate : 7200 , temperature : 30 },{ name : 'sdb' , rotation_rate : 7200 , temperature : 60 },{ name : 'sdc' , rotation_rate : 7200 , temperature : 65 },{ name : 'sdd' , temperature : 0 }], { normal : 1 , warning : 1 , critical : 1 , unavailable : 1 });
for ( const [ name , rotation_rate , values ] of [[ 'sda' , 7200 ,[ 59.9 , 60 , 64.9 , 65 ]],[ 'sdb' , 0 ,[ 69.9 , 70 , 74.9 , 75 ]],[ 'nvme0n1' , 0 ,[ 79.9 , 80 , 84.9 , 85 ]]]) {
for ( const [ index , temperature ] of values . entries ()) {
const expected = [{ normal : 1 , warning : 0 , critical : 0 , unavailable : 0 },{ normal : 0 , warning : 1 , critical : 0 , unavailable : 0 },{ normal : 0 , warning : 1 , critical : 0 , unavailable : 0 },{ normal : 0 , warning : 0 , critical : 1 , unavailable : 0 }][ index ];
check ( ` ${ name } exact default ${ temperature } ` , [{ name , rotation_rate , temperature }], expected );
check ( ` ${ name } exact custom ${ temperature - 20 } ` , [{ name , rotation_rate , temperature : temperature - 20 }], expected , { HDD : { warn : 40 , hot : 45 }, SSD : { warn : 50 , hot : 55 }, NVMe : { warn : 60 , hot : 65 }});
}
}
console . log ( 'PASS availability, mixed counts, default/custom boundaries' );
// Render the actual Physical Disks JSX with the real Card/Badge and provider lookup.
const React = require ( 'react' );
const { renderToStaticMarkup } = require ( 'react-dom/server' );
function load ( file ) {
if ( ! file . endsWith ( '.tsx' ) && ! file . endsWith ( '.ts' )) file += fs . existsSync ( file + '.tsx' ) ? '.tsx' : '.ts' ;
const module = { exports : {}};
const code = ts . transpileModule ( fs . readFileSync ( file , 'utf8' ), { compilerOptions : { module : ts . ModuleKind . CommonJS , jsx : ts . JsxEmit . React }}). outputText ;
const localRequire = id => id . startsWith ( '@/' ) ? load ( path . join ( root , id . slice ( 2 ))) : require ( id );
vm . runInNewContext ( code , { module , exports : module . exports , require : localRequire , React });
return module . exports ;
}
const provider = read ( 'lib/i18n/provider.tsx' );
const pure = provider . slice ( provider . indexOf ( 'function getMessage(' ), provider . indexOf ( 'export function I18nProvider' ));
const translator = provider . slice ( provider . indexOf ( ' (key: string, params?: TranslationParams) => {' ), provider . indexOf ( '\n [language],' )). trim (). replace ( /,$/ , '' );
const en = JSON . parse ( read ( 'messages/en/common.json' ));
function translation ( locale ) {
const ctx = vm . createContext ({ MESSAGE_CATALOG : { en , test : locale }, language : 'test' });
vm . runInContext ( ts . transpileModule ( pure + '\nglobalThis.t = ' + translator ,{}). outputText , ctx );
return ctx . t ;
}
const start = source . indexOf ( ' {(() => {' , source . indexOf ( '/* ── Physical Disks' ));
const end = source . indexOf ( ' })()}' , start );
assert . ok ( start >= 0 && end > start , 'actual JSX seam exists' );
const jsx = source . slice ( start , end + ' })()}' . length ). trim (). slice ( 1 , - 1 );
function render ( disks , locale = {}) {
const t = translation ( locale );
const ctx = { React ,... load ( path . join ( root , 'components/ui/card' )),... load ( path . join ( root , 'components/ui/badge' )), HardDrive : ()=> null ,
storageData : { disks , disk_count : disks . length }, diskHealthBreakdown : context . run ( disks ), diskTypesBreakdown : { nvme : 0 , ssd : 0 , hdd : disks . length , usb : 0 }, diskCountLabel : ()=> 'disks' , t };
return renderToStaticMarkup ( vm . runInNewContext ( ts . transpileModule ( 'const result = ' + jsx + '; result;' , { compilerOptions : { jsx : ts . JsxEmit . React }}). outputText , ctx ));
}
const empty = render ([]);
assert . ok ( empty . includes ( 'No disks' ), 'empty array must show neutral No disks' );
assert . ok ( ! empty . includes ( 'text-green-500' ), 'empty is not green' );
console . log ( 'PASS empty JSX is neutral' );
const disk = temperature => ({ name : 'sda' , rotation_rate : 7200 , temperature });
const fixtures = [
[[], [ 'No disks' ], 'neutral' ],
[[ disk ( 30 )], [ 'Temperatures normal' ], 'green' ],
[[ disk ( 30 ), disk ( 35 )], [ 'Temperatures normal' ], 'green' ],
[[ disk ( 0 )], [ 'Temperatures unavailable' , 'Unavailable: 1' ], 'neutral' ],
2026-09-22 19:18:23 +02:00
// /api/storage can pair cached positive SMART values with current policy flags.
[[{... disk ( 30 ), excluded : true }], [ 'Temperatures unavailable' , 'Unavailable: 1' ], 'neutral' ],
[[{... disk ( 90 ), excluded : true }], [ 'Temperatures unavailable' , 'Unavailable: 1' ], 'neutral' ],
[[ disk ( 30 ),{... disk ( 90 ), excluded : true }], [ 'Temperature data incomplete' , 'Unavailable: 1' ], 'neutral' ],
[[ disk ( 60 ),{... disk ( 90 ), excluded : true },{... disk ( 90 ), idle : true }], [ 'High temperatures: 1' , 'Unavailable: 2' ], 'yellow' ],
[[ disk ( 65 ),{... disk ( 90 ), excluded : true , idle : true , standby : true }], [ 'Critical temperatures: 1' , 'Unavailable: 1' ], 'red' ],
[[{... disk ( 30 ), idle : true }], [ 'Temperatures unavailable' , 'Unavailable: 1' ], 'neutral' ],
[[{... disk ( 90 ), idle : true }], [ 'Temperatures unavailable' , 'Unavailable: 1' ], 'neutral' ],
[[ disk ( 30 ),{... disk ( 90 ), idle : true }], [ 'Temperature data incomplete' , 'Unavailable: 1' ], 'neutral' ],
2026-09-22 18:59:42 +02:00
[[ disk ( 0 ),{... disk ( 90 ), standby : true }], [ 'Temperatures unavailable' , 'Unavailable: 2' ], 'neutral' ],
[[ disk ( 30 ), disk ( 0 )], [ 'Temperature data incomplete' , 'Unavailable: 1' ], 'neutral' ],
[[ disk ( 60 ), disk ( 0 )], [ 'High temperatures: 1' , 'Unavailable: 1' ], 'yellow' ],
[[ disk ( 65 ), disk ( 60 ), disk ( 60 ), disk ( 0 )], [ 'Critical temperatures: 1' , 'High temperatures: 2' , 'Unavailable: 1' ], 'red' ],
];
for ( const [ disks , messages , tone ] of fixtures ) {
const html = render ( disks );
for ( const message of messages ) assert . ok ( html . includes ( message ), `actual JSX: ${ message } ` );
assert . equal ( html . includes ( 'text-green-500' ), tone === 'green' );
2026-09-22 19:18:23 +02:00
assert . equal ( html . includes ( 'High temperatures:' ), messages . some ( message => message . startsWith ( 'High temperatures:' )), 'suppressed readings cannot add warning alerts' );
assert . equal ( html . includes ( 'Critical temperatures:' ), messages . some ( message => message . startsWith ( 'Critical temperatures:' )), 'suppressed readings cannot add critical alerts' );
2026-09-22 18:59:42 +02:00
if ( tone !== 'neutral' ) assert . ok ( html . includes ( `text- ${ tone } -500` ));
assert . ok ( ! html . includes ( 'all healthy' ));
}
const synthetic = { storage : { temperatureNoDisks : 'SYN empty' , temperatureNormal : 'SYN normal' , temperatureUnavailable : 'SYN unavailable' , temperatureIncomplete : 'SYN incomplete' , temperatureHighCount : 'SYN high {count}' , temperatureCriticalCount : 'SYN critical {count}' , temperatureUnavailableCount : 'SYN missing {count}' }};
for ( const [ disks ] of fixtures ) {
const html = render ( disks , synthetic );
assert . ok ( html . includes ( 'SYN' ), 'consumer uses localized whole messages' );
assert . ok ( ! html . includes ( 'Temperatures' ) && ! html . includes ( 'High temperatures' ) && ! html . includes ( 'Critical temperatures' ) && ! html . includes ( 'Unavailable:' ));
assert . ok ( ! html . includes ( '{count}' ));
}
const mixedTranslated = render ([ disk ( 65 ), disk ( 60 ), disk ( 60 ), disk ( 0 )], synthetic );
for ( const text of [ 'SYN critical 1' , 'SYN high 2' , 'SYN missing 1' ]) assert . ok ( mixedTranslated . includes ( text ));
for ( const key of Object . keys ( synthetic . storage )) {
assert . equal ( translation ({})( `storage. ${ key } ` ,{ count : 12 }), en . storage [ key ]. replace ( '{count}' , '12' ), 'unconditional missing-new-key English fallback' );
}
console . log ( 'PASS complete thermal JSX, mixed counts, translated messages and missing-key fallback' );
for ( const disks of [ undefined , null ,{}]) {
check ( 'invalid list is safe before error rendering' , disks ,{ normal : 0 , warning : 0 , critical : 0 , unavailable : 0 });
}
const errorGuard = source . match ( / if \((!storageData \|\| storageData\.error[^\n]*)\) \{/ )[ 1 ];
for ( const storageData of [ null ,{},{ disks : {}},{ disks : null },{ disks : [], error : 'fixture failure' }])
assert . equal ( Boolean ( vm . runInNewContext ( errorGuard ,{ storageData })), true , 'invalid/failed payload renders error' );
assert . equal ( Boolean ( vm . runInNewContext ( errorGuard ,{ storageData : { disks : []}})), false , 'valid empty is not error' );
console . log ( 'PASS invalid payload remains error, not empty' );