HEX
Server: LiteSpeed
System: Linux CentOS-79-64-minimal 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
User: vishn3436 (5293)
PHP: 8.0.15
Disabled: NONE
Upload Files
File: //scripts/script-server/web-src/tests/unit/scriptConfig_test.js
import {clearArray, removeElement, SocketClosedError} from '@/common/utils/common';
import scriptConfig, {__RewireAPI__ as SocketRewireAPI} from '@/main-app/store/scriptConfig';
import Vuex from 'vuex';
import {createScriptServerTestVue, timeout, vueTicks} from './test_utils'


const localVue = createScriptServerTestVue();
localVue.use(Vuex);

const DEFAULT_SCRIPT_NAME = 'testScript'

function createStore() {
    return new Vuex.Store({
        modules: {
            scriptConfig: scriptConfig(),
            scripts: {
                namespaced: true,
                state: {
                    selectedScript: DEFAULT_SCRIPT_NAME
                }
            }
        }
    });
}

const DEFAULT_PARAM2_VALUES = ['abc', 'def', 'xyz'];

function createConfig(extraParameters = []) {

    const parameters = [{
        name: 'param1',
        'default': 123
    },
        {
            name: 'param2',
            type: 'list',
            values: DEFAULT_PARAM2_VALUES,
            'default': 'def'
        },
        ...extraParameters];

    return {
        name: DEFAULT_SCRIPT_NAME,
        description: 'some script description',
        parameters: parameters
    };
}

function createConfigEvent(config) {
    return JSON.stringify({
        event: 'initialConfig',
        data: config
    });
}

function createReloadModelEvent(config) {
    return JSON.stringify({
        event: 'reloadedConfig',
        data: config
    });
}

function createReloadModelRequest(clientModelId, parameterValues) {
    return JSON.stringify({
        event: 'reloadModelValues',
        data: {parameterValues, clientModelId}
    });
}

function createAddParameterEvent(parameter) {
    return JSON.stringify({
        event: 'parameterAdded',
        data: parameter
    });
}

function createUpdateParameterEvent(parameter, clientStateVersion) {
    return JSON.stringify({
        event: 'parameterChanged',
        data: {clientStateVersion, ...parameter}
    });
}

function createRemoveParameterEvent(parameter, clientStateVersion) {
    return JSON.stringify({
        event: 'parameterRemoved',
        data: {'parameterName': parameter, clientStateVersion}
    });
}

function createSentValueEvent(parameter, value, clientStateVersion) {
    return JSON.stringify({
        'event': 'parameterValue',
        data: {parameter, value, clientStateVersion}
    });
}

function createInitialValuesEvent(parameterValues) {
    return JSON.stringify({
        'event': 'initialValues',
        data: {parameterValues}
    });
}

function createClientStateVersionAcceptedEvent(clientStateVersion) {
    return JSON.stringify({
        event: 'clientStateVersionAccepted',
        data: {clientStateVersion}
    });
}

describe('Test scriptConfig module', function () {
    let observers = [];

    beforeEach(function () {
        const sentData = [];

        this.sentData = sentData;

        SocketRewireAPI.__Rewire__('ReactiveWebSocket', function (path, callback) {
            const observer = {callback, path}
            observers.push(observer);
            let closed = false;

            this.close = () => {
                removeElement(observers, observer);
                closed = true;
            };

            this.send = (data) => {
                sentData.push(data);
            };

            this.isClosed = () => closed;
            this.isOpen = () => !closed;
        });

        SocketRewireAPI.__Rewire__('reconnectionDelay', 10);

        this.disconnectSocket = async function (awaitTimeout) {
            const observer = observers[0];
            observers.splice(0, 1);
            observer.callback.onError(new SocketClosedError());
            await timeout(awaitTimeout);
        };
    });

    function sendEventFromServer(event) {
        observers[0].callback.onNext(event)
    }

    function sendSocketError(error) {
        observers[0].callback.onError(error)
    }

    describe('Test config in single connection', function () {
        it('Test connect on reload', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();

            sendEventFromServer(createConfigEvent(config));

            expect(store.state.scriptConfig.scriptConfig).toEqual(config);
            expect(store.state.scriptConfig.parameters).toEqual(config.parameters);
            expect(observers[0].path).toEndWith('?initWithValues=false')
        });

        it('Test replace config', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();

            sendEventFromServer(createConfigEvent(config));

            config.name = 'new name';
            config.parameters.splice(1, 1);
            config.parameters.push({name: 'param3'});

            sendEventFromServer(createConfigEvent(config));

            expect(store.state.scriptConfig.scriptConfig).toEqual(config);
            expect(store.state.scriptConfig.parameters).toEqual(config.parameters);
        });

        it('Test add parameter', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();

            sendEventFromServer(createConfigEvent(config));

            const newParameter = {name: 'param3', type: 'multiselect', 'default': 'abc'};

            sendEventFromServer(createAddParameterEvent(newParameter));

            const expectedParameters = config.parameters.concat([newParameter]);
            newParameter.multiselect = true;

            expect(store.state.scriptConfig.parameters).toEqual(expectedParameters)
        });

        it('Test remove parameter', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();

            sendEventFromServer(createConfigEvent(config));

            sendEventFromServer(createRemoveParameterEvent(config.parameters[0].name, 1));

            config.parameters.splice(0, 1);

            expect(store.state.scriptConfig.parameters).toEqual(config.parameters)
        });
    });

    describe('Test reconnection', function () {
        it('Test disconnect before initial config', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const oldObserver = observers[0];
            sendSocketError(new SocketClosedError());

            expect(store.state.scriptConfig.scriptConfig).toBeNil()
            expect(store.state.scriptConfig.loadError).toBe('Failed to connect to the server')
        });

        it('Test disconnect after initial config', async function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const oldObserver = observers[0];

            const config = createConfig();
            sendEventFromServer(createConfigEvent(config));

            await this.disconnectSocket(50);

            const newObserver = observers[0];

            expect(newObserver).not.toEqual(oldObserver)
            expect(store.state.scriptConfig.scriptConfig).toEqual(config)
            expect(store.state.scriptConfig.loadError).toBeNil()
        });

        it('Test reload config after reconnect', async function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();
            sendEventFromServer(createConfigEvent(config));

            await this.disconnectSocket(50);

            expect(store.state.scriptConfig.scriptConfig).toEqual(config)
            config.name = 'new name';
            config.parameters.push({'name': 'param3'});

            sendEventFromServer(createConfigEvent(config));

            expect(store.state.scriptConfig.scriptConfig).toEqual(config)
            expect(store.state.scriptConfig.parameters).toEqual(config.parameters)
        });
    });

    describe('Test send current value', function () {
        it('Test send value', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});

            expect(this.sentData).toEqual([createSentValueEvent('param1', 123, 1)])
        });

        it('Test send same value multiple times', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});

            expect(this.sentData).toEqual([createSentValueEvent('param1', 123, 1)])
        });

        it('Test send same parameter different values', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 456});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});

            expect(this.sentData).toEqual([
                createSentValueEvent('param1', 123, 1),
                createSentValueEvent('param1', 456, 2),
                createSentValueEvent('param1', 123, 3)
            ])
        });

        it('Test send different parameters', function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param2', value: 123});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 'hello'});

            expect(this.sentData).toEqual([
                createSentValueEvent('param2', 123, 1),
                createSentValueEvent('param1', 'hello', 2)
            ])
        });

        it('Test resend values on reconnect', async function () {
            const store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig();
            sendEventFromServer(createConfigEvent(config));

            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param2', value: 123});
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 'hello'});

            clearArray(this.sentData);

            await this.disconnectSocket(100);

            expect(this.sentData).toEqual(
                [createInitialValuesEvent({param2: 123, param1: 'hello'})])
            expect(observers[0].path).toEndWith('?initWithValues=true')

            clearArray(this.sentData);

            sendEventFromServer(createConfigEvent(config));

            expect(this.sentData).toEqual([
                createSentValueEvent('param2', 123),
                createSentValueEvent('param1', 'hello')
            ])
        });
    });

    describe('Test reloadModel', function () {
        let store

        beforeEach(function () {
            store = createStore();

            store.dispatch('scriptConfig/reloadScript', {selectedScript: DEFAULT_SCRIPT_NAME});
            const config = createConfig();

            sendEventFromServer(createConfigEvent(config));
        })

        it('Test simple scenario', async function () {
            const parameterValues = {'param1': 'abc'}
            const modelId = 12345

            store.dispatch('scriptConfig/reloadModel',
                {parameterValues: parameterValues, clientModelId: modelId, scriptName: DEFAULT_SCRIPT_NAME});

            expect(this.sentData).toEqual([createReloadModelRequest(modelId, parameterValues)])
            expect(store.state.scriptConfig.loading).toBeTrue()

            clearArray(this.sentData)

            const newConfig = createConfig()
            newConfig.name = 'New name'
            newConfig.clientModelId = modelId

            sendEventFromServer(createReloadModelEvent(newConfig))

            expect(store.state.scriptConfig.loading).toBeFalse()
            expect(store.state.scriptConfig.scriptConfig).toEqual(newConfig)
            expect(this.sentData).toBeEmpty()
        })

        it('Test ignore reload for different script', async function () {
            store.state.scripts.selectedScript = 'another script'

            store.dispatch('scriptConfig/reloadModel',
                {
                    parameterValues: {'param1': 'abc'},
                    clientModelId: 12345,
                    scriptName: DEFAULT_SCRIPT_NAME
                });

            expect(this.sentData).toEqual([])
            expect(store.state.scriptConfig.loading).toBeFalse()
        })
    })

    describe('Test reload dependant parameter', function () {
        let store

        beforeEach(async function () {
            store = createStore();
            store.dispatch('scriptConfig/reloadScript', {selectedScript: 'my script'});

            const config = createConfig([{
                name: 'dependant param',
                type: 'list',
                values: DEFAULT_PARAM2_VALUES,
                'default': 'def',
                'requiredParameters': ['param1']
            }])

            sendEventFromServer(createConfigEvent(config));
            await vueTicks()
        })

        function assertLoading(paramName, expectedValue) {
            const dependantParam = store.state.scriptConfig.parameters.find(p => p.name === paramName)
            expect(dependantParam.loading || false).toEqual(expectedValue)
        }

        it('test set parameter to loading on dependency change', async function () {
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            await vueTicks()

            assertLoading('param1', false)
            assertLoading('param2', false)
            assertLoading('dependant param', true)
        })

        it('test reset loading getting server event', async function () {
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            await vueTicks()
            sendEventFromServer(createUpdateParameterEvent({name: 'dependant param', default: 'xyz'}, 3));
            await vueTicks()

            assertLoading('dependant param', false)
            const dependantParam = store.state.scriptConfig.parameters.find(p => p.name === 'dependant param')
            expect(dependantParam.default).toEqual('xyz')
        })

        it('test keep loading on old event', async function () {
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            await vueTicks()
            sendEventFromServer(createUpdateParameterEvent({name: 'dependant param', default: 'xyz'}, 0));
            await vueTicks()

            assertLoading('dependant param', true)

            const dependantParam = store.state.scriptConfig.parameters.find(p => p.name === 'dependant param')
            expect(dependantParam.default).toEqual('xyz')
        })

        it('test reset loading on clientStateVersionAccepted', async function () {
            store.dispatch('scriptConfig/sendParameterValue', {parameterName: 'param1', value: 123});
            await vueTicks()
            sendEventFromServer(createClientStateVersionAcceptedEvent(3));
            await vueTicks()

            assertLoading('dependant param', false)
        })

        it('test no loading when not dependency change', async function () {
            store.dispatch('scriptConfig/sendParameterValue', {
                parameterName: 'param2',
                value: DEFAULT_PARAM2_VALUES[1]
            });
            await vueTicks()

            assertLoading('param1', false)
            assertLoading('param2', false)
            assertLoading('dependant param', false)
        })


    })
});