NatLibFi/marc-record-validators-melinda

View on GitHub
src/field-exclusion.spec.js

Summary

Maintainability
F
2 wks
Test Coverage
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {MarcRecord} from '@natlibfi/marc-record';
import validatorFactory from '../src/field-exclusion';

const {expect} = chai;
chai.use(chaiAsPromised);

// Factory validation
describe('field-exclusion', () => {
  describe('#validate: Check configuration validation', () => {
    it('Creates a validator from simple config', async () => {
      const config = [/^500$/u];

      const validator = await validatorFactory(config);
      expect(validator)
        .to.be.an('object')
        .that.has.any.keys('description', 'validate');

      expect(validator.description).to.be.a('string');
      expect(validator.validate).to.be.a('function');
    });

    it('Creates a validator from complex config', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
        }
      ];

      const validator = await validatorFactory(config);
      expect(validator)
        .to.be.an('object')
        .that.has.any.keys('description', 'validate');

      expect(validator.description).to.be.a('string');
      expect(validator.validate).to.be.a('function');
    });

    it('Fails to create a validator from invalid config - tag', async () => {
      const config = [
        {
          tag: '500',
          subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - invalid data type for: tag');
      }
    });

    it('Fails to create a validator from invalid config - msising array', async () => {
      const config = {
        tag: '500',
        subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
      };

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration array not provided');
      }
    });

    it('Fails to create a validator from invalid config - code', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [{code: 9, value: /^(?!FENNI<KEEP>).*$/u}]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - invalid data type for: code');
      }
    });

    it('Fails to create a validator from invalid config - value', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [{code: /9/u, value: 'Fenni'}]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - invalid data type for: value');
      }
    });

    it('Fails to create a validator from invalid config - exclusion: value, ind1', async () => {
      const config = [
        {
          tag: /^500$/u,
          value: /^500$/u,
          ind1: /^500$/u,
          subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - excluded element');
      }
    });

    it('Fails to create a validator from invalid config - missing mandatory: tag', async () => {
      const config = [
        {
          value: /^500$/u,
          subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - missing mandatory element: tag');
      }
    });

    it('Fails to create a validator from invalid config - subfield not object: array', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [
            ['/9/', '/^(?!FENNI<KEEP>).*$/'],
            {value: /^(?!FENNI<KEEP>).*$/u}
          ]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - subfield: /9/,/^(?!FENNI<KEEP>).*$/ not object');
      }
    });

    it('Fails to create a validator from invalid config - subfield not object: string', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [
            '/9/',
            '/^(?!FENNI<KEEP>).*$/',
            {value: /^(?!FENNI<KEEP>).*$/u}
          ]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - subfield: /9/ not object');
      }
    });

    it('Fails to create a validator from invalid config - missing mandatory: subfield.code', async () => {
      const config = [
        {
          tag: /^500$/u,
          subfields: [
            {code: /9/u, value: /^(?!FENNI<KEEP>).*$/u},
            {value: /^(?!FENNI<KEEP>).*$/u}
          ]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - missing mandatory element: code');
      }
    });

    it('Fails to create a validator from invalid config - unidentified field: unidentified', async () => {
      const config = [
        {
          tag: /^500$/u,
          unidentified: /^500$/u,
          subfields: [
            {code: /9/u, value: /^(?!FENNI<KEEP>).*$/u},
            {value: /^(?!FENNI<KEEP>).*$/u}
          ]
        }
      ];

      try {
        await validatorFactory(config);
      } catch (error) {
        expect(error).to.be.an('error').with.property('message', 'Configuration not valid - unidentified value: unidentified');
      }
    });
  });

  // Simple configuration https://github.com/NatLibFi/marc-record-validators-melinda/issues/45
  describe('#validate: Simple configuration (spec)', () => {
    const config = [
      {
        tag: /^500$/u
      }
    ];

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    const recordInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidDouble = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }, {
          tag: '500',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $500 should be excluded']});
    });

    it('Finds the record invalid - double', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalidDouble);
      expect({valid, message}).to.eql({valid: false, message: ['Field $500 should be excluded', 'Field $500 should be excluded']});
    });

    it('Repairs invalid record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalid);
      expect(recordInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });

    it('Repairs invalid record - double', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalidDouble);
      expect(recordInvalidDouble.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });

  // Simple multi tag configuration
  describe('#validate: Simple multi tag configuration (spec)', () => {
    const config = [
      {
        tag: /^(648|650|651|655)$/u // eslint-disable-line prefer-named-capture-group
      }
    ];

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    const recordInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '648',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidDouble = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '648',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $648 should be excluded']});
    });

    it('Finds the record invalid - double', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalidDouble);
      expect({valid, message}).to.eql({valid: false, message: ['Field $648 should be excluded', 'Field $650 should be excluded']});
    });

    it('Repairs invalid record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalid);
      expect(recordInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });

    it('Repairs invalid record - double', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalidDouble);
      expect(recordInvalidDouble.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });

  // Simple multi tag configuration
  describe('#validate: Simple multi tag configuration - No object (spec)', () => {
    const config = [/^(648|650|651|655)$/u]; // eslint-disable-line prefer-named-capture-group

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    const recordInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '648',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidDouble = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '648',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $648 should be excluded']});
    });

    it('Finds the record invalid - double', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalidDouble);
      expect({valid, message}).to.eql({valid: false, message: ['Field $648 should be excluded', 'Field $650 should be excluded']});
    });

    it('Repairs invalid record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalid);
      expect(recordInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });

    it('Repairs invalid record - double', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalidDouble);
      expect(recordInvalidDouble.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });


  // Complex configuration https://github.com/NatLibFi/marc-record-validators-melinda/issues/45
  describe('#validate: Complex configuration (spec)', () => {
    const config = [
      {
        tag: /^500$/u,
        subfields: [{code: /9/u, value: /^(?!FENNI<KEEP>).*$/u}]
      }
    ];

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'FENNI<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $500 should be excluded']});
    });

    it('Repairs invalid record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalid);
      expect(recordInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });

  // Complex multi tag configuration
  describe('#validate: Complex multi tag configuration (spec)', () => {
    const config = [
      {
        tag: /^(648|650|651|655)$/u, // eslint-disable-line prefer-named-capture-group
        subfields: [{code: /^2$/u, value: /^(ysa|musa|allars|cilla)$/u}] // eslint-disable-line prefer-named-capture-group
      }
    ];

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '2', value: 'yso'}
          ]
        }
      ]
    });

    const recordInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '2', value: 'ysa'}
          ]
        }
      ]
    });

    const recordInvalidMulti = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '648',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: '2', value: 'ysa'}]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: '2', value: 'ysa'}]
        }, {
          tag: '650',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: '2', value: 'ysa'}]
        }, {
          tag: '651',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: '2', value: 'ysa'}]
        }, {
          tag: '655',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: '2', value: 'ysa'}]
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $650 should be excluded']});
    });

    it('Finds the record invalid (spec)', async () => {
      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(recordInvalidMulti);
      expect({valid, message}).to.eql({valid: false, message: [
        'Field $648 should be excluded',
        'Field $650 should be excluded',
        'Field $650 should be excluded',
        'Field $651 should be excluded',
        'Field $655 should be excluded'
      ]});
    });

    it('Repairs invalid multi record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalidMulti);
      expect(recordInvalidMulti.equalsTo(recordInvalidFixed)).to.eql(true);
    });

    it('Repairs invalid record', async () => {
      const validator = await validatorFactory(config);
      await validator.fix(recordInvalid);
      expect(recordInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });

  // More special cases to increase coverage
  describe('#validate: Custom configuration', () => {
    const configInd = [
      {
        tag: /^500$/u,
        ind1: /^8$/u,
        ind2: /^4$/u
      }
    ];

    const configValue = [
      {
        tag: /^500$/u,
        value: /^8$/u
      }
    ];

    const recordValid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '500',
          ind1: '8',
          ind2: '0',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordIndInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          ind1: '8',
          ind2: '4',
          subfields: [
            {code: 'a', value: 'Foo Bar Foo Bar Foo Bar'},
            {code: '9', value: 'ALMA<KEEP>'}
          ]
        }
      ]
    });

    const recordValueInvalid = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }, {
          tag: '500',
          value: '8'
        }
      ]
    });

    const recordInvalidFixed = new MarcRecord({
      leader: 'foo',
      fields: [
        {
          tag: '245',
          ind1: ' ',
          ind2: ' ',
          subfields: [{code: 'a', value: 'Fubar'}]
        }
      ]
    });

    it('Finds the record valid - Ind1&Ind2', async () => {
      const validator = await validatorFactory(configInd);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record valid - Value', async () => {
      const validator = await validatorFactory(configValue);
      const {valid, message} = await validator.validate(recordValid);
      expect({valid, message}).to.eql({valid: true, message: []});
    });

    it('Finds the record invalid - Ind', async () => {
      const validator = await validatorFactory(configInd);
      const {valid, message} = await validator.validate(recordIndInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $500 should be excluded']});
    });

    it('Finds the record invalid - Value', async () => {
      const validator = await validatorFactory(configValue);
      const {valid, message} = await validator.validate(recordValueInvalid);
      expect({valid, message}).to.eql({valid: false, message: ['Field $500 should be excluded']});
    });

    it('Repairs invalid record - Ind', async () => {
      const validator = await validatorFactory(configInd);
      await validator.fix(recordIndInvalid);
      expect(recordIndInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });

    it('Repairs invalid record - Value', async () => {
      const validator = await validatorFactory(configValue);
      await validator.fix(recordValueInvalid);
      expect(recordValueInvalid.equalsTo(recordInvalidFixed)).to.eql(true);
    });
  });

  describe('Dependencies', () => {
    it('Finds the record invalid because dependency matches', async () => {
      const config = [
        {
          tag: /^041$/u,
          dependencies: [{leader: /^.{6}a/u}]
        }
      ];

      const record = new MarcRecord({
        leader: '00000cam^a22003372i^4500',
        fields: [
          {
            tag: '041',
            ind1: ' ',
            ind2: ' ',
            subfields: [{code: 'a', value: 'Fubar'}]
          }
        ]
      });

      const validator = await validatorFactory(config);
      const {valid, message} = await validator.validate(record);

      expect({valid, message}).to.eql({valid: false, message: ['Field $041 should be excluded']});
    });
  });
});