simmer 1.0.0.pre.alpha.3 → 1.0.0.pre.alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -0
  4. data/lib/simmer.rb +103 -9
  5. data/lib/simmer/configuration.rb +7 -17
  6. data/lib/simmer/database.rb +10 -0
  7. data/lib/simmer/{util → database}/fixture.rb +7 -3
  8. data/lib/simmer/{util → database}/fixture_set.rb +10 -4
  9. data/lib/simmer/externals/aws_file_system.rb +30 -23
  10. data/lib/simmer/externals/mysql_database.rb +17 -10
  11. data/lib/simmer/externals/spoon_client.rb +5 -19
  12. data/lib/simmer/runner.rb +2 -2
  13. data/lib/simmer/specification/act.rb +1 -6
  14. data/lib/simmer/specification/assert/assertions/table.rb +1 -6
  15. data/lib/simmer/spoon_mock.rb +35 -0
  16. data/lib/simmer/suite.rb +49 -43
  17. data/lib/simmer/{session → suite}/reporter.rb +1 -1
  18. data/lib/simmer/{session → suite}/result.rb +1 -1
  19. data/lib/simmer/util.rb +0 -1
  20. data/lib/simmer/util/evaluator.rb +4 -5
  21. data/lib/simmer/util/record.rb +2 -0
  22. data/lib/simmer/util/record_set.rb +5 -1
  23. data/lib/simmer/util/resolver.rb +2 -2
  24. data/lib/simmer/util/yaml_reader.rb +9 -12
  25. data/lib/simmer/version.rb +1 -1
  26. data/spec/config/simmer.yaml.ci +7 -0
  27. data/spec/db/database.sql +1 -0
  28. data/spec/db/tables.sql +20 -0
  29. data/spec/db_helper.rb +26 -0
  30. data/spec/fixtures/agent_fixtures.yaml +14 -0
  31. data/spec/fixtures/configuration.yaml +11 -0
  32. data/spec/fixtures/noc_list.csv +3 -0
  33. data/spec/fixtures/specifications/load_noc_list.yaml +30 -0
  34. data/spec/fixtures/yaml_reader/bar.yaml +2 -0
  35. data/spec/fixtures/yaml_reader/baz/baz.yaml +2 -0
  36. data/spec/fixtures/yaml_reader/foo.yaml +2 -0
  37. data/spec/simmer/configuration_spec.rb +46 -0
  38. data/spec/simmer/database/fixture_set_spec.rb +75 -0
  39. data/spec/simmer/database/fixture_spec.rb +57 -0
  40. data/spec/simmer/externals/aws_file_system_spec.rb +75 -0
  41. data/spec/simmer/externals/mysql_database_spec.rb +79 -0
  42. data/spec/simmer/externals/spoon_client_spec.rb +67 -0
  43. data/spec/simmer/specification/act/params_spec.rb +38 -0
  44. data/spec/simmer/specification/act_spec.rb +37 -0
  45. data/spec/simmer/specification/assert_spec.rb +27 -0
  46. data/spec/simmer/specification/stage_spec.rb +32 -0
  47. data/spec/simmer/specification_spec.rb +28 -0
  48. data/spec/simmer/util/evaluator_spec.rb +82 -0
  49. data/spec/simmer/util/record_set_spec.rb +41 -0
  50. data/spec/simmer/util/record_spec.rb +218 -0
  51. data/spec/simmer/util/yaml_reader_spec.rb +49 -0
  52. data/spec/spec_helper.rb +21 -0
  53. metadata +60 -8
  54. data/lib/simmer/externals/spoon_client/mock.rb +0 -39
  55. data/lib/simmer/session.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f39dfd0f306f15f008f9074944a8a9f2180ba1188f45eda8568317c0a6f235d
4
- data.tar.gz: 41c1d9dd94a31a6fce03ec37ba6b9fc7067672bf05047babd1858ea336055756
3
+ metadata.gz: 1197e913b7b6a7b9a814fb2474aa9d89844af7c4efe872ae001116354a3b6cdb
4
+ data.tar.gz: 6e24db3887b3abb23489642547f64ce4c68af85493b06c54d0c9bc15c637cc44
5
5
  SHA512:
6
- metadata.gz: e659dde01a157945a3c7f64159384eee954696b1905200b5b1fdc3ff908f5c079ddf6dbfe2f46b3654b9ce045f504b21eff4a31eac951b54f465ba76b0dbe713
7
- data.tar.gz: ef419cbe1aa6dbc729522d63b11d2e2657c2c039ecde09efafcf62619ad1e6a263dae3082be2951c0e85a243e9375fbf3bf37b66df92469144d5ac6ca25d768d
6
+ metadata.gz: 3e994e8d2f87598a9baa31211e5baeae2210c8dc8e5e8337579ea2417c835162b12c59dbbd5e84d34d5f969bbe5770dc02ec8017c3e45fb0a1af6572a07f5916
7
+ data.tar.gz: 4f15f5aadaff3d595969aee68ad8c0689f879b1f6c0fde5fc7febe0f4aeff0fa25cc04c0cfeaa263fcca9e3e33ea20598d8d586803e34db68fd58bbf1739eb19
data/.gitignore CHANGED
@@ -5,3 +5,4 @@
5
5
  Gemfile.lock
6
6
  /pkg
7
7
  /bin/di
8
+ /spec/config/simmer.yaml
@@ -2,6 +2,8 @@ env:
2
2
  global:
3
3
  - CC_TEST_REPORTER_ID=48c3a48cc3c203ac365190a30b3c0a9aae0d2c10c9a86735e654f23b8660bb02
4
4
  language: ruby
5
+ services:
6
+ - mysql
5
7
  rvm:
6
8
  # Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
7
9
  - 2.4.6
@@ -13,6 +15,9 @@ before_script:
13
15
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
14
16
  - chmod +x ./cc-test-reporter
15
17
  - ./cc-test-reporter before-build
18
+ - cp spec/config/simmer.yaml.ci spec/config/simmer.yaml
19
+ - cat spec/db/database.sql | mysql
20
+ - cat spec/db/tables.sql | mysql
16
21
  script:
17
22
  - bundle exec rubocop
18
23
  - bundle exec rspec spec --format documentation
@@ -30,25 +30,119 @@ Hash.include Simmer::CoreExt::Hash
30
30
  require_relative 'simmer/util'
31
31
 
32
32
  # Core code
33
+ require_relative 'simmer/configuration'
34
+ require_relative 'simmer/database'
35
+ require_relative 'simmer/externals'
36
+ require_relative 'simmer/runner'
37
+ require_relative 'simmer/specification'
38
+ require_relative 'simmer/spoon_mock'
33
39
  require_relative 'simmer/suite'
34
40
 
35
41
  # The main entry-point API for the library.
36
42
  module Simmer
37
43
  DEFAULT_CONFIG_PATH = File.join('config', 'simmer.yaml')
38
- DEFAULT_RESULTS_DIR = 'results'
39
44
  DEFAULT_SIMMER_DIR = 'simmer'
45
+
40
46
  class << self
41
- def run(path)
42
- suite.run(path)
47
+ def run(
48
+ path,
49
+ config_path: DEFAULT_CONFIG_PATH,
50
+ out: $stdout,
51
+ simmer_dir: DEFAULT_SIMMER_DIR
52
+ )
53
+ # Get configuration
54
+ yaml_reader = Util::YamlReader.new
55
+ raw_config = yaml_reader.smash(config_path)
56
+ configuration = Configuration.new(raw_config, simmer_dir)
57
+
58
+ # Get fixtures
59
+ raw_fixtures = yaml_reader.smash(configuration.fixtures_dir)
60
+ fixtures = Database::FixtureSet.new(raw_fixtures)
61
+
62
+ # Get specifications to run
63
+ specs = make_specifications(path, configuration.tests_dir)
64
+
65
+ # Make main executable instances
66
+ runner = make_runner(configuration, out, fixtures)
67
+ suite = Suite.new(
68
+ config: configuration.config,
69
+ out: out,
70
+ results_dir: configuration.results_dir,
71
+ runner: runner
72
+ )
73
+
74
+ suite.run(specs)
75
+ end
76
+
77
+ private
78
+
79
+ def make_specifications(path, tests_dir)
80
+ path = path.to_s.empty? ? tests_dir : path
81
+
82
+ Util::YamlReader.new.read(path).map do |file|
83
+ config = (file.data || {}).merge(path: file.path)
84
+
85
+ Specification.make(config)
86
+ end
87
+ end
88
+
89
+ def make_runner(configuration, out, fixtures)
90
+ database = make_mysql_database(configuration, fixtures)
91
+ file_system = make_aws_file_system(configuration)
92
+ spoon_client = make_spoon_client(configuration)
93
+
94
+ Runner.new(
95
+ database: database,
96
+ file_system: file_system,
97
+ out: out,
98
+ spoon_client: spoon_client
99
+ )
100
+ end
101
+
102
+ def make_fixture_set(configuration)
103
+ config = Util::YamlReader.new.smash(configuration.fixtures_dir)
104
+
105
+ Database::FixtureSet.new(config)
106
+ end
107
+
108
+ def make_mysql_database(configuration, fixtures)
109
+ config = (configuration.mysql_database_config || {}).symbolize_keys
110
+ client = Mysql2::Client.new(config)
111
+ exclude_tables = config[:exclude_tables]
112
+
113
+ Externals::MysqlDatabase.new(client, exclude_tables, fixtures)
43
114
  end
44
115
 
45
- def suite
46
- Suite.new(
47
- out: $stdout,
48
- config_path: DEFAULT_CONFIG_PATH,
49
- results_dir: DEFAULT_RESULTS_DIR,
50
- simmer_dir: DEFAULT_SIMMER_DIR
116
+ def make_aws_file_system(configuration)
117
+ config = (configuration.aws_file_system_config || {}).symbolize_keys
118
+
119
+ client = Aws::S3::Client.new(
120
+ access_key_id: config[:access_key_id],
121
+ secret_access_key: config[:secret_access_key],
122
+ region: config[:region]
123
+ )
124
+
125
+ Externals::AwsFileSystem.new(
126
+ client,
127
+ config[:bucket],
128
+ config[:encryption],
129
+ configuration.files_dir
51
130
  )
52
131
  end
132
+
133
+ def make_spoon_client(configuration)
134
+ config = (configuration.spoon_client_config || {}).symbolize_keys
135
+
136
+ spoon =
137
+ if config[:mock]
138
+ SpoonMock.new
139
+ elsif config[:mock_err]
140
+ SpoonMock.new(false)
141
+ else
142
+ Pdi::Spoon.new(dir: config[:dir])
143
+ end
144
+
145
+ Externals::SpoonClient.new(configuration.files_dir, spoon)
146
+ end
53
147
  end
54
148
  end
@@ -19,6 +19,7 @@ module Simmer
19
19
  # Paths
20
20
  FILES = 'files'
21
21
  FIXTURES = 'fixtures'
22
+ RESULTS = 'results'
22
23
  TESTS = 'specs'
23
24
 
24
25
  private_constant :AWS_FILE_SYSTEM_KEY,
@@ -30,27 +31,19 @@ module Simmer
30
31
 
31
32
  attr_reader :config
32
33
 
33
- def initialize(
34
- config_path:,
35
- resolver: Objectable.resolver,
36
- results_dir:,
37
- simmer_dir:,
38
- yaml_reader: Util::YamlReader.new
39
- )
40
- @config = yaml_reader.read(config_path)
34
+ def initialize(config, simmer_dir, resolver: Objectable.resolver)
35
+ @config = config || {}
41
36
  @resolver = resolver
42
- @results_dir = results_dir
43
37
  @simmer_dir = simmer_dir
44
- @yaml_reader = yaml_reader
45
38
 
46
39
  freeze
47
40
  end
48
41
 
49
- def database_config
42
+ def mysql_database_config
50
43
  get(MYSQL_DATABASE_KEY) || {}
51
44
  end
52
45
 
53
- def file_system_config
46
+ def aws_file_system_config
54
47
  get(AWS_FILE_SYSTEM_KEY) || {}
55
48
  end
56
49
 
@@ -70,16 +63,13 @@ module Simmer
70
63
  File.join(simmer_dir, FILES)
71
64
  end
72
65
 
73
- def fixture_set
74
- config = yaml_reader.smash(fixtures_dir)
75
-
76
- Util::FixtureSet.new(config)
66
+ def results_dir
67
+ File.join(simmer_dir, RESULTS)
77
68
  end
78
69
 
79
70
  private
80
71
 
81
72
  attr_reader :resolver,
82
- :results_dir,
83
73
  :simmer_dir,
84
74
  :yaml_reader
85
75
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require_relative 'database/fixture_set'
@@ -8,7 +8,7 @@
8
8
  #
9
9
 
10
10
  module Simmer
11
- module Util
11
+ module Database
12
12
  # A fixture is a database record that can be inserted in the Stage phase of a specification
13
13
  # execution.
14
14
  class Fixture
@@ -24,9 +24,13 @@ module Simmer
24
24
  @table = table.to_s
25
25
  end
26
26
 
27
- def to_s
28
- "#{name} (#{table}) #{fields}"
27
+ def ==(other)
28
+ other.instance_of?(self.class) &&
29
+ fields == other.fields &&
30
+ name == other.name &&
31
+ table == other.table
29
32
  end
33
+ alias eql? ==
30
34
  end
31
35
  end
32
36
  end
@@ -10,13 +10,11 @@
10
10
  require_relative 'fixture'
11
11
 
12
12
  module Simmer
13
- module Util
13
+ module Database
14
14
  # Hydrate a collection of Fixture instances from configuration.
15
15
  class FixtureSet
16
16
  def initialize(config = {})
17
- @fixtures_by_name = config.each_with_object({}) do |(name, fixture_config), memo|
18
- memo[name.to_s] = Fixture.make((fixture_config || {}).merge('name' => name))
19
- end
17
+ @fixtures_by_name = config_to_fixures_by_name(config)
20
18
 
21
19
  freeze
22
20
  end
@@ -36,6 +34,14 @@ module Simmer
36
34
  private
37
35
 
38
36
  attr_reader :fixtures_by_name
37
+
38
+ def config_to_fixures_by_name(config)
39
+ (config || {}).each_with_object({}) do |(name, fixture_config), memo|
40
+ full_config = (fixture_config || {}).merge(name: name)
41
+
42
+ memo[name.to_s] = Fixture.make(full_config)
43
+ end
44
+ end
39
45
  end
40
46
  end
41
47
  end
@@ -15,26 +15,21 @@ module Simmer
15
15
 
16
16
  private_constant :BUCKET_SUFFIX
17
17
 
18
- def initialize(config, files_dir)
19
- config = (config || {}).symbolize_keys
18
+ def initialize(aws_s3_client, bucket, encryption, files_dir)
19
+ raise ArgumentError, 'aws_s3_client is required' unless aws_s3_client
20
+ raise ArgumentError, 'bucket is required' if bucket.to_s.empty?
20
21
 
21
- client = Aws::S3::Client.new(
22
- access_key_id: config[:access_key_id],
23
- secret_access_key: config[:secret_access_key],
24
- region: config[:region]
25
- )
26
-
27
- bucket_name = config[:bucket].to_s
28
- assert_bucket_name(bucket_name)
22
+ assert_bucket_name(bucket)
29
23
 
30
- @bucket = Aws::S3::Bucket.new(name: bucket_name, client: client)
31
- @encryption = config[:encryption]
32
- @files_dir = files_dir
24
+ @aws_s3_client = aws_s3_client
25
+ @bucket = bucket.to_s
26
+ @encryption = encryption
27
+ @files_dir = files_dir
33
28
 
34
29
  freeze
35
30
  end
36
31
 
37
- def write(specification)
32
+ def write!(specification)
38
33
  files = specification.stage.files
39
34
 
40
35
  files.each do |file|
@@ -46,24 +41,36 @@ module Simmer
46
41
  files.length
47
42
  end
48
43
 
49
- def clean
50
- bucket.objects.inject(0) do |memo, object|
51
- object.delete
44
+ def clean!
45
+ response = aws_s3_client.list_objects(bucket: bucket)
46
+ objects = response.contents
47
+ keys = objects.map(&:key)
48
+ delete_keys = keys.map { |key| { key: key } }
52
49
 
53
- memo + 1
54
- end
50
+ return 0 if objects.length.zero?
51
+
52
+ aws_s3_client.delete_objects(
53
+ bucket: bucket,
54
+ delete: {
55
+ objects: delete_keys
56
+ }
57
+ )
58
+
59
+ delete_keys.length
55
60
  end
56
61
 
57
62
  private
58
63
 
59
- attr_reader :bucket, :encryption, :files_dir
64
+ attr_reader :aws_s3_client, :bucket, :encryption, :files_dir
60
65
 
61
66
  def write_single(dest, src)
62
67
  src = File.expand_path(src)
63
68
 
64
69
  File.open(src, 'rb') do |file|
65
- bucket.object(dest).put(
66
- body: file,
70
+ aws_s3_client.put_object(
71
+ body: file.read,
72
+ bucket: bucket,
73
+ key: dest,
67
74
  server_side_encryption: encryption
68
75
  )
69
76
  end
@@ -72,7 +79,7 @@ module Simmer
72
79
  end
73
80
 
74
81
  def assert_bucket_name(name)
75
- return if name.end_with?(BUCKET_SUFFIX)
82
+ return if name.to_s.end_with?(BUCKET_SUFFIX)
76
83
 
77
84
  raise ArgumentError, "bucket (#{name}) must end in #{BUCKET_SUFFIX}"
78
85
  end
@@ -15,17 +15,14 @@ module Simmer
15
15
  class MysqlDatabase
16
16
  DATABASE_SUFFIX = 'test'
17
17
 
18
- def initialize(config, fixture_set)
19
- config = (config || {}).symbolize_keys
20
-
21
- database = config[:database].to_s
22
- assert_database_name(database)
23
-
24
- @client = Mysql2::Client.new(config)
18
+ def initialize(client, exclude_tables, fixture_set)
19
+ @client = client
25
20
  @fixture_set = fixture_set
26
- exclude_tables = Array(config[:exclude_tables]).map(&:to_s)
21
+ exclude_tables = Array(exclude_tables).map(&:to_s)
27
22
  @table_names = retrieve_table_names - exclude_tables
28
23
 
24
+ assert_database_name(schema)
25
+
29
26
  freeze
30
27
  end
31
28
 
@@ -97,9 +94,19 @@ module Simmer
97
94
  'SET @@foreign_key_checks = 1'
98
95
  end
99
96
 
97
+ def schema
98
+ client.query_options[:database].to_s
99
+ end
100
+
100
101
  def retrieve_table_names
101
- schema = client.escape(client.query_options[:database].to_s)
102
- sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '#{schema}'"
102
+ escaped_schema = client.escape(schema)
103
+
104
+ sql = <<~SQL
105
+ SELECT TABLE_NAME
106
+ FROM INFORMATION_SCHEMA.TABLES
107
+ WHERE TABLE_SCHEMA = '#{escaped_schema}'
108
+ AND TABLE_TYPE = 'BASE TABLE'
109
+ SQL
103
110
 
104
111
  client.query(sql).to_a.map { |v| v['TABLE_NAME'].to_s }
105
112
  end
@@ -7,36 +7,22 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
- require_relative 'spoon_client/mock'
11
10
  require_relative 'spoon_client/result'
12
11
 
13
12
  module Simmer
14
13
  module Externals
15
14
  # Wraps up Pdi::Spoon at a higher-level for Simmer to consume.
16
15
  class SpoonClient
17
- MOCK_KEY = :mock
18
- MOCK_ERR_KEY = :mock_err
16
+ def initialize(files_dir, spoon)
17
+ raise ArgumentError, 'spoon is required' unless spoon
19
18
 
20
- private_constant :MOCK_KEY, :MOCK_ERR_KEY
21
-
22
- def initialize(config, files_dir)
23
- @files_dir = files_dir
24
-
25
- config = (config || {}).symbolize_keys
26
-
27
- @spoon =
28
- if config[MOCK_KEY]
29
- Mock.new
30
- elsif config[MOCK_ERR_KEY]
31
- Mock.new(false)
32
- else
33
- Pdi::Spoon.new(dir: config[:dir])
34
- end
19
+ @files_dir = files_dir.to_s
20
+ @spoon = spoon
35
21
 
36
22
  freeze
37
23
  end
38
24
 
39
- def run(specification, config = {})
25
+ def run(specification, config)
40
26
  execution_result = nil
41
27
  time_in_seconds = nil
42
28