mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-07-02 10:01:37 +00:00
c6958c4991
* feat: Automatic settings migration * Handle failing on unsupported version. * Handle change not needed. * Test change not wanted. * Cleanup. * Add `external_converters` removal. * Fix schema export.
711 lines
32 KiB
TypeScript
711 lines
32 KiB
TypeScript
// side-effect ensures using mock paths
|
|
import * as data from './mocks/data';
|
|
|
|
import {existsSync, readFileSync, rmSync} from 'fs';
|
|
|
|
import objectAssignDeep from 'object-assign-deep';
|
|
|
|
import mockedData from '../lib/util/data';
|
|
import * as settings from '../lib/util/settings';
|
|
import * as settingsMigration from '../lib/util/settingsMigration';
|
|
|
|
describe('Settings Migration', () => {
|
|
beforeAll(() => {});
|
|
|
|
afterAll(() => {
|
|
rmSync(data.mockDir, {recursive: true, force: true});
|
|
});
|
|
|
|
beforeEach(() => {
|
|
data.writeDefaultConfiguration();
|
|
settings.reRead();
|
|
});
|
|
|
|
afterEach(() => {
|
|
// always validate after each test
|
|
expect(settings.validate()).toStrictEqual([]);
|
|
});
|
|
|
|
it('Fails on unsupported version', () => {
|
|
settings.set(['version'], 0);
|
|
|
|
expect(() => settingsMigration.migrateIfNecessary()).toThrow(
|
|
`Your configuration.yaml has an unsupported version 0, expected one of undefined,2`,
|
|
);
|
|
|
|
settings.set(['version'], 99999);
|
|
|
|
expect(() => settingsMigration.migrateIfNecessary()).toThrow(
|
|
`Your configuration.yaml has an unsupported version 99999, expected one of undefined,2`,
|
|
);
|
|
});
|
|
|
|
describe('Migrates v1.x.x to v2.0.0', () => {
|
|
const DEFAULT_CONFIG_V2 = {
|
|
homeassistant: false,
|
|
mqtt: {
|
|
base_topic: 'zigbee2mqtt',
|
|
server: 'mqtt://localhost',
|
|
},
|
|
serial: {
|
|
port: '/dev/dummy',
|
|
},
|
|
devices: {
|
|
'0x18fc2600000d7ae2': {
|
|
friendly_name: 'bosch_radiator',
|
|
},
|
|
'0x000b57fffec6a5b2': {
|
|
retain: true,
|
|
friendly_name: 'bulb',
|
|
description: 'this is my bulb',
|
|
},
|
|
'0x0017880104e45517': {
|
|
retain: true,
|
|
friendly_name: 'remote',
|
|
},
|
|
'0x0017880104e45520': {
|
|
retain: false,
|
|
friendly_name: 'button',
|
|
},
|
|
'0x0017880104e45521': {
|
|
retain: false,
|
|
friendly_name: 'button_double_key',
|
|
},
|
|
'0x0017880104e45522': {
|
|
qos: 1,
|
|
retain: false,
|
|
friendly_name: 'weather_sensor',
|
|
},
|
|
'0x0017880104e45523': {
|
|
retain: false,
|
|
friendly_name: 'occupancy_sensor',
|
|
},
|
|
'0x0017880104e45524': {
|
|
retain: false,
|
|
friendly_name: 'power_plug',
|
|
},
|
|
'0x0017880104e45530': {
|
|
retain: false,
|
|
friendly_name: 'button_double_key_interviewing',
|
|
},
|
|
'0x0017880104e45540': {
|
|
friendly_name: 'ikea_onoff',
|
|
},
|
|
'0x000b57fffec6a5b7': {
|
|
retain: false,
|
|
friendly_name: 'bulb_2',
|
|
},
|
|
'0x000b57fffec6a5b3': {
|
|
retain: false,
|
|
friendly_name: 'bulb_color',
|
|
},
|
|
'0x000b57fffec6a5b4': {
|
|
retain: false,
|
|
friendly_name: 'bulb_color_2',
|
|
},
|
|
'0x0017880104e45541': {
|
|
retain: false,
|
|
friendly_name: 'wall_switch',
|
|
},
|
|
'0x0017880104e45542': {
|
|
retain: false,
|
|
friendly_name: 'wall_switch_double',
|
|
},
|
|
'0x0017880104e45543': {
|
|
retain: false,
|
|
friendly_name: 'led_controller_1',
|
|
},
|
|
'0x0017880104e45544': {
|
|
retain: false,
|
|
friendly_name: 'led_controller_2',
|
|
},
|
|
'0x0017880104e45545': {
|
|
retain: false,
|
|
friendly_name: 'dimmer_wall_switch',
|
|
},
|
|
'0x0017880104e45547': {
|
|
retain: false,
|
|
friendly_name: 'curtain',
|
|
},
|
|
'0x0017880104e45548': {
|
|
retain: false,
|
|
friendly_name: 'fan',
|
|
},
|
|
'0x0017880104e45549': {
|
|
retain: false,
|
|
friendly_name: 'siren',
|
|
},
|
|
'0x0017880104e45529': {
|
|
retain: false,
|
|
friendly_name: 'unsupported2',
|
|
},
|
|
'0x0017880104e45550': {
|
|
retain: false,
|
|
friendly_name: 'thermostat',
|
|
},
|
|
'0x0017880104e45551': {
|
|
retain: false,
|
|
friendly_name: 'smart vent',
|
|
},
|
|
'0x0017880104e45552': {
|
|
retain: false,
|
|
friendly_name: 'j1',
|
|
},
|
|
'0x0017880104e45553': {
|
|
retain: false,
|
|
friendly_name: 'bulb_enddevice',
|
|
},
|
|
'0x0017880104e45559': {
|
|
retain: false,
|
|
friendly_name: 'cc2530_router',
|
|
},
|
|
'0x0017880104e45560': {
|
|
retain: false,
|
|
friendly_name: 'livolo',
|
|
},
|
|
'0x90fd9ffffe4b64ae': {
|
|
retain: false,
|
|
friendly_name: 'tradfri_remote',
|
|
},
|
|
'0x90fd9ffffe4b64af': {
|
|
friendly_name: 'roller_shutter',
|
|
},
|
|
'0x90fd9ffffe4b64ax': {
|
|
friendly_name: 'ZNLDP12LM',
|
|
},
|
|
'0x90fd9ffffe4b64aa': {
|
|
friendly_name: 'SP600_OLD',
|
|
},
|
|
'0x90fd9ffffe4b64ab': {
|
|
friendly_name: 'SP600_NEW',
|
|
},
|
|
'0x90fd9ffffe4b64ac': {
|
|
friendly_name: 'MKS-CM-W5',
|
|
},
|
|
'0x0017880104e45526': {
|
|
friendly_name: 'GL-S-007ZS',
|
|
},
|
|
'0x0017880104e43559': {
|
|
friendly_name: 'U202DST600ZB',
|
|
},
|
|
'0xf4ce368a38be56a1': {
|
|
retain: false,
|
|
friendly_name: 'zigfred_plus',
|
|
front_surface_enabled: 'true',
|
|
dimmer_1_enabled: 'true',
|
|
dimmer_1_dimming_enabled: 'true',
|
|
dimmer_2_enabled: 'true',
|
|
dimmer_2_dimming_enabled: 'true',
|
|
dimmer_3_enabled: 'true',
|
|
dimmer_3_dimming_enabled: 'true',
|
|
dimmer_4_enabled: 'true',
|
|
dimmer_4_dimming_enabled: 'true',
|
|
cover_1_enabled: 'true',
|
|
cover_1_tilt_enabled: 'true',
|
|
cover_2_enabled: 'true',
|
|
cover_2_tilt_enabled: 'true',
|
|
},
|
|
'0x0017880104e44559': {
|
|
friendly_name: '3157100_thermostat',
|
|
},
|
|
'0x0017880104a44559': {
|
|
friendly_name: 'J1_cover',
|
|
},
|
|
'0x0017882104a44559': {
|
|
friendly_name: 'TS0601_thermostat',
|
|
},
|
|
'0x0017882104a44560': {
|
|
friendly_name: 'TS0601_switch',
|
|
},
|
|
'0x0017882104a44562': {
|
|
friendly_name: 'TS0601_cover_switch',
|
|
},
|
|
'0x0017882194e45543': {
|
|
friendly_name: 'QS-Zigbee-D02-TRIAC-2C-LN',
|
|
},
|
|
'0x0017880104e45724': {
|
|
friendly_name: 'GLEDOPTO_2ID',
|
|
},
|
|
'0x0017880104e45561': {
|
|
friendly_name: 'temperature_sensor',
|
|
},
|
|
'0x0017880104e45562': {
|
|
friendly_name: 'heating_actuator',
|
|
},
|
|
},
|
|
groups: {
|
|
1: {
|
|
friendly_name: 'group_1',
|
|
retain: false,
|
|
},
|
|
2: {
|
|
friendly_name: 'group_2',
|
|
retain: false,
|
|
},
|
|
15071: {
|
|
friendly_name: 'group_tradfri_remote',
|
|
retain: false,
|
|
},
|
|
11: {
|
|
friendly_name: 'group_with_tradfri',
|
|
retain: false,
|
|
},
|
|
12: {
|
|
friendly_name: 'thermostat_group',
|
|
retain: false,
|
|
},
|
|
14: {
|
|
friendly_name: 'switch_group',
|
|
retain: false,
|
|
},
|
|
21: {
|
|
friendly_name: 'gledopto_group',
|
|
},
|
|
9: {
|
|
friendly_name: 'ha_discovery_group',
|
|
},
|
|
},
|
|
};
|
|
|
|
beforeEach(() => {
|
|
data.writeDefaultConfiguration(DEFAULT_CONFIG_V2);
|
|
settings.reRead();
|
|
});
|
|
|
|
it('no change needed - only add version', () => {
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
});
|
|
|
|
it('remove all', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
|
|
settings.set(['homeassistant', 'legacy_triggers'], true);
|
|
settings.set(['homeassistant', 'legacy_entity_attributes'], true);
|
|
settings.set(['ota', 'ikea_ota_use_test_url'], true);
|
|
settings.set(['advanced', 'homeassistant_legacy_triggers'], true);
|
|
settings.set(['advanced', 'homeassistant_legacy_entity_attributes'], true);
|
|
settings.set(['permit_join'], true);
|
|
settings.set(['advanced', 'ikea_ota_use_test_url'], true);
|
|
settings.set(['advanced', 'legacy_api'], true);
|
|
settings.set(['advanced', 'legacy_availability_payload'], true);
|
|
settings.set(['advanced', 'soft_reset_timeout'], 12);
|
|
settings.set(['advanced', 'report'], true);
|
|
settings.set(['advanced', 'availability_timeout'], 65);
|
|
settings.set(['advanced', 'availability_blocklist'], ['abcd', 'efgh']);
|
|
settings.set(['advanced', 'availability_passlist'], ['abcd']);
|
|
settings.set(['advanced', 'availability_blacklist'], ['abcd', 'efgh']);
|
|
settings.set(['advanced', 'availability_whitelist'], ['abcd', 'efgh']);
|
|
settings.set(['device_options', 'legacy'], true);
|
|
settings.set(['devices', '0x18fc2600000d7ae2', 'retrieve_state'], true);
|
|
settings.set(['devices', '0x000b57fffec6a5b2', 'retrieve_state'], true);
|
|
settings.set(['groups', '15071', 'retrieve_state'], true);
|
|
settings.set(['groups', '12', 'devices'], ['0x0017880104e45521', '0x0017880104e45524']);
|
|
settings.set(['external_converters'], ['zyx.js']);
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
permit_join: true,
|
|
homeassistant: {
|
|
legacy_triggers: true,
|
|
legacy_entity_attributes: true,
|
|
},
|
|
ota: {ikea_ota_use_test_url: true},
|
|
advanced: {
|
|
homeassistant_legacy_triggers: true,
|
|
homeassistant_legacy_entity_attributes: true,
|
|
ikea_ota_use_test_url: true,
|
|
legacy_api: true,
|
|
legacy_availability_payload: true,
|
|
soft_reset_timeout: 12,
|
|
report: true,
|
|
availability_timeout: 65,
|
|
availability_blocklist: ['abcd', 'efgh'],
|
|
availability_passlist: ['abcd'],
|
|
availability_blacklist: ['abcd', 'efgh'],
|
|
availability_whitelist: ['abcd', 'efgh'],
|
|
},
|
|
device_options: {legacy: true},
|
|
devices: {
|
|
'0x18fc2600000d7ae2': {retrieve_state: true},
|
|
'0x000b57fffec6a5b2': {retrieve_state: true},
|
|
},
|
|
groups: {
|
|
15071: {retrieve_state: true},
|
|
12: {devices: ['0x0017880104e45521', '0x0017880104e45524']},
|
|
},
|
|
external_converters: ['zyx.js'],
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings.advanced).toStrictEqual({});
|
|
expect(migratedSettings.device_options).toStrictEqual({});
|
|
expect(migratedSettings.ota).toStrictEqual({});
|
|
expect(migratedSettings.homeassistant).toStrictEqual({});
|
|
|
|
// defaults added automatically when pushing to these keys, remove to match against default (verified by above expects)
|
|
migratedSettings.homeassistant = false;
|
|
delete migratedSettings.advanced;
|
|
delete migratedSettings.device_options;
|
|
delete migratedSettings.ota;
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).toContain('homeassistant.legacy_triggers');
|
|
expect(migrationNotesContent).toContain('homeassistant.legacy_entity_attributes');
|
|
expect(migrationNotesContent).toContain('ota.ikea_ota_use_test_url');
|
|
expect(migrationNotesContent).toContain('permit_join');
|
|
expect(migrationNotesContent).toContain('advanced.legacy_api');
|
|
expect(migrationNotesContent).toContain('advanced.legacy_availability_payload');
|
|
expect(migrationNotesContent).toContain('advanced.soft_reset_timeout');
|
|
expect(migrationNotesContent).toContain('advanced.report');
|
|
expect(migrationNotesContent).toContain('advanced.availability_timeout');
|
|
expect(migrationNotesContent).toContain('advanced.availability_blocklist');
|
|
expect(migrationNotesContent).toContain('advanced.availability_passlist');
|
|
expect(migrationNotesContent).toContain('advanced.availability_blacklist');
|
|
expect(migrationNotesContent).toContain('advanced.availability_whitelist');
|
|
expect(migrationNotesContent).toContain('device_options.legacy');
|
|
expect(migrationNotesContent).toContain('(devices|groups).xyz.retrieve_state');
|
|
expect(migrationNotesContent).toContain('groups.xyz.devices');
|
|
expect(migrationNotesContent).toContain('External converters are now automatically loaded');
|
|
});
|
|
|
|
it('remove partial', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
|
|
settings.set(['advanced', 'homeassistant_legacy_triggers'], true);
|
|
settings.set(['advanced', 'homeassistant_legacy_entity_attributes'], true);
|
|
settings.set(['permit_join'], true);
|
|
settings.set(['advanced', 'ikea_ota_use_test_url'], true);
|
|
settings.set(['advanced', 'legacy_api'], true);
|
|
settings.set(['advanced', 'legacy_availability_payload'], false);
|
|
settings.set(['advanced', 'soft_reset_timeout'], 16);
|
|
settings.set(['advanced', 'report'], true);
|
|
settings.set(['advanced', 'availability_timeout'], 64);
|
|
settings.set(['advanced', 'availability_passlist'], []);
|
|
settings.set(['device_options', 'legacy'], true);
|
|
settings.set(['groups', '12', 'devices'], ['0x0017880104e45521', '0x0017880104e45524']);
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
permit_join: true,
|
|
advanced: {
|
|
homeassistant_legacy_triggers: true,
|
|
homeassistant_legacy_entity_attributes: true,
|
|
ikea_ota_use_test_url: true,
|
|
legacy_api: true,
|
|
legacy_availability_payload: false,
|
|
soft_reset_timeout: 16,
|
|
report: true,
|
|
availability_timeout: 64,
|
|
availability_passlist: [],
|
|
},
|
|
device_options: {legacy: true},
|
|
groups: {12: {devices: ['0x0017880104e45521', '0x0017880104e45524']}},
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings.advanced).toStrictEqual({});
|
|
expect(migratedSettings.device_options).toStrictEqual({});
|
|
|
|
// defaults added automatically when pushing to these keys, remove to match against default (verified by above expects)
|
|
migratedSettings.homeassistant = false;
|
|
delete migratedSettings.advanced;
|
|
delete migratedSettings.device_options;
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).toContain('homeassistant.legacy_triggers');
|
|
expect(migrationNotesContent).toContain('homeassistant.legacy_entity_attributes');
|
|
expect(migrationNotesContent).toContain('ota.ikea_ota_use_test_url');
|
|
expect(migrationNotesContent).toContain('permit_join');
|
|
expect(migrationNotesContent).toContain('advanced.legacy_api');
|
|
expect(migrationNotesContent).not.toContain('advanced.legacy_availability_payload'); // was false, no impact
|
|
expect(migrationNotesContent).toContain('advanced.soft_reset_timeout');
|
|
expect(migrationNotesContent).toContain('advanced.report');
|
|
expect(migrationNotesContent).toContain('advanced.availability_timeout');
|
|
expect(migrationNotesContent).not.toContain('advanced.availability_blocklist');
|
|
expect(migrationNotesContent).not.toContain('advanced.availability_passlist'); // empty array
|
|
expect(migrationNotesContent).not.toContain('advanced.availability_blacklist');
|
|
expect(migrationNotesContent).not.toContain('advanced.availability_whitelist');
|
|
expect(migrationNotesContent).toContain('device_options.legacy');
|
|
expect(migrationNotesContent).not.toContain('(devices|groups).xyz.retrieve_state');
|
|
expect(migrationNotesContent).toContain('groups.xyz.devices');
|
|
});
|
|
|
|
it('changes log_level', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
afterSettings.advanced = {log_level: 'warning'};
|
|
|
|
settings.set(['advanced', 'log_level'], 'warn');
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
advanced: {
|
|
log_level: 'warn',
|
|
},
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).toContain(`Log level 'warn' has been renamed to 'warning'.`);
|
|
});
|
|
|
|
it('does not changes already migrated log_level', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
afterSettings.advanced = {log_level: 'warning'};
|
|
|
|
settings.set(['advanced', 'log_level'], 'warning');
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
advanced: {
|
|
log_level: 'warning',
|
|
},
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).not.toContain(`Log level 'warn' has been renamed to 'warning'.`);
|
|
});
|
|
|
|
it('does not changes other log_level', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
afterSettings.advanced = {log_level: 'info'};
|
|
|
|
settings.set(['advanced', 'log_level'], 'info');
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
advanced: {
|
|
log_level: 'info',
|
|
},
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).not.toContain(`Log level 'warn' has been renamed to 'warning'.`);
|
|
});
|
|
|
|
it('transfer all', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
afterSettings.advanced = {
|
|
transmit_power: 12,
|
|
output: 'attribute',
|
|
};
|
|
afterSettings.serial.baudrate = 115200;
|
|
afterSettings.serial.rtscts = true;
|
|
afterSettings.blocklist = ['abcd'];
|
|
afterSettings.passlist = ['efgh'];
|
|
afterSettings.homeassistant = {
|
|
discovery_topic: 'ha_disc',
|
|
status_topic: 'ha_stat',
|
|
};
|
|
// Here, the `experimental` section is also explicitly removed after the transfer, so the empty object is gone
|
|
// afterSettings.experimental = {}; // caused by pushing to key and removing all
|
|
|
|
settings.set(['advanced', 'homeassistant_discovery_topic'], 'ha_disc');
|
|
settings.set(['advanced', 'homeassistant_status_topic'], 'ha_stat');
|
|
settings.set(['advanced', 'baudrate'], 115200);
|
|
settings.set(['advanced', 'rtscts'], true); // only deleted since also below
|
|
settings.set(['serial', 'rtscts'], true);
|
|
settings.set(['experimental', 'transmit_power'], 12);
|
|
settings.set(['experimental', 'output'], 'attribute');
|
|
settings.set(['ban'], ['abcd']);
|
|
settings.set(['whitelist'], ['efgh']);
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
advanced: {
|
|
homeassistant_discovery_topic: 'ha_disc',
|
|
homeassistant_status_topic: 'ha_stat',
|
|
baudrate: 115200,
|
|
rtscts: true,
|
|
},
|
|
serial: {
|
|
rtscts: true,
|
|
},
|
|
experimental: {
|
|
transmit_power: 12,
|
|
output: 'attribute',
|
|
},
|
|
ban: ['abcd'],
|
|
whitelist: ['efgh'],
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).toContain(
|
|
`HA discovery_topic was moved from advanced.homeassistant_discovery_topic to homeassistant.discovery_topic.`,
|
|
);
|
|
expect(migrationNotesContent).toContain(
|
|
`HA status_topic was moved from advanced.homeassistant_status_topic to homeassistant.status_topic.`,
|
|
);
|
|
expect(migrationNotesContent).toContain(`Baudrate was moved from advanced.baudrate to serial.baudrate.`);
|
|
expect(migrationNotesContent).toContain(`RTSCTS was moved from advanced.rtscts to serial.rtscts.`);
|
|
expect(migrationNotesContent).toContain(`Transmit power was moved from experimental.transmit_power to advanced.transmit_power.`);
|
|
expect(migrationNotesContent).toContain(`Output was moved from experimental.output to advanced.output.`);
|
|
expect(migrationNotesContent).toContain(`ban was renamed to passlist.`);
|
|
expect(migrationNotesContent).toContain(`whitelist was renamed to passlist.`);
|
|
expect(migrationNotesContent).toContain(`The entire experimental section was removed.`);
|
|
});
|
|
|
|
it('transfer partial', () => {
|
|
// @ts-expect-error workaround
|
|
const beforeSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
// @ts-expect-error workaround
|
|
const afterSettings = objectAssignDeep.noMutate({}, settings.getInternalSettings());
|
|
afterSettings.version = 2;
|
|
afterSettings.advanced = {}; // caused by pushing to key and removing all
|
|
afterSettings.serial.baudrate = 115200;
|
|
afterSettings.serial.rtscts = true;
|
|
afterSettings.blocklist = ['abcd', 'efgh'];
|
|
afterSettings.homeassistant = {
|
|
discovery_topic: 'ha_disc_newer', // keeps the newer value, just removes the old path
|
|
};
|
|
|
|
settings.set(['homeassistant', 'discovery_topic'], 'ha_disc_newer');
|
|
settings.set(['advanced', 'homeassistant_discovery_topic'], 'ha_disc');
|
|
settings.set(['advanced', 'baudrate'], 115200);
|
|
settings.set(['advanced', 'rtscts'], true); // only deleted since also below
|
|
settings.set(['serial', 'rtscts'], true);
|
|
settings.set(['ban'], ['abcd']);
|
|
settings.set(['blocklist'], ['efgh']);
|
|
|
|
// console.log(JSON.stringify(settings.getInternalSettings(), undefined, 2));
|
|
|
|
expect(settings.getInternalSettings()).toStrictEqual(
|
|
// @ts-expect-error workaround
|
|
objectAssignDeep.noMutate(beforeSettings, {
|
|
homeassistant: {discovery_topic: 'ha_disc_newer'},
|
|
advanced: {
|
|
homeassistant_discovery_topic: 'ha_disc',
|
|
baudrate: 115200,
|
|
rtscts: true,
|
|
},
|
|
serial: {
|
|
rtscts: true,
|
|
},
|
|
ban: ['abcd'],
|
|
blocklist: ['efgh'],
|
|
}),
|
|
);
|
|
|
|
settingsMigration.migrateIfNecessary();
|
|
|
|
const migratedSettings = settings.getInternalSettings();
|
|
// console.log(JSON.stringify(migratedSettings, undefined, 2));
|
|
|
|
expect(migratedSettings).toStrictEqual(afterSettings);
|
|
expect(existsSync(mockedData.joinPath('configuration_backup_v1.yaml'))).toStrictEqual(true);
|
|
const migrationNotes = mockedData.joinPath('migration-1.x.x-to-2.0.0.log');
|
|
expect(existsSync(migrationNotes)).toStrictEqual(true);
|
|
const migrationNotesContent = readFileSync(migrationNotes, 'utf8');
|
|
expect(migrationNotesContent).toContain(`[TRANSFER] Baudrate was moved from advanced.baudrate to serial.baudrate.`);
|
|
expect(migrationNotesContent).toContain(`[REMOVAL] RTSCTS was moved from advanced.rtscts to serial.rtscts.`);
|
|
expect(migrationNotesContent).toContain(`[TRANSFER] ban was renamed to passlist.`);
|
|
});
|
|
});
|
|
});
|