fix(core): tidy formatting and minor fixes across CLI, SNMP, HTTP server, migrations and packaging
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Manual Docker Testing Scripts
|
||||
|
||||
This directory contains scripts for manually testing NUPST installation and migration in Docker containers with systemd support.
|
||||
This directory contains scripts for manually testing NUPST installation and migration in Docker
|
||||
containers with systemd support.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -15,12 +16,14 @@ This directory contains scripts for manually testing NUPST installation and migr
|
||||
Creates a Docker container with systemd and installs NUPST v3.
|
||||
|
||||
**What it does:**
|
||||
|
||||
- Creates Ubuntu 22.04 container with systemd enabled
|
||||
- Installs NUPST v3 from commit `806f81c6` (last v3 version)
|
||||
- Enables and starts the systemd service
|
||||
- Leaves container running for testing
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
chmod +x 01-setup-v3-container.sh
|
||||
./01-setup-v3-container.sh
|
||||
@@ -33,6 +36,7 @@ chmod +x 01-setup-v3-container.sh
|
||||
Tests the migration from v3 to v4.
|
||||
|
||||
**What it does:**
|
||||
|
||||
- Checks current v3 installation
|
||||
- Pulls v4 code from `migration/deno-v4` branch
|
||||
- Runs install.sh (should auto-detect and migrate)
|
||||
@@ -40,6 +44,7 @@ Tests the migration from v3 to v4.
|
||||
- Tests basic commands
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
chmod +x 02-test-v3-to-v4-migration.sh
|
||||
./02-test-v3-to-v4-migration.sh
|
||||
@@ -52,6 +57,7 @@ chmod +x 02-test-v3-to-v4-migration.sh
|
||||
Removes the test container.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
chmod +x 03-cleanup.sh
|
||||
./03-cleanup.sh
|
||||
@@ -134,16 +140,19 @@ docker rm -f nupst-test-v3
|
||||
## Troubleshooting
|
||||
|
||||
### Container won't start
|
||||
|
||||
- Ensure Docker daemon is running
|
||||
- Check you have privileged access
|
||||
- Try: `docker logs nupst-test-v3`
|
||||
|
||||
### Systemd not working in container
|
||||
|
||||
- Requires Linux host (not macOS/Windows)
|
||||
- Needs `--privileged` and cgroup volume mounts
|
||||
- Check: `docker exec nupst-test-v3 systemctl --version`
|
||||
|
||||
### Migration fails
|
||||
|
||||
- Check logs: `docker exec nupst-test-v3 journalctl -xe`
|
||||
- Verify install.sh ran: `docker exec nupst-test-v3 ls -la /opt/nupst/`
|
||||
- Check service: `docker exec nupst-test-v3 systemctl status nupst`
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* Run with: deno run --allow-all test/showcase.ts
|
||||
*/
|
||||
|
||||
import { logger, type ITableColumn } from '../ts/logger.ts';
|
||||
import { theme, symbols, getBatteryColor, formatPowerStatus } from '../ts/colors.ts';
|
||||
import { type ITableColumn, logger } from '../ts/logger.ts';
|
||||
import { formatPowerStatus, getBatteryColor, symbols, theme } from '../ts/colors.ts';
|
||||
|
||||
console.log('');
|
||||
console.log('═'.repeat(80));
|
||||
@@ -38,31 +38,51 @@ logger.logBoxEnd();
|
||||
|
||||
console.log('');
|
||||
|
||||
logger.logBox('Success Box (Green)', [
|
||||
'Used for successful operations',
|
||||
'Installation complete, service started, etc.',
|
||||
], 60, 'success');
|
||||
logger.logBox(
|
||||
'Success Box (Green)',
|
||||
[
|
||||
'Used for successful operations',
|
||||
'Installation complete, service started, etc.',
|
||||
],
|
||||
60,
|
||||
'success',
|
||||
);
|
||||
|
||||
console.log('');
|
||||
|
||||
logger.logBox('Error Box (Red)', [
|
||||
'Used for critical errors and failures',
|
||||
'Configuration errors, connection failures, etc.',
|
||||
], 60, 'error');
|
||||
logger.logBox(
|
||||
'Error Box (Red)',
|
||||
[
|
||||
'Used for critical errors and failures',
|
||||
'Configuration errors, connection failures, etc.',
|
||||
],
|
||||
60,
|
||||
'error',
|
||||
);
|
||||
|
||||
console.log('');
|
||||
|
||||
logger.logBox('Warning Box (Yellow)', [
|
||||
'Used for warnings and deprecations',
|
||||
'Old command format, missing config, etc.',
|
||||
], 60, 'warning');
|
||||
logger.logBox(
|
||||
'Warning Box (Yellow)',
|
||||
[
|
||||
'Used for warnings and deprecations',
|
||||
'Old command format, missing config, etc.',
|
||||
],
|
||||
60,
|
||||
'warning',
|
||||
);
|
||||
|
||||
console.log('');
|
||||
|
||||
logger.logBox('Info Box (Cyan)', [
|
||||
'Used for informational messages',
|
||||
'Version info, update available, etc.',
|
||||
], 60, 'info');
|
||||
logger.logBox(
|
||||
'Info Box (Cyan)',
|
||||
[
|
||||
'Used for informational messages',
|
||||
'Version info, update available, etc.',
|
||||
],
|
||||
60,
|
||||
'info',
|
||||
);
|
||||
|
||||
console.log('');
|
||||
|
||||
@@ -112,15 +132,24 @@ const upsColumns: ITableColumn[] = [
|
||||
{ header: 'ID', key: 'id' },
|
||||
{ header: 'Name', key: 'name' },
|
||||
{ header: 'Host', key: 'host' },
|
||||
{ header: 'Status', key: 'status', color: (v) => {
|
||||
if (v.includes('Online')) return theme.success(v);
|
||||
if (v.includes('Battery')) return theme.warning(v);
|
||||
return theme.dim(v);
|
||||
}},
|
||||
{ header: 'Battery', key: 'battery', align: 'right', color: (v) => {
|
||||
const pct = parseInt(v);
|
||||
return getBatteryColor(pct)(v);
|
||||
}},
|
||||
{
|
||||
header: 'Status',
|
||||
key: 'status',
|
||||
color: (v) => {
|
||||
if (v.includes('Online')) return theme.success(v);
|
||||
if (v.includes('Battery')) return theme.warning(v);
|
||||
return theme.dim(v);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Battery',
|
||||
key: 'battery',
|
||||
align: 'right',
|
||||
color: (v) => {
|
||||
const pct = parseInt(v);
|
||||
return getBatteryColor(pct)(v);
|
||||
},
|
||||
},
|
||||
{ header: 'Runtime', key: 'runtime', align: 'right' },
|
||||
];
|
||||
|
||||
|
||||
47
test/test.ts
47
test/test.ts
@@ -1,9 +1,9 @@
|
||||
import { assert, assertEquals, assertExists } from 'jsr:@std/assert@^1.0.0';
|
||||
import { NupstSnmp } from '../ts/snmp/manager.ts';
|
||||
import { UpsOidSets } from '../ts/snmp/oid-sets.ts';
|
||||
import type { ISnmpConfig, TUpsModel, IOidSet } from '../ts/snmp/types.ts';
|
||||
import type { IOidSet, ISnmpConfig, TUpsModel } from '../ts/snmp/types.ts';
|
||||
import { shortId } from '../ts/helpers/shortid.ts';
|
||||
import { TIMING, SNMP, THRESHOLDS, HTTP_SERVER, UI } from '../ts/constants.ts';
|
||||
import { HTTP_SERVER, SNMP, THRESHOLDS, TIMING, UI } from '../ts/constants.ts';
|
||||
import { Action, type IActionContext } from '../ts/actions/base-action.ts';
|
||||
|
||||
import * as qenv from 'npm:@push.rocks/qenv@^6.0.0';
|
||||
@@ -56,8 +56,14 @@ Deno.test('SNMP constants: port is 161', () => {
|
||||
});
|
||||
|
||||
Deno.test('SNMP constants: timeouts increase with security level', () => {
|
||||
assert(SNMP.TIMEOUT_NO_AUTH_MS <= SNMP.TIMEOUT_AUTH_MS, 'Auth timeout should be >= noAuth timeout');
|
||||
assert(SNMP.TIMEOUT_AUTH_MS <= SNMP.TIMEOUT_AUTH_PRIV_MS, 'AuthPriv timeout should be >= Auth timeout');
|
||||
assert(
|
||||
SNMP.TIMEOUT_NO_AUTH_MS <= SNMP.TIMEOUT_AUTH_MS,
|
||||
'Auth timeout should be >= noAuth timeout',
|
||||
);
|
||||
assert(
|
||||
SNMP.TIMEOUT_AUTH_MS <= SNMP.TIMEOUT_AUTH_PRIV_MS,
|
||||
'AuthPriv timeout should be >= Auth timeout',
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test('THRESHOLDS constants: defaults are reasonable', () => {
|
||||
@@ -92,21 +98,21 @@ Deno.test('UpsOidSets: all models have OID sets', () => {
|
||||
Deno.test('UpsOidSets: all non-custom models have complete OIDs', () => {
|
||||
const requiredOids = ['POWER_STATUS', 'BATTERY_CAPACITY', 'BATTERY_RUNTIME', 'OUTPUT_LOAD'];
|
||||
|
||||
for (const model of UPS_MODELS.filter(m => m !== 'custom')) {
|
||||
for (const model of UPS_MODELS.filter((m) => m !== 'custom')) {
|
||||
const oidSet = UpsOidSets.getOidSet(model);
|
||||
|
||||
for (const oid of requiredOids) {
|
||||
const value = oidSet[oid as keyof IOidSet];
|
||||
assert(
|
||||
typeof value === 'string' && value.length > 0,
|
||||
`${model} should have non-empty ${oid}`
|
||||
`${model} should have non-empty ${oid}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test('UpsOidSets: power status values defined for non-custom models', () => {
|
||||
for (const model of UPS_MODELS.filter(m => m !== 'custom')) {
|
||||
for (const model of UPS_MODELS.filter((m) => m !== 'custom')) {
|
||||
const oidSet = UpsOidSets.getOidSet(model);
|
||||
assertExists(oidSet.POWER_STATUS_VALUES, `${model} should have POWER_STATUS_VALUES`);
|
||||
assertExists(oidSet.POWER_STATUS_VALUES?.online, `${model} should have online value`);
|
||||
@@ -200,11 +206,11 @@ Deno.test('Action.shouldExecute: onlyPowerChanges mode', () => {
|
||||
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ triggerReason: 'powerStatusChange' })),
|
||||
true
|
||||
true,
|
||||
);
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ triggerReason: 'thresholdViolation' })),
|
||||
false
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -218,13 +224,13 @@ Deno.test('Action.shouldExecute: onlyThresholds mode', () => {
|
||||
// Below thresholds - should execute
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ batteryCapacity: 50, batteryRuntime: 10 })),
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
// Above thresholds - should not execute
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ batteryCapacity: 100, batteryRuntime: 60 })),
|
||||
false
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -248,7 +254,7 @@ Deno.test('Action.shouldExecute: powerChangesAndThresholds mode (default)', () =
|
||||
// Power change - should execute
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ triggerReason: 'powerStatusChange' })),
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
// Threshold violation - should execute
|
||||
@@ -257,7 +263,7 @@ Deno.test('Action.shouldExecute: powerChangesAndThresholds mode (default)', () =
|
||||
triggerReason: 'thresholdViolation',
|
||||
batteryCapacity: 50,
|
||||
})),
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
// No power change and above thresholds - should not execute
|
||||
@@ -267,7 +273,7 @@ Deno.test('Action.shouldExecute: powerChangesAndThresholds mode (default)', () =
|
||||
batteryCapacity: 100,
|
||||
batteryRuntime: 60,
|
||||
})),
|
||||
false
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -279,11 +285,11 @@ Deno.test('Action.shouldExecute: anyChange mode always returns true', () => {
|
||||
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ triggerReason: 'powerStatusChange' })),
|
||||
true
|
||||
true,
|
||||
);
|
||||
assertEquals(
|
||||
action.testShouldExecute(createMockContext({ triggerReason: 'thresholdViolation' })),
|
||||
true
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -339,7 +345,7 @@ async function testUpsConnection(
|
||||
assertExists(status, 'Status should exist');
|
||||
assert(
|
||||
['online', 'onBattery', 'unknown'].includes(status.powerStatus),
|
||||
`Power status should be valid: ${status.powerStatus}`
|
||||
`Power status should be valid: ${status.powerStatus}`,
|
||||
);
|
||||
assertEquals(typeof status.batteryCapacity, 'number', 'Battery capacity should be a number');
|
||||
assertEquals(typeof status.batteryRuntime, 'number', 'Battery runtime should be a number');
|
||||
@@ -347,9 +353,12 @@ async function testUpsConnection(
|
||||
// Validate ranges
|
||||
assert(
|
||||
status.batteryCapacity >= 0 && status.batteryCapacity <= 100,
|
||||
`Battery capacity should be 0-100: ${status.batteryCapacity}`
|
||||
`Battery capacity should be 0-100: ${status.batteryCapacity}`,
|
||||
);
|
||||
assert(
|
||||
status.batteryRuntime >= 0,
|
||||
`Battery runtime should be non-negative: ${status.batteryRuntime}`,
|
||||
);
|
||||
assert(status.batteryRuntime >= 0, `Battery runtime should be non-negative: ${status.batteryRuntime}`);
|
||||
}
|
||||
|
||||
// Create SNMP instance for integration tests
|
||||
|
||||
Reference in New Issue
Block a user