mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
add rabbitmq config resource
Signed-off-by: Dominik Richter <dominik.richter@gmail.com>
This commit is contained in:
parent
21258f7296
commit
02e435b6d0
9 changed files with 344 additions and 201 deletions
55
docs/resources/rabbitmq_config.md.erb
Normal file
55
docs/resources/rabbitmq_config.md.erb
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
title: About the rabbitmq_config Resource
|
||||
---
|
||||
|
||||
# rabbitmq_config
|
||||
|
||||
Use the `rabbitmq_config` InSpec audit resource to test configuration data for the RabbitMQ daemon located at `/etc/rabbitmq/rabbitmq.config` on Linux and Unix platforms.
|
||||
|
||||
## Syntax
|
||||
|
||||
A `rabbitmq_config` resource block declares the RabbitMQ configuration data to be tested:
|
||||
|
||||
describe rabbitmq_config.params('rabbit', 'ssl_listeners') do
|
||||
it { should cmp 5671 }
|
||||
end
|
||||
|
||||
where
|
||||
|
||||
* `params` is the list of parameters configured in the RabbitMQ config file
|
||||
* `{ should cmp 5671 }` tests the value of `rabbit.ssl_listeners` as read from `rabbitmq.config` versus the value declared in the test
|
||||
|
||||
|
||||
## Matchers
|
||||
|
||||
This InSpec audit resource has the following matchers:
|
||||
|
||||
### be
|
||||
|
||||
<%= partial "/shared/matcher_be" %>
|
||||
|
||||
### cmp
|
||||
|
||||
<%= partial "/shared/matcher_cmp" %>
|
||||
|
||||
### eq
|
||||
|
||||
<%= partial "/shared/matcher_eq" %>
|
||||
|
||||
### include
|
||||
|
||||
<%= partial "/shared/matcher_include" %>
|
||||
|
||||
### match
|
||||
|
||||
<%= partial "/shared/matcher_match" %>
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this InSpec audit resource.
|
||||
|
||||
### Test the list of TCP listeners
|
||||
|
||||
describe rabbitmq_config.params('rabbit', 'tcp_listeners') do
|
||||
it { should eq [5672] }
|
||||
end
|
|
@ -42,4 +42,5 @@ Gem::Specification.new do |spec|
|
|||
spec.add_dependency 'faraday', '>=0.9.0'
|
||||
spec.add_dependency 'toml', '~> 0.1'
|
||||
spec.add_dependency 'addressable', '~> 2.4'
|
||||
spec.add_dependency 'parslet', '~> 1.5'
|
||||
end
|
||||
|
|
|
@ -120,6 +120,7 @@ require 'resources/postgres_conf'
|
|||
require 'resources/postgres_session'
|
||||
require 'resources/powershell'
|
||||
require 'resources/processes'
|
||||
require 'resources/rabbitmq_conf'
|
||||
require 'resources/registry_key'
|
||||
require 'resources/security_policy'
|
||||
require 'resources/service'
|
||||
|
|
53
lib/resources/rabbitmq_conf.rb
Normal file
53
lib/resources/rabbitmq_conf.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
|
||||
require 'utils/erlang_parser'
|
||||
|
||||
module Inspec::Resources
|
||||
class RabbitmqConf < Inspec.resource(1)
|
||||
name 'rabbitmq_config'
|
||||
desc 'Use the rabbitmq_config InSpec resource to test configuration data '\
|
||||
'for the RabbitMQ service located in /etc/rabbitmq/rabbitmq.config on '\
|
||||
'Linux and UNIX platforms.'
|
||||
example "
|
||||
describe rabbitmq_config.params('rabbit', 'ssl_listeners') do
|
||||
it { should cmp 5671 }
|
||||
end
|
||||
"
|
||||
|
||||
def initialize(conf_path = nil)
|
||||
@conf_path = conf_path || '/etc/rabbitmq/rabbitmq.config'
|
||||
end
|
||||
|
||||
def params(*opts)
|
||||
opts.inject(read_params) do |res, nxt|
|
||||
res.respond_to?(:key) ? res[nxt] : nil
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"rabbitmq_config #{@conf_path}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_content
|
||||
return @content if defined?(@content)
|
||||
file = inspec.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
|
||||
@content = file.content
|
||||
end
|
||||
|
||||
def read_params
|
||||
return @params if defined?(@params)
|
||||
return @params = {} if read_content.nil?
|
||||
@params = ErlangConfigFile.parse(read_content)
|
||||
rescue Parslet::ParseFailed
|
||||
raise "Cannot parse RabbitMQ config: \"#{read_content}\""
|
||||
end
|
||||
end
|
||||
end
|
192
lib/utils/erlang_parser.rb
Normal file
192
lib/utils/erlang_parser.rb
Normal file
|
@ -0,0 +1,192 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
|
||||
require 'parslet'
|
||||
|
||||
class ErlangParser < Parslet::Parser
|
||||
root :outermost
|
||||
# only designed for rabbitmq config files for now:
|
||||
rule(:outermost) { filler? >> array.maybe >> dot.maybe }
|
||||
|
||||
rule(:exp) {
|
||||
(tuple | array | binary | string | bool | identifier | float | integer) >> filler?
|
||||
}
|
||||
|
||||
rule(:array) {
|
||||
str('[') >> filler? >> (
|
||||
exp.repeat(1) >>
|
||||
(comma >> exp).repeat
|
||||
).maybe.as(:array) >> str(']') >> filler?
|
||||
}
|
||||
|
||||
rule(:tuple) {
|
||||
str('{') >> filler? >> (
|
||||
exp.repeat(1) >> filler? >>
|
||||
(comma >> exp).repeat
|
||||
).maybe.as(:tuple) >> str('}') >> filler?
|
||||
}
|
||||
|
||||
rule(:filler?) { space.repeat }
|
||||
rule(:space) { match('\s+') | match["\n"] | comment }
|
||||
|
||||
rule(:comment) { str('%') >> (match["\n\r"].absent? >> any).repeat }
|
||||
rule(:comma) { str(',') >> filler? }
|
||||
rule(:dot) { str('.') >> filler? }
|
||||
rule(:bool) { str('true').as(:bool) | str('false').as(:bool) }
|
||||
|
||||
rule(:identifier) {
|
||||
(match('[a-zA-Z]') >> match('[a-zA-Z0-9_]').repeat).as(:identifier) >> filler?
|
||||
}
|
||||
|
||||
rule(:float) {
|
||||
(
|
||||
integer >> (
|
||||
str('.') >> match('[0-9]').repeat(1) |
|
||||
str('e') >> match('[0-9]').repeat(1)
|
||||
).as(:e)
|
||||
).as(:float) >> filler?
|
||||
}
|
||||
|
||||
rule(:integer) {
|
||||
((str('+') | str('-')).maybe >> match('[0-9]').repeat(1)).as(:integer) >> filler?
|
||||
}
|
||||
|
||||
rule(:string) { stringS | stringD }
|
||||
|
||||
rule(:stringS) {
|
||||
str("'") >> (
|
||||
str('\\') >> any | str("'").absent? >> any
|
||||
).repeat.as(:string) >> str("'") >> filler?
|
||||
}
|
||||
|
||||
rule(:stringD) {
|
||||
str('"') >> (
|
||||
str('\\') >> any | str('"').absent? >> any
|
||||
).repeat.as(:string) >> str('"') >> filler?
|
||||
}
|
||||
|
||||
rule(:binary_item) {
|
||||
(string | integer) >>
|
||||
(str(':') >> integer).maybe.as(:size) >>
|
||||
(str('/') >> identifier).maybe.as(:type) >>
|
||||
filler?
|
||||
}
|
||||
|
||||
rule(:binary) {
|
||||
str('<<') >> filler? >> (
|
||||
binary_item.repeat(1) >>
|
||||
(comma >> binary_item).repeat
|
||||
).maybe.as(:binary) >> str('>>') >> filler?
|
||||
}
|
||||
end
|
||||
|
||||
class ErlangBitstream
|
||||
def initialize
|
||||
@data = [] # a stream of 8-bit numbers
|
||||
@cur_bits = '' # a string of binary bits 10010010...
|
||||
end
|
||||
|
||||
TYPES = {
|
||||
'integer' => 8,
|
||||
'float' => 8*8,
|
||||
'utf8' => 8,
|
||||
'utf16' => 8*2,
|
||||
'utf32' => 8*4,
|
||||
}.freeze
|
||||
|
||||
def bit_size(size, type)
|
||||
raise 'Cannot specify size and type at the same time.' if !type.nil? && !size.nil?
|
||||
return (size || 8).to_i if type.nil?
|
||||
TYPES[type] || raise("Cannot handle binary-stream type #{type}")
|
||||
end
|
||||
|
||||
def add(i)
|
||||
if i[:integer].nil? && i[:string].nil?
|
||||
raise 'No data provided, internal error for binary-stream processing!'
|
||||
end
|
||||
s = bit_size(i[:size], i[:type])
|
||||
unless i[:string].nil?
|
||||
str2int(i[:string].to_s, i[:type]).map { |e| add_bits(int2bits(e, 8)) }
|
||||
else
|
||||
add_int(i[:integer], s)
|
||||
end
|
||||
rescue RuntimeError => e
|
||||
raise 'Error processing Erlang bit string '\
|
||||
"'#{i[:string] || i[:integer]}:#{i[:size]}/#{i[:type]}'. #{e.message}"
|
||||
end
|
||||
|
||||
def str2int(s, type)
|
||||
case type
|
||||
when 'utf8' then s.encode('utf-8').unpack('C*')
|
||||
when 'utf16' then s.encode('utf-16').unpack('C*').drop(2)
|
||||
when 'utf32' then s.encode('utf-32').unpack('C*').drop(4)
|
||||
when 'integer', 'float' then raise "Cannot handle bit string as type #{type}"
|
||||
else s.split('').map { |x| x.ord & 0xff }
|
||||
end
|
||||
end
|
||||
|
||||
def int2bits(i, len)
|
||||
format("%0#{len}b", i)
|
||||
end
|
||||
|
||||
def add_int(v, size)
|
||||
x = v.to_i & (2**size - 1) # only get the bits specified in size
|
||||
add_bits(int2bits(x, size))
|
||||
end
|
||||
|
||||
def add_bits(s)
|
||||
b = (@cur_bits + s).scan(/.{1,8}/)
|
||||
@data += b[0..-2].map { |x| x.to_i(2) }
|
||||
@cur_bits = b.last
|
||||
end
|
||||
|
||||
def value(encoding = 'utf-8')
|
||||
# fill in the rest
|
||||
rest = '0' * (8 - @cur_bits.length) + @cur_bits
|
||||
arr = @data + [rest.to_i(2)]
|
||||
s = arr.pack('C*')
|
||||
s.force_encoding(encoding) unless encoding.nil?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
class ErlangTransform < Parslet::Transform
|
||||
class Tuple < Array; end
|
||||
class Identifier < String; end
|
||||
|
||||
def self.assemble_binary(seq)
|
||||
b = ErlangBitstream.new
|
||||
seq.each { |i| b.add(i) }
|
||||
b.value
|
||||
end
|
||||
|
||||
rule(string: simple(:x)) { x.to_s }
|
||||
rule(string: []) { '' }
|
||||
rule(integer: simple(:x)) { x.to_i }
|
||||
rule(float: { integer: simple(:a), e: simple(:b) }) { (a+b).to_f }
|
||||
rule(bool: 'true') { true }
|
||||
rule(bool: 'false') { false }
|
||||
rule(binary: subtree(:x)) { x.nil? ? '' : ErlangTransform.assemble_binary(x) }
|
||||
rule(identifier: simple(:x)) { Identifier.new(x.to_s) }
|
||||
rule(array: subtree(:x)) { Array(x) }
|
||||
rule(tuple: subtree(:x)) {
|
||||
x.nil? ? Tuple.new : Tuple.new(x)
|
||||
}
|
||||
end
|
||||
|
||||
class ErlangConfigFile
|
||||
def self.parse(content)
|
||||
lex = ErlangParser.new.parse(content)
|
||||
tree = ErlangTransform.new.apply(lex)
|
||||
turn_to_hash(tree)
|
||||
end
|
||||
|
||||
def self.turn_to_hash(t)
|
||||
if t.is_a?(Array) && t.all? { |x| x.class == ErlangTransform::Tuple && x.length == 2 }
|
||||
Hash[t.map { |i| [i[0], turn_to_hash(i[1])] }]
|
||||
else
|
||||
t
|
||||
end
|
||||
end
|
||||
end
|
|
@ -115,6 +115,7 @@ class MockLoader
|
|||
'/etc/audit/auditd.conf' => mockfile.call('auditd.conf'),
|
||||
'/etc/mysql/my.cnf' => mockfile.call('mysql.conf'),
|
||||
'/etc/mysql/mysql2.conf' => mockfile.call('mysql2.conf'),
|
||||
'/etc/rabbitmq/rabbitmq.config' => mockfile.call('rabbitmq.config'),
|
||||
'kitchen.yml' => mockfile.call('kitchen.yml'),
|
||||
'example.csv' => mockfile.call('example.csv'),
|
||||
'policyfile.lock.json' => mockfile.call('policyfile.lock.json'),
|
||||
|
|
10
test/unit/mock/files/rabbitmq.config
Normal file
10
test/unit/mock/files/rabbitmq.config
Normal file
|
@ -0,0 +1,10 @@
|
|||
%% -*- mode: erlang -*-
|
||||
[
|
||||
{rabbit,
|
||||
[%% some comments...
|
||||
{ssl_listeners, [5671]},
|
||||
%% duplicate entries
|
||||
{tcp_listeners, [5672]},
|
||||
{tcp_listeners, [{"127.0.0.1", 5672},
|
||||
{"::1", 5672}]}
|
||||
]}].
|
17
test/unit/resources/rabbitmq_conf_test.rb
Normal file
17
test/unit/resources/rabbitmq_conf_test.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
|
||||
describe 'Inspec::Resources::RabbitmqConf' do
|
||||
|
||||
describe 'rabbitmq_config' do
|
||||
it 'check rabbitmq config parsing' do
|
||||
resource = load_resource('rabbitmq_config')
|
||||
_(resource.params('rabbit', 'ssl_listeners')).must_equal [5671]
|
||||
_(resource.params('rabbit', 'tcp_listeners')).must_equal({'127.0.0.1'=>5672, '::1'=>5672})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,91 +1,13 @@
|
|||
require 'parslet'
|
||||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
|
||||
class ErlAx < Parslet::Parser
|
||||
root :outermost
|
||||
# only designed for rabbitmq config files for now:
|
||||
rule(:outermost) { filler? >> array.maybe >> dot.maybe }
|
||||
require 'helper'
|
||||
require 'utils/erlang_parser'
|
||||
|
||||
rule(:exp) {
|
||||
(tuple | array | binary | string | bool | identifier | float | integer) >> filler?
|
||||
}
|
||||
|
||||
rule(:array) {
|
||||
str('[') >> filler? >> (
|
||||
exp.repeat(1) >>
|
||||
(comma >> exp).repeat
|
||||
).maybe.as(:array) >> str(']') >> filler?
|
||||
}
|
||||
|
||||
rule(:tuple) {
|
||||
str('{') >> filler? >> (
|
||||
exp.repeat(1) >> filler? >>
|
||||
(comma >> exp).repeat
|
||||
).maybe.as(:tuple) >> str('}') >> filler?
|
||||
}
|
||||
|
||||
rule(:filler?) { space.repeat }
|
||||
rule(:space) { match('\s+') | match["\n"] | comment }
|
||||
|
||||
rule(:comment) { str('%') >> (match["\n\r"].absent? >> any).repeat }
|
||||
rule(:comma) { str(',') >> filler? }
|
||||
rule(:dot) { str('.') >> filler? }
|
||||
rule(:bool) { str('true').as(:bool) | str('false').as(:bool) }
|
||||
|
||||
rule(:identifier) {
|
||||
(match('[a-zA-Z]') >> match('[a-zA-Z0-9_]').repeat).as(:identifier) >> filler?
|
||||
}
|
||||
|
||||
rule(:float) {
|
||||
(
|
||||
integer >> (
|
||||
str('.') >> match('[0-9]').repeat(1) |
|
||||
str('e') >> match('[0-9]').repeat(1)
|
||||
).as(:e)
|
||||
).as(:float) >> filler?
|
||||
}
|
||||
|
||||
rule(:integer) {
|
||||
((str('+') | str('-')).maybe >> match('[0-9]').repeat(1)).as(:integer) >> filler?
|
||||
}
|
||||
|
||||
rule(:string) { stringS | stringD }
|
||||
|
||||
rule(:stringS) {
|
||||
str("'") >> (
|
||||
str('\\') >> any | str("'").absent? >> any
|
||||
).repeat.as(:string) >> str("'") >> filler?
|
||||
}
|
||||
|
||||
rule(:stringD) {
|
||||
str('"') >> (
|
||||
str('\\') >> any | str('"').absent? >> any
|
||||
).repeat.as(:string) >> str('"') >> filler?
|
||||
}
|
||||
|
||||
rule(:binary_item) {
|
||||
(string | integer) >>
|
||||
(str(':') >> integer).maybe.as(:size) >>
|
||||
(str('/') >> identifier).maybe.as(:type) >>
|
||||
filler?
|
||||
}
|
||||
|
||||
rule(:binary) {
|
||||
str('<<') >> filler? >> (
|
||||
binary_item.repeat(1) >>
|
||||
(comma >> binary_item).repeat
|
||||
).maybe.as(:binary) >> str('>>') >> filler?
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/spec'
|
||||
describe ErlAx do
|
||||
describe ErlangParser do
|
||||
def parse(c)
|
||||
ErlAx.new.parse(c)
|
||||
ErlangParser.new.parse(c)
|
||||
end
|
||||
|
||||
def parsestr(c)
|
||||
|
@ -194,103 +116,9 @@ describe ErlAx do
|
|||
end
|
||||
end
|
||||
|
||||
class ErlangBitstream
|
||||
def initialize
|
||||
@data = [] # a stream of 8-bit numbers
|
||||
@cur_bits = '' # a string of binary bits 10010010...
|
||||
end
|
||||
|
||||
TYPES = {
|
||||
'integer' => 8,
|
||||
'float' => 8*8,
|
||||
'utf8' => 8,
|
||||
'utf16' => 8*2,
|
||||
'utf32' => 8*4,
|
||||
}.freeze
|
||||
|
||||
def bit_size(size, type)
|
||||
raise 'Cannot specify size and type at the same time.' if !type.nil? && !size.nil?
|
||||
return (size || 8).to_i if type.nil?
|
||||
TYPES[type] || raise("Cannot handle binary-stream type #{type}")
|
||||
end
|
||||
|
||||
def add(i)
|
||||
if i[:integer].nil? && i[:string].nil?
|
||||
raise 'No data provided, internal error for binary-stream processing!'
|
||||
end
|
||||
s = bit_size(i[:size], i[:type])
|
||||
unless i[:string].nil?
|
||||
str2int(i[:string].to_s, i[:type]).map { |e| add_bits(int2bits(e, 8)) }
|
||||
else
|
||||
add_int(i[:integer], s)
|
||||
end
|
||||
rescue RuntimeError => e
|
||||
raise 'Error processing Erlang bit string '\
|
||||
"'#{i[:string] || i[:integer]}:#{i[:size]}/#{i[:type]}'. #{e.message}"
|
||||
end
|
||||
|
||||
def str2int(s, type)
|
||||
case type
|
||||
when 'utf8' then s.encode('utf-8').unpack('C*')
|
||||
when 'utf16' then s.encode('utf-16').unpack('C*').drop(2)
|
||||
when 'utf32' then s.encode('utf-32').unpack('C*').drop(4)
|
||||
when 'integer', 'float' then raise "Cannot handle bit string as type #{type}"
|
||||
else s.split('').map { |x| x.ord & 0xff }
|
||||
end
|
||||
end
|
||||
|
||||
def int2bits(i, len)
|
||||
format("%0#{len}b", i)
|
||||
end
|
||||
|
||||
def add_int(v, size)
|
||||
x = v.to_i & (2**size - 1) # only get the bits specified in size
|
||||
add_bits(int2bits(x, size))
|
||||
end
|
||||
|
||||
def add_bits(s)
|
||||
b = (@cur_bits + s).scan(/.{1,8}/)
|
||||
@data += b[0..-2].map { |x| x.to_i(2) }
|
||||
@cur_bits = b.last
|
||||
end
|
||||
|
||||
def value(encoding = 'utf-8')
|
||||
# fill in the rest
|
||||
rest = '0' * (8 - @cur_bits.length) + @cur_bits
|
||||
arr = @data + [rest.to_i(2)]
|
||||
s = arr.pack('C*')
|
||||
s.force_encoding(encoding) unless encoding.nil?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
class ErlOm < Parslet::Transform
|
||||
class Tuple < Array; end
|
||||
class Identifier < String; end
|
||||
|
||||
def self.assemble_binary(seq)
|
||||
b = ErlangBitstream.new
|
||||
seq.each { |i| b.add(i) }
|
||||
b.value
|
||||
end
|
||||
|
||||
rule(string: simple(:x)) { x.to_s }
|
||||
rule(string: []) { '' }
|
||||
rule(integer: simple(:x)) { x.to_i }
|
||||
rule(float: { integer: simple(:a), e: simple(:b) }) { (a+b).to_f }
|
||||
rule(bool: 'true') { true }
|
||||
rule(bool: 'false') { false }
|
||||
rule(binary: subtree(:x)) { x.nil? ? '' : ErlOm.assemble_binary(x) }
|
||||
rule(identifier: simple(:x)) { Identifier.new(x.to_s) }
|
||||
rule(array: subtree(:x)) { Array(x) }
|
||||
rule(tuple: subtree(:x)) {
|
||||
x.nil? ? Tuple.new : Tuple.new(x)
|
||||
}
|
||||
end
|
||||
|
||||
describe ErlOm do
|
||||
describe ErlangTransform do
|
||||
def parse(c)
|
||||
ErlOm.new.apply(ErlAx.new.parse(c))
|
||||
ErlangTransform.new.apply(ErlangParser.new.parse(c))
|
||||
end
|
||||
|
||||
it 'transforms and empty file' do
|
||||
|
@ -354,37 +182,22 @@ describe ErlOm do
|
|||
end
|
||||
|
||||
it 'transforms an empty tuple' do
|
||||
_(parse('[{}].')).must_equal [ErlOm::Tuple.new]
|
||||
_(parse('[{}].')).must_equal [ErlangTransform::Tuple.new]
|
||||
end
|
||||
|
||||
it 'transforms a tuple with one element' do
|
||||
_(parse('[{1}].')).must_equal [ErlOm::Tuple.new([1])]
|
||||
_(parse('[{1}].')).must_equal [ErlangTransform::Tuple.new([1])]
|
||||
end
|
||||
|
||||
it 'transforms a tuple with multiple elements' do
|
||||
_(parse('[{id123, 1, 1.1}].')).must_equal [ErlOm::Tuple.new([ErlOm::Identifier.new('id123'), 1, 1.1])]
|
||||
_(parse('[{id123, 1, 1.1}].')).must_equal [ErlangTransform::Tuple.new([ErlangTransform::Identifier.new('id123'), 1, 1.1])]
|
||||
end
|
||||
|
||||
it 'transforms a deep tuple' do
|
||||
_(parse('[{{{1}}}].')).must_equal [ErlOm::Tuple.new([ErlOm::Tuple.new([ErlOm::Tuple.new([1])])])]
|
||||
_(parse('[{{{1}}}].')).must_equal [ErlangTransform::Tuple.new([ErlangTransform::Tuple.new([ErlangTransform::Tuple.new([1])])])]
|
||||
end
|
||||
|
||||
it 'transforms a deep mix of tuple and array' do
|
||||
_(parse('[{[{1}]}].')).must_equal [ErlOm::Tuple.new([[ErlOm::Tuple.new([1])]])]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'complex use-case' do
|
||||
it 'parses a tricky rabbitmq config file' do
|
||||
f = 'rabbitmq.config'
|
||||
unless File.file?(f)
|
||||
puts "NO #{f}, skipping this check!"
|
||||
return
|
||||
end
|
||||
puts 'Wheeee, testing a real rabbitmq nasty config file'
|
||||
c = File.read(f)
|
||||
_(ErlAx.new.parse(c).to_s).must_be_instance_of String
|
||||
res = ErlOm.new.apply(ErlAx.new.parse(c))
|
||||
# require "pry"; binding.pry
|
||||
_(parse('[{[{1}]}].')).must_equal [ErlangTransform::Tuple.new([[ErlangTransform::Tuple.new([1])]])]
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue