Documentationcurrent version
Help us improve the docs by fixing typos and proposing enhancements.

Nikita

Action "fs.assert"

Assert a file exists or a provided text match the content of a text file.

Output

  • err (Error)
    Error if assertion failed.

Example

Validate the content of a file:

nikita.fs.assert({
  target: '/tmp/a_file', 
  content: 'nikita is around'
})

Ensure a file does not exists:

nikita.fs.assert({
  target: '/tmp/a_file',
  not: true
})

Hooks

on_action = ({config, metadata}) ->
  config.filter = [config.filter] if config.filter instanceof RegExp

Schema definitions

definitions =
  config:
    type: 'object'
    properties:
      'content':
        oneOf: [{type: 'string'}, {instanceof: 'Buffer'}, {instanceof: 'RegExp'}]
        description: '''
        Text to validate.
        '''
      'encoding':
        type: 'string'
        default: 'utf8'
        description: '''
        Content encoding, see the Node.js supported Buffer encoding.
        '''
      'filetype':
        type: 'array'
        items:
          type: ['integer', 'string']
        description: '''
        Validate the file, could be any [file type
        constants](https://nodejs.org/api/fs.html#fs_file_type_constants) or
        one of 'ifreg', 'file', 'ifdir', 'directory', 'ifchr', 'chardevice',
        'iffblk', 'blockdevice', 'ififo', 'fifo', 'iflink', 'symlink',
        'ifsock',  'socket'.
        '''
      'filter':
        type: 'array'
        items:
          instanceof: 'RegExp'
        description: '''
        Text to filter in actual content before matching.
        '''
      'gid':
        type: ['integer', 'string']
        description: '''
        Group ID to assert.
        '''
      'md5':
        type: 'string'
        description: '''
        Validate signature.
        '''
      'mode':
        type: 'array'
        items:
          $ref: 'module://@nikitajs/core/src/actions/fs/base/chmod#/definitions/config/properties/mode'
        description: '''
        Validate file permissions.
        '''
      'not':
        $ref: 'module://@nikitajs/core/src/actions/assert#/definitions/config/properties/not'
      'sha1':
        type: 'string'
        description: '''
        Validate signature.
        '''
      'sha256':
        type: 'string'
        description: '''
        Validate signature.
        '''
      'target':
        type: 'string'
        description: '''
        Location of the file to assert.
        '''
      'trim':
        type: 'boolean'
        default: false
        description: '''
        Trim the actual and expected content before matching.
        '''
      'uid':
        type: ['integer', 'string']
        description: '''
        User ID to assert.
        '''
    required: ['target']

Handler

handler = ({config, metadata}) ->
  config.filetype = for filetype in config.filetype or []
    continue unless filetype
    if typeof filetype is 'string'
      switch filetype.toLowerCase()
        when 'ifreg', 'file' then fs.constants.S_IFREG
        when 'ifdir', 'directory' then fs.constants.S_IFDIR
        when 'ifchr', 'chardevice' then fs.constants.S_IFCHR
        when 'iffblk', 'blockdevice' then fs.constants.S_IFBLK
        when 'ififo', 'fifo' then fs.constants.S_IFIFO
        when 'iflink', 'symlink' then fs.constants.S_IFLNK
        when 'ifsock', 'socket' then fs.constants.S_IFSOCK
        else filetype
    else filetype
  if typeof config.content is 'string'
    config.content = config.content.trim() if config.trim
    config.content = Buffer.from config.content, config.encoding
  else if Buffer.isBuffer config.content
    config.content = utils.buffer.trim config.content, config.encoding if config.trim
  # Assert file exists
  unless config.content? or config.md5 or config.sha1 or config.sha256 or config.mode?.length
    {exists} = await @fs.base.exists config.target.toString()
    unless config.not
      unless exists
        err = errors.NIKITA_FS_ASSERT_FILE_MISSING config: config
    else
      if exists
        err = errors.NIKITA_FS_ASSERT_FILE_EXISTS config: config
    throw err if err
  # Assert file filetype
  if config.filetype?.length
    {stats} = await @fs.base.lstat config.target
    if fs.constants.S_IFREG in config.filetype and not utils.stats.isFile stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'File', stats: stats
    if fs.constants.S_IFDIR in config.filetype and not utils.stats.isDirectory stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'Directory', stats: stats
    if fs.constants.S_IFCHR in config.filetype and not utils.stats.isCharacterDevice stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'Character Device', stats: stats
    if fs.constants.S_IFBLK in config.filetype and not utils.stats.isBlockDevice stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'Block Device', stats: stats
    if fs.constants.S_IFIFO in config.filetype and not utils.stats.isFIFO stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'FIFO', stats: stats
    if fs.constants.S_IFLNK in config.filetype and not utils.stats.isSymbolicLink stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'Symbolic Link', stats: stats
    if fs.constants.S_IFSOCK in config.filetype and not utils.stats.isSocket stats.mode
      throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID config: config, expect: 'Socket', stats: stats
  # Assert content equal
  if config.content? and (typeof config.content is 'string' or Buffer.isBuffer config.content)
    {data} = await @fs.base.readFile config.target
    for filter in config.filter or []
      data = filter[Symbol.replace] data, ''
    # RegExp returns string
    if typeof data is 'string'
      data = Buffer.from data
    data = utils.buffer.trim data, config.encoding if config.trim
    unless config.not
      unless data.equals config.content
        throw errors.NIKITA_FS_ASSERT_CONTENT_UNEQUAL config: config, expect: data
    else
      if data.equals config.content
        throw errors.NIKITA_FS_ASSERT_CONTENT_EQUAL config: config, expect: data
    throw err if err
  # Assert content match
  if config.content? and config.content instanceof RegExp
    {data} = await @fs.base.readFile config.target
    for filter in config.filter or []
      data = filter[Symbol.replace] data, ''
    data = utils.buffer.trim data, config.encoding if config.trim
    unless config.not
      unless config.content.test data
        throw errors.NIKITA_FS_ASSERT_CONTENT_UNMATCH config: config, expect: data
    else
      if config.content.test data
        throw errors.NIKITA_FS_ASSERT_CONTENT_MATCH config: config, expect: data
    throw err if err
  # Assert hash match
  # todo, also support config.algo and config.hash
  (algo = 'md5'; _hash = config.md5) if config.md5
  (algo = 'sha1'; _hash = config.sha1) if config.sha1
  (algo = 'sha256'; _hash = config.sha256) if config.sha256
  if algo
    {hash} = await @fs.hash config.target, algo: algo
    unless config.not
      if _hash isnt hash
        throw errors.NIKITA_FS_ASSERT_HASH_UNMATCH config: config, algo: algo, hash:
          expected: _hash, actual: hash
    else
      if _hash is hash
        throw errors.NIKITA_FS_ASSERT_HASH_MATCH config: config, algo: algo, hash: hash
  # Assert uid ownerships
  if config.uid?
    {stats} = await @fs.base.lstat config.target
    unless config.not
      unless "#{stats.uid}" is "#{config.uid}"
        throw errors.NIKITA_FS_ASSERT_UID_UNMATCH config: config, actual: stats.uid
    else
      if "#{stats.uid}" is "#{config.uid}"
        throw errors.NIKITA_FS_ASSERT_UID_MATCH config: config
  # Assert gid ownerships
  if config.gid?
    {stats} = await @fs.base.stat config.target
    unless config.not
      unless "#{stats.gid}" is "#{config.gid}"
        throw errors.NIKITA_FS_ASSERT_GID_UNMATCH config: config, actual: stats.gid
    else
      if "#{stats.gid}" is "#{config.gid}"
        throw errors.NIKITA_FS_ASSERT_GID_MATCH config: config
  # Assert file permissions
  if config.mode?.length
    {stats} = await @fs.base.stat config.target
    unless config.not
      unless utils.mode.compare config.mode, stats.mode
        throw errors.NIKITA_FS_ASSERT_MODE_UNMATCH config: config, mode: stats.mode
    else
      if utils.mode.compare config.mode, stats.mode
        throw errors.NIKITA_FS_ASSERT_MODE_MATCH config: config

Exports

module.exports =
  handler: handler
  hooks:
    on_action: on_action
  metadata:
    argument_to_config: 'target'
    definitions: definitions

Errors

errors =
  NIKITA_FS_ASSERT_FILE_MISSING: ({config}) ->
    utils.error 'NIKITA_FS_ASSERT_FILE_MISSING', [
      'file does not exists,'
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_FILE_EXISTS: ({config}) ->
    utils.error 'NIKITA_FS_ASSERT_FILE_EXISTS', [
      'file exists,'
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_FILETYPE_INVALID: ({config, expect, stats}) ->
    utils.error 'NIKITA_FS_ASSERT_FILETYPE_INVALID', [
      'filetype is invalid,'
      "expect #{JSON.stringify expect} type,"
      "got #{JSON.stringify utils.stats.type stats.mode} type,"
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_CONTENT_UNEQUAL: ({config, expect}) ->
    utils.error 'NIKITA_FS_ASSERT_CONTENT_UNEQUAL', [
      'content does not equal the expected value,'
      "expect #{JSON.stringify expect.toString()}"
      "to equal #{JSON.stringify config.content.toString()},"
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_CONTENT_EQUAL: ({config, expect}) ->
    utils.error 'NIKITA_FS_ASSERT_CONTENT_EQUAL', [
      'content is matching,'
      "not expecting to equal #{JSON.stringify expect.toString()},"
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_CONTENT_UNMATCH: ({config, expect}) ->
    utils.error 'NIKITA_FS_ASSERT_CONTENT_UNMATCH', [
      'content does not match the provided regexp,'
      "expect #{JSON.stringify expect.toString()}"
      "to match #{config.content.toString()},"
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_CONTENT_MATCH: ({config, expect}) ->
    utils.error 'NIKITA_FS_ASSERT_CONTENT_MATCH', [
      'content is matching the provided regexp,'
      "got #{JSON.stringify expect.toString()}"
      "to match #{config.content.toString()},"
      "location is #{JSON.stringify config.target}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_HASH_UNMATCH: ({config, algo, hash}) ->
    utils.error 'NIKITA_FS_ASSERT_HASH_UNMATCH', [
      "an invalid #{algo} signature was computed,"
      "expect #{JSON.stringify hash.expected},"
      "got #{JSON.stringify hash.actual}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_HASH_MATCH: ({config, algo, hash}) ->
    utils.error 'NIKITA_FS_ASSERT_HASH_MATCH', [
      "the #{algo} signatures are matching,"
      "not expecting to equal #{JSON.stringify hash}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_MODE_UNMATCH: ({config, mode}) ->
    expect = config.mode.map (mode) -> "#{pad 4, utils.mode.stringify(mode), '0'}"
    utils.error "NIKITA_FS_ASSERT_MODE_UNMATCH", [
      'content permission don\'t match the provided mode,'
      "expect #{expect},"
      "got #{utils.mode.stringify(mode).substr -4}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_MODE_MATCH: ({config}) ->
    expect = config.mode.map (mode) -> "#{pad 4, utils.mode.stringify(mode), '0'}"
    utils.error "NIKITA_FS_ASSERT_MODE_MATCH", [
      'the content permission match the provided mode,'
      "not expecting to equal #{expect}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_UID_UNMATCH: ({config, actual}) ->
    utils.error 'NIKITA_FS_ASSERT_UID_UNMATCH', [
      'the uid of the target does not match the expected value,'
      "expected #{JSON.stringify config.uid},"
      "got #{JSON.stringify actual}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_UID_MATCH: ({config}) ->
    utils.error 'NIKITA_FS_ASSERT_UID_MATCH', [
      'the uid of the target  match the provided value,'
      "not expecting to equal #{JSON.stringify config.uid}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_GID_UNMATCH: ({config, actual}) ->
    utils.error 'NIKITA_FS_ASSERT_GID_UNMATCH', [
      'the gid of the target does not match the expected value,'
      "expected #{JSON.stringify config.uid},"
      "got #{JSON.stringify actual}."
    ], target: config.target, message: config.error
  NIKITA_FS_ASSERT_GID_MATCH: ({config}) ->
    utils.error 'NIKITA_FS_ASSERT_GID_MATCH', [
      'the gid of the target  match the provided value,'
      "not expecting to equal #{JSON.stringify config.uid}."
    ], target: config.target, message: config.error

Dependencies

pad = require 'pad'
fs = require 'fs'
utils = require '../../utils'
Edit on GitHub
Navigate
About

Nikita is an open source project hosted on GitHub and developed by Adaltas.