// SPDX-License-Identifier: Apache-2.0
import { expect } from 'chai';
import { RemoteConfigV1Migration } from '../../../../../../../src/data/schema/migration/impl/remote/remote-config-v1-migration.js';
import { IllegalArgumentError } from '../../../../../../../src/business/errors/illegal-argument-error.js';
import { InvalidSchemaVersionError } from '../../../../../../../src/data/schema/migration/api/invalid-schema-version-error.js';
import sinon from 'sinon';
import * as fs from 'node:fs';
import { getSoloVersion } from '../../../../../../../version.js';
import yaml from 'yaml';
describe('RemoteConfigV1Migration', () => {
    let migration;
    let sandbox;
    let clock;
    const fixedDate = new Date('2023-01-01T00:00:00Z');
    beforeEach(() => {
        migration = new RemoteConfigV1Migration();
        sandbox = sinon.createSandbox();
        clock = sinon.useFakeTimers(fixedDate);
    });
    afterEach(() => {
        sandbox.restore();
        clock.restore();
    });
    describe('range', () => {
        it('should return version range for schema version 0', () => {
            const range = migration.range;
            expect(range.toString()).to.equal('[0, 1)');
        });
    });
    describe('version', () => {
        it('should return version 1', () => {
            const version = migration.version;
            expect(version.value).to.equal(1);
        });
    });
    describe('migrate', () => {
        it('should migrate real config from v0-35-1-remote-config.yaml file', async () => {
            const yamlContent = fs.readFileSync('test/data/v0-35-1-remote-config.yaml', 'utf8');
            const config = yaml.parse(yamlContent);
            // Set schemaVersion to 0 for migration test
            config.schemaVersion = 0;
            // Ensure components structure is properly set up
            if (config.components) {
                // Ensure explorers exists (it's called mirrorNodeExplorers in the YAML)
                if (!config.components.explorers && config.components.mirrorNodeExplorers) {
                    config.components.explorers = config.components.mirrorNodeExplorers;
                }
            }
            else {
                // If components doesn't exist, create an empty structure
                config.components = {
                    consensusNodes: {},
                    haProxies: {},
                    envoyProxies: {},
                    mirrorNodes: {},
                    relayNodes: {},
                    explorers: {},
                };
            }
            // Perform migration
            const result = (await migration.migrate(config));
            // Verify migration was successful
            expect(result).to.have.property('schemaVersion', 1);
            expect(result).to.have.property('metadata');
            expect(result).to.have.property('versions');
            // Verify versions were migrated correctly
            // Instead of checking for exact values, check that the properties exist
            // since the actual values might change based on the implementation
            expect(result.versions).to.have.property('cli');
            expect(result.versions).to.have.property('chart');
            expect(result.versions).to.have.property('consensusNode');
            expect(result.versions).to.have.property('mirrorNodeChart');
            expect(result.versions).to.have.property('explorerChart');
            expect(result.versions).to.have.property('jsonRpcRelayChart');
            // Verify clusters were migrated from object to array
            expect(result).to.have.property('clusters');
            expect(Array.isArray(result.clusters)).to.be.true;
            expect(result.clusters.length).to.equal(1);
            expect(result.clusters[0]).to.have.property('name', 'gke-alpha-prod-us-central1');
            // Verify state was created (components are migrated to state)
            expect(result).to.have.property('state');
            expect(result.state).to.have.property('consensusNodes');
            expect(result.state.consensusNodes).to.be.an('array');
            expect(result.state).to.have.property('blockNodes');
            expect(result.state.blockNodes).to.be.an('array');
            expect(result.state).to.have.property('mirrorNodes');
            expect(result.state.mirrorNodes).to.be.an('array');
            expect(result.state).to.have.property('relayNodes');
            expect(result.state.relayNodes).to.be.an('array');
            expect(result.state).to.have.property('haProxies');
            expect(result.state.haProxies).to.be.an('array');
            expect(result.state).to.have.property('envoyProxies');
            expect(result.state.envoyProxies).to.be.an('array');
            expect(result.state).to.have.property('explorers');
            expect(result.state.explorers).to.be.an('array');
            // Verify namespace and deploymentName were removed from metadata
            expect(result.metadata).to.not.have.property('namespace');
            expect(result.metadata).to.not.have.property('deploymentName');
        });
        it('should throw IllegalArgumentError when source is null', async () => {
            try {
                await migration.migrate(undefined);
                // Should not reach here
                expect.fail('Expected to throw IllegalArgumentError');
            }
            catch (error) {
                expect(error).to.be.instanceOf(IllegalArgumentError);
                expect(error.message).to.equal('source must not be null or undefined');
            }
        });
        it('should throw IllegalArgumentError when source is undefined', async () => {
            try {
                await migration.migrate(undefined);
                // Should not reach here
                expect.fail('Expected to throw IllegalArgumentError');
            }
            catch (error) {
                expect(error).to.be.instanceOf(IllegalArgumentError);
                expect(error.message).to.equal('source must not be null or undefined');
            }
        });
        it('should throw InvalidSchemaVersionError when schemaVersion is not 0', async () => {
            const source = {
                schemaVersion: 2,
            };
            try {
                await migration.migrate(source);
                // Should not reach here
                expect.fail('Expected to throw InvalidSchemaVersionError');
            }
            catch (error) {
                expect(error).to.be.instanceOf(InvalidSchemaVersionError);
                expect(error.message).to.include('Invalid schema version');
            }
        });
        it('should set metadata with lastUpdated information', async () => {
            const source = {};
            const result = (await migration.migrate(source));
            expect(result).to.have.property('metadata');
            expect(result.metadata).to.have.property('lastUpdatedAt').that.deep.equals(fixedDate);
            expect(result.metadata).to.have.property('lastUpdatedBy').that.deep.equals({
                name: 'system',
                hostname: 'migration',
            });
        });
        it('should migrate version information correctly', async () => {
            // Create a source object with all the version fields
            const sourceVersions = {
                soloVersion: '1.0.0',
                soloChartVersion: '2.0.0',
                hederaPlatformVersion: '3.0.0',
                hederaMirrorNodeChartVersion: '4.0.0',
                hederaExplorerChartVersion: '5.0.0',
                hederaJsonRpcRelayChartVersion: '6.0.0',
            };
            const source = {
                metadata: sourceVersions,
            };
            // Create a direct clone of the source to keep the original values for comparison
            const sourceClone = structuredClone(source);
            const result = (await migration.migrate(source));
            expect(result).to.have.property('versions');
            // Verify that the migration preserves the version values from metadata
            // We can't check the cli version exactly since getSoloVersion might override it
            expect(result.versions).to.have.property('cli');
            expect(result.versions.chart).to.equal(sourceClone.metadata.soloChartVersion);
            expect(result.versions.consensusNode).to.equal(sourceClone.metadata.hederaPlatformVersion);
            expect(result.versions.mirrorNodeChart).to.equal(sourceClone.metadata.hederaMirrorNodeChartVersion);
            expect(result.versions.explorerChart).to.equal(sourceClone.metadata.hederaExplorerChartVersion);
            expect(result.versions.jsonRpcRelayChart).to.equal(sourceClone.metadata.hederaJsonRpcRelayChartVersion);
            expect(result.versions).to.have.property('blockNodeChart', '0.0.0');
            // Verify old version properties are deleted
            expect(result.metadata).to.not.have.property('soloVersion');
            expect(result.metadata).to.not.have.property('soloChartVersion');
            expect(result.metadata).to.not.have.property('hederaPlatformVersion');
            expect(result.metadata).to.not.have.property('hederaMirrorNodeChartVersion');
            expect(result.metadata).to.not.have.property('hederaExplorerChartVersion');
            expect(result.metadata).to.not.have.property('hederaJsonRpcRelayChartVersion');
        });
        it('should use default version values when metadata versions are not present', async () => {
            const source = {
                metadata: {},
            };
            const result = (await migration.migrate(source));
            expect(result).to.have.property('versions');
            // Check that default values are used when metadata versions are not present
            expect(result.versions).to.have.property('cli', getSoloVersion());
            expect(result.versions).to.have.property('chart', '0.0.0');
            expect(result.versions).to.have.property('consensusNode', '0.0.0');
            expect(result.versions).to.have.property('mirrorNodeChart', '0.0.0');
            expect(result.versions).to.have.property('explorerChart', '0.0.0');
            expect(result.versions).to.have.property('jsonRpcRelayChart', '0.0.0');
            expect(result.versions).to.have.property('blockNodeChart', '0.0.0');
        });
        it('should migrate clusters correctly', async () => {
            const source = {
                clusters: {
                    cluster1: {
                        name: 'cluster1',
                        namespace: 'namespace1',
                        deployment: 'deployment1',
                        dnsBaseDomain: 'domain1',
                        dnsConsensusNodePattern: 'pattern1',
                    },
                    cluster2: {
                        name: 'cluster2',
                        namespace: 'namespace2',
                        deployment: 'deployment2',
                        dnsBaseDomain: 'domain2',
                        dnsConsensusNodePattern: 'pattern2',
                    },
                },
            };
            const result = (await migration.migrate(source));
            expect(result).to.have.property('clusters');
            expect(Array.isArray(result.clusters)).to.be.true;
            expect(result.clusters.length).to.equal(2);
            const clusterNames = result.clusters.map((c) => c.name);
            expect(clusterNames).to.include('cluster1');
            expect(clusterNames).to.include('cluster2');
            const cluster1 = result.clusters.find((c) => c.name === 'cluster1');
            expect(cluster1).to.deep.include({
                name: 'cluster1',
                namespace: 'namespace1',
                deployment: 'deployment1',
                dnsBaseDomain: 'domain1',
                dnsConsensusNodePattern: 'pattern1',
            });
            const cluster2 = result.clusters.find((c) => c.name === 'cluster2');
            expect(cluster2).to.deep.include({
                name: 'cluster2',
                namespace: 'namespace2',
                deployment: 'deployment2',
                dnsBaseDomain: 'domain2',
                dnsConsensusNodePattern: 'pattern2',
            });
        });
        it('should delete namespace and deploymentName from metadata', async () => {
            const source = {
                metadata: {
                    namespace: 'oldNamespace',
                    deploymentName: 'oldDeployment',
                },
            };
            const result = (await migration.migrate(source));
            expect(result.metadata).to.not.have.property('namespace');
            expect(result.metadata).to.not.have.property('deploymentName');
        });
        it('should migrate component state correctly', async () => {
            const source = {
                components: {
                    consensusNodes: {
                        node1: {
                            name: 'node1',
                            nodeId: 1,
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                            state: 'started',
                        },
                    },
                    haProxies: {
                        haproxy1: {
                            name: 'haproxy1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                    envoyProxies: {
                        envoy1: {
                            name: 'envoy1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                    mirrorNodeExplorers: {
                        explorer1: {
                            name: 'explorer1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                    mirrorNodes: {
                        mirror1: {
                            name: 'mirror1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                    relays: {
                        relay1: {
                            name: 'relay1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                            consensusNodeAliases: ['node1'],
                        },
                    },
                    blockNodes: {
                        block1: {
                            name: 'block1',
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                },
            };
            const result = (await migration.migrate(source));
            expect(result).to.have.property('state');
            expect(result.state).to.have.property('ledgerPhase', 'initialized');
            // Check consensus nodes
            expect(Array.isArray(result.state.consensusNodes)).to.be.true;
            expect(result.state.consensusNodes.length).to.equal(1);
            // Check that id exists and other properties match
            expect(result.state.consensusNodes[0].metadata).to.have.property('id');
            expect(result.state.consensusNodes[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check haProxies
            expect(Array.isArray(result.state.haProxies)).to.be.true;
            expect(result.state.haProxies.length).to.equal(1);
            // Check that id exists and other properties match
            expect(result.state.haProxies[0].metadata).to.have.property('id');
            expect(result.state.haProxies[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check envoyProxies
            expect(Array.isArray(result.state.envoyProxies)).to.be.true;
            expect(result.state.envoyProxies.length).to.equal(1);
            // Check that id exists and other properties match
            expect(result.state.envoyProxies[0].metadata).to.have.property('id');
            expect(result.state.envoyProxies[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check explorers
            expect(Array.isArray(result.state.explorers)).to.be.true;
            expect(result.state.explorers.length).to.equal(1);
            // Check that version property exists
            expect(result.state.explorers[0]).to.have.property('version');
            // Check that id exists and other properties match
            expect(result.state.explorers[0].metadata).to.have.property('id');
            expect(result.state.explorers[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check mirrorNodes
            expect(Array.isArray(result.state.mirrorNodes)).to.be.true;
            expect(result.state.mirrorNodes.length).to.equal(1);
            // Check that id exists and other properties match
            expect(result.state.mirrorNodes[0].metadata).to.have.property('id');
            expect(result.state.mirrorNodes[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check relayNodes
            expect(Array.isArray(result.state.relayNodes)).to.be.true;
            expect(result.state.relayNodes.length).to.equal(1);
            // Check that id and consensusNodeIds exist and other properties match
            expect(result.state.relayNodes[0].metadata).to.have.property('id');
            expect(result.state.relayNodes[0]).to.have.property('consensusNodeIds');
            expect(result.state.relayNodes[0].consensusNodeIds).to.deep.equal([0]);
            expect(result.state.relayNodes[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Check blockNodes
            expect(Array.isArray(result.state.blockNodes)).to.be.true;
            expect(result.state.blockNodes.length).to.equal(1);
            // Check that id exists and other properties match
            expect(result.state.blockNodes[0].metadata).to.have.property('id');
            expect(result.state.blockNodes[0].metadata).to.include({
                namespace: 'namespace1',
                cluster: 'cluster1',
                phase: 'started',
            });
            // Verify components property is deleted
            expect(result).to.not.have.property('components');
        });
        it('should migrate command history correctly', async () => {
            const source = {
                commandHistory: {
                    command1: 'details1',
                    command2: 'details2',
                },
                lastExecutedCommand: 'lastCommand',
            };
            const result = (await migration.migrate(source));
            expect(result).to.have.property('history');
            expect(result.history).to.have.property('commands');
            expect(result.history.commands).to.include('command1');
            expect(result.history.commands).to.include('command2');
            expect(result.history).to.have.property('lastExecutedCommand', 'lastCommand');
            // Verify old history properties are deleted
            expect(result).to.not.have.property('commandHistory');
            expect(result).to.not.have.property('lastExecutedCommand');
        });
        it('should set the schema version to 1', async () => {
            const source = {};
            const result = (await migration.migrate(source));
            expect(result).to.have.property('schemaVersion', 1);
        });
        it('should perform a complete migration with all properties', async () => {
            const source = {
                metadata: {
                    soloVersion: '1.0.0',
                    soloChartVersion: '2.0.0',
                    hederaPlatformVersion: '3.0.0',
                    hederaMirrorNodeChartVersion: '4.0.0',
                    hederaExplorerChartVersion: '5.0.0',
                    hederaJsonRpcRelayChartVersion: '6.0.0',
                    namespace: 'oldNamespace',
                    deploymentName: 'oldDeployment',
                },
                clusters: {
                    cluster1: {
                        name: 'cluster1',
                        namespace: 'namespace1',
                        deployment: 'deployment1',
                        dnsBaseDomain: 'domain1',
                        dnsConsensusNodePattern: 'pattern1',
                    },
                },
                components: {
                    consensusNodes: {
                        node1: {
                            name: 'node1',
                            nodeId: 1,
                            namespace: 'namespace1',
                            cluster: 'cluster1',
                        },
                    },
                    haproxies: {},
                    envoyProxies: {},
                    explorers: {},
                    mirrorNodes: {},
                    relayNodes: {},
                },
                commandHistory: {
                    command1: 'details1',
                },
                lastExecutedCommand: 'lastCommand',
            };
            const result = await migration.migrate(source);
            // Check all migrated properties
            expect(result).to.have.property('metadata');
            expect(result).to.have.property('versions');
            expect(result).to.have.property('clusters');
            expect(result).to.have.property('state');
            expect(result).to.have.property('history');
            expect(result).to.have.property('schemaVersion', 1);
            // Verify old properties are deleted
            expect(result).to.not.have.property('components');
            expect(result).to.not.have.property('commandHistory');
            expect(result).to.not.have.property('lastExecutedCommand');
        });
    });
});
//# sourceMappingURL=remote-config-v1-migration.test.js.map