Home Manual Reference Source Test

test/src/lib/server/WriteStream.spec.js

import { spy } from 'sinon';
import { StatusCodes, NodeClass } from 'node-opcua';
import Logger from 'gulplog';
import expect from '../../../expect';
import _WriteStream from '../../../../src/lib/server/WriteStream';
import _CreateNodeStream from '../../../../src/lib/server/CreateNodeStream';
import AtviseFile from '../../../../src/lib/server/AtviseFile';
import NodeId from '../../../../src/lib/model/opcua/NodeId';

class CreateNodeStream extends _CreateNodeStream {
  processChunk(file, handleErrors) {
    setTimeout(() => {
      handleErrors(null, StatusCodes.Good, (done) => done());
    }, 10);
  }
}

// Ignore dependencies in tests
class WriteStream extends _WriteStream {
  dependenciesFor() {
    return [];
  }
}

/** @test {WriteStream} */
describe('WriteStream', function () {
  /** @test {WriteStream#processErrorMessage} */
  describe('#processErrorMessage', function () {
    it('should include nodeId', function () {
      expect(
        WriteStream.prototype.processErrorMessage(
          new AtviseFile({
            path: 'src/AGENT/DISPLAYS/Main.display',
            base: 'src',
          })
        ),
        'to contain',
        'AGENT.DISPLAYS.Main'
      );
    });
  });

  /** @test {WriteStream#processChunk} */
  describe('#processChunk', function () {
    it('should forward errors', function () {
      const stream = new WriteStream(new CreateNodeStream());

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = (nodeId, value, callback) => callback(new Error('Test'));
      });

      return expect(
        [
          {
            nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS.Main'),
            typeDefinition: new NodeId('VariableTypes.ATVISE.Display', 0),
            nodeClass: NodeClass.Variable,
          },
        ],
        'when piped through',
        stream,
        'to error with',
        /Test/
      );
    });

    it('should forward synchronous errors', function () {
      const stream = new WriteStream(new CreateNodeStream());

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = () => {
          throw new Error('Sync test');
        };
      });

      return expect(
        [
          {
            nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS.Main'),
            typeDefinition: new NodeId('ns=1;s=VariableTypes.ATVISE.Display'),
            nodeClass: NodeClass.Variable,
          },
        ],
        'when piped through',
        stream,
        'to error with',
        /Sync test/
      );
    });

    it('should warn if access is denied', function () {
      const stream = new WriteStream(new CreateNodeStream());

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = (nodeId, value, callback) =>
          callback(null, StatusCodes.BadUserAccessDenied);
      });

      const warnSpy = spy();
      Logger.on('warn', warnSpy);

      return expect(
        [
          {
            nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS.Main'),
            typeDefinition: new NodeId('ns=1;s=VariableTypes.ATVISE.Display'),
            nodeClass: NodeClass.Variable,
          },
        ],
        'when piped through',
        stream,
        'to yield objects satisfying',
        'to have length',
        0
      )
        .then(() => expect(warnSpy, 'was called once'))
        .then(() => expect(warnSpy.lastCall, 'to satisfy', [/opened in atvise builder/]));
    });

    it('should push non-variable files', function () {
      const createStream = new CreateNodeStream();
      const stream = new WriteStream(createStream);
      stream.pipe(createStream);

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = (nodeId, value, callback) =>
          callback(null, StatusCodes.Good);
      });

      const file = {
        nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS'),
        typeDefinition: new NodeId('ns=0;i=61'),
        nodeClass: NodeClass.Object,
      };
      return expect([file], 'when piped through', stream, 'to yield objects satisfying', [
        expect.it('to be', file),
      ]);
    });

    it('should push files where no node can be found', function () {
      const createStream = new CreateNodeStream();
      const stream = new WriteStream(createStream);
      stream.pipe(createStream);

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = (nodeId, value, callback) =>
          callback(null, StatusCodes.BadNodeIdUnknown);
      });

      const file = {
        nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS.Main'),
        typeDefinition: new NodeId('ns=1;s=VariableTypes.ATVISE.Display'),
        nodeClass: NodeClass.Variable,
      };
      return expect([file], 'when piped through', stream, 'to yield objects satisfying', [
        expect.it('to be', file),
      ]);
    });

    it('should push files with good status to add references stream', async function () {
      const pushToAddRefsStream = spy();
      const stream = new WriteStream(new CreateNodeStream(), {
        push: pushToAddRefsStream,
      });

      stream.prependOnceListener('session-open', () => {
        stream.session.writeSingleNode = (nodeId, value, callback) =>
          callback(null, StatusCodes.Good);
      });

      const file = {
        nodeId: new NodeId('ns=1;s=AGENT.DISPLAYS.Main'),
        typeDefinition: new NodeId('ns=1;s=VariableTypes.ATVISE.Display'),
        nodeClass: NodeClass.Variable,
      };
      await expect(
        [file],
        'when piped through',
        stream,
        'to yield objects satisfying',
        'to have length',
        0
      );

      return expect(pushToAddRefsStream, 'was called once');
    });
  });
});