simmer 1.0.0.pre.alpha.5 → 1.0.0.pre.alpha.6
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/.travis.yml +0 -1
- data/lib/simmer/configuration.rb +16 -3
- data/lib/simmer/database/fixture_set.rb +0 -4
- data/lib/simmer/externals/aws_file_system.rb +13 -24
- data/lib/simmer/externals/file_system.rb +29 -0
- data/lib/simmer/externals/local_file_system.rb +56 -0
- data/lib/simmer/externals/mysql_database.rb +9 -15
- data/lib/simmer/externals.rb +1 -0
- data/lib/simmer/runner.rb +8 -4
- data/lib/simmer/suite/reporter.rb +0 -8
- data/lib/simmer/version.rb +1 -1
- data/lib/simmer.rb +30 -31
- data/simmer.gemspec +1 -1
- data/spec/config/simmer.yaml.ci +15 -0
- data/spec/fixtures/specifications/load_noc_list.yaml +1 -1
- data/spec/mocks/aws_s3_client.rb +56 -0
- data/spec/mocks/load_noc_list/pan.sh +10 -0
- data/spec/mocks/load_noc_list_bad_output/pan.sh +10 -0
- data/spec/mocks/out.rb +14 -0
- data/spec/mocks/spoon/kitchen.sh +14 -0
- data/spec/mocks/spoon/pan.sh +14 -0
- data/spec/simmer/externals/aws_file_system_spec.rb +11 -28
- data/spec/simmer/externals/mysql_database_spec.rb +12 -2
- data/spec/simmer/externals/spoon_client_spec.rb +19 -39
- data/spec/simmer/specification/assert_spec.rb +1 -1
- data/spec/simmer/util/record_set_spec.rb +26 -0
- data/spec/simmer_spec/files/noc_list.csv +3 -0
- data/spec/simmer_spec/fixtures/agent_fixtures.yaml +14 -0
- data/spec/simmer_spec.rb +104 -0
- metadata +23 -4
- data/lib/simmer/spoon_mock.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7bb744ed6c7545eff9608e7b4548b5b71afbc219d75cba872f93b6fb0e7db31
|
4
|
+
data.tar.gz: ab9487df40d1d7dd7030c09e9d35398cf45169ec5c1ac322165728ee40bdaa8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da855ec25b4bccb880dc974abab073cadd22467f36451320c1bf550599f818c72684c5c2b01e61453a8d498e46beb949c21035b1a012009c4ee37f9e36ef59ad
|
7
|
+
data.tar.gz: e906e083ade596051e93e5a67f64faa5c306ac112eaafa0dabb507c2e42d0daec0baa3434645ee39675b0b05ff9657db331ef435ff36417dc4d40294ce3f7023
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/lib/simmer/configuration.rb
CHANGED
@@ -12,9 +12,10 @@ module Simmer
|
|
12
12
|
# Simmer implementation.
|
13
13
|
class Configuration
|
14
14
|
# Configuration Keys
|
15
|
-
AWS_FILE_SYSTEM_KEY
|
16
|
-
|
17
|
-
|
15
|
+
AWS_FILE_SYSTEM_KEY = :aws_file_system
|
16
|
+
LOCAL_FILE_SYSTEM_KEY = :local_file_system
|
17
|
+
MYSQL_DATABASE_KEY = :mysql_database
|
18
|
+
SPOON_CLIENT_KEY = :spoon_client
|
18
19
|
|
19
20
|
# Paths
|
20
21
|
FILES = 'files'
|
@@ -47,6 +48,18 @@ module Simmer
|
|
47
48
|
get(AWS_FILE_SYSTEM_KEY) || {}
|
48
49
|
end
|
49
50
|
|
51
|
+
def local_file_system_config
|
52
|
+
get(LOCAL_FILE_SYSTEM_KEY) || {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def aws_file_system?
|
56
|
+
config.key?(AWS_FILE_SYSTEM_KEY.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
def local_file_system?
|
60
|
+
config.key?(LOCAL_FILE_SYSTEM_KEY.to_s)
|
61
|
+
end
|
62
|
+
|
50
63
|
def spoon_client_config
|
51
64
|
get(SPOON_CLIENT_KEY) || {}
|
52
65
|
end
|
@@ -7,42 +7,37 @@
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
8
8
|
#
|
9
9
|
|
10
|
+
require_relative 'file_system'
|
11
|
+
|
10
12
|
module Simmer
|
11
13
|
module Externals
|
12
14
|
# Provides the implementation for using AWS S3 as a destination file store.
|
13
|
-
class AwsFileSystem
|
14
|
-
BUCKET_SUFFIX = 'test'
|
15
|
-
|
16
|
-
private_constant :BUCKET_SUFFIX
|
17
|
-
|
15
|
+
class AwsFileSystem < FileSystem
|
18
16
|
def initialize(aws_s3_client, bucket, encryption, files_dir)
|
19
17
|
raise ArgumentError, 'aws_s3_client is required' unless aws_s3_client
|
20
18
|
raise ArgumentError, 'bucket is required' if bucket.to_s.empty?
|
21
19
|
|
22
|
-
|
20
|
+
super(bucket)
|
23
21
|
|
24
22
|
@aws_s3_client = aws_s3_client
|
25
|
-
@bucket = bucket.to_s
|
26
23
|
@encryption = encryption
|
27
24
|
@files_dir = files_dir
|
28
25
|
|
29
26
|
freeze
|
30
27
|
end
|
31
28
|
|
32
|
-
def write!(
|
33
|
-
|
34
|
-
|
35
|
-
files.each do |file|
|
36
|
-
src = File.join(files_dir, file.src)
|
29
|
+
def write!(input_files)
|
30
|
+
input_files.each do |input_file|
|
31
|
+
src = File.join(files_dir, input_file.src)
|
37
32
|
|
38
|
-
write_single(
|
33
|
+
write_single(input_file.dest, src)
|
39
34
|
end
|
40
35
|
|
41
|
-
|
36
|
+
input_files.length
|
42
37
|
end
|
43
38
|
|
44
39
|
def clean!
|
45
|
-
response = aws_s3_client.list_objects(bucket:
|
40
|
+
response = aws_s3_client.list_objects(bucket: root)
|
46
41
|
objects = response.contents
|
47
42
|
keys = objects.map(&:key)
|
48
43
|
delete_keys = keys.map { |key| { key: key } }
|
@@ -50,7 +45,7 @@ module Simmer
|
|
50
45
|
return 0 if objects.length.zero?
|
51
46
|
|
52
47
|
aws_s3_client.delete_objects(
|
53
|
-
bucket:
|
48
|
+
bucket: root,
|
54
49
|
delete: {
|
55
50
|
objects: delete_keys
|
56
51
|
}
|
@@ -61,7 +56,7 @@ module Simmer
|
|
61
56
|
|
62
57
|
private
|
63
58
|
|
64
|
-
attr_reader :aws_s3_client, :
|
59
|
+
attr_reader :aws_s3_client, :encryption, :files_dir
|
65
60
|
|
66
61
|
def write_single(dest, src)
|
67
62
|
src = File.expand_path(src)
|
@@ -69,7 +64,7 @@ module Simmer
|
|
69
64
|
File.open(src, 'rb') do |file|
|
70
65
|
aws_s3_client.put_object(
|
71
66
|
body: file.read,
|
72
|
-
bucket:
|
67
|
+
bucket: root,
|
73
68
|
key: dest,
|
74
69
|
server_side_encryption: encryption
|
75
70
|
)
|
@@ -77,12 +72,6 @@ module Simmer
|
|
77
72
|
|
78
73
|
nil
|
79
74
|
end
|
80
|
-
|
81
|
-
def assert_bucket_name(name)
|
82
|
-
return if name.to_s.end_with?(BUCKET_SUFFIX)
|
83
|
-
|
84
|
-
raise ArgumentError, "bucket (#{name}) must end in #{BUCKET_SUFFIX}"
|
85
|
-
end
|
86
75
|
end
|
87
76
|
end
|
88
77
|
end
|
@@ -0,0 +1,29 @@
|
|
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
|
+
module Simmer
|
11
|
+
module Externals
|
12
|
+
# Provides the shared basics of all file systems.
|
13
|
+
class FileSystem
|
14
|
+
SUFFIX = 'test'
|
15
|
+
|
16
|
+
private_constant :SUFFIX
|
17
|
+
|
18
|
+
def initialize(root)
|
19
|
+
@root = root.to_s
|
20
|
+
|
21
|
+
raise ArgumentError, "root: #{root} must end in #{SUFFIX}" unless root.end_with?(SUFFIX)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :root
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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 'file_system'
|
11
|
+
|
12
|
+
module Simmer
|
13
|
+
module Externals
|
14
|
+
# Provides the implementation for using a local directory
|
15
|
+
class LocalFileSystem < FileSystem
|
16
|
+
def initialize(root, files_dir)
|
17
|
+
root = File.expand_path(root.to_s)
|
18
|
+
|
19
|
+
super(root)
|
20
|
+
|
21
|
+
@files_dir = files_dir.to_s
|
22
|
+
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def write!(input_files)
|
27
|
+
input_files.each do |input_file|
|
28
|
+
src = File.join(files_dir, input_file.src)
|
29
|
+
|
30
|
+
write_single(input_file.dest, src)
|
31
|
+
end
|
32
|
+
|
33
|
+
input_files.length
|
34
|
+
end
|
35
|
+
|
36
|
+
def clean!
|
37
|
+
glob = File.join(root, '**', '*')
|
38
|
+
all_files = Dir[glob].reject { |p| File.directory?(p) }
|
39
|
+
|
40
|
+
all_files.each { |path| FileUtils.rm(path) }
|
41
|
+
all_files.length
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :files_dir
|
47
|
+
|
48
|
+
def write_single(dest, src)
|
49
|
+
full_dest = File.join(root, dest)
|
50
|
+
dest_dir = File.dirname(full_dest)
|
51
|
+
FileUtils.mkdir_p(dest_dir)
|
52
|
+
FileUtils.cp(src, full_dest)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -15,14 +15,14 @@ module Simmer
|
|
15
15
|
class MysqlDatabase
|
16
16
|
DATABASE_SUFFIX = 'test'
|
17
17
|
|
18
|
-
def initialize(client, exclude_tables
|
19
|
-
@client
|
20
|
-
@fixture_set = fixture_set
|
21
|
-
exclude_tables = Array(exclude_tables).map(&:to_s)
|
22
|
-
@table_names = retrieve_table_names - exclude_tables
|
18
|
+
def initialize(client, exclude_tables = [])
|
19
|
+
@client = client
|
23
20
|
|
24
21
|
assert_database_name(schema)
|
25
22
|
|
23
|
+
exclude_tables = Array(exclude_tables).map(&:to_s)
|
24
|
+
@table_names = retrieve_table_names - exclude_tables
|
25
|
+
|
26
26
|
freeze
|
27
27
|
end
|
28
28
|
|
@@ -32,8 +32,8 @@ module Simmer
|
|
32
32
|
client.query(query).to_a
|
33
33
|
end
|
34
34
|
|
35
|
-
def seed!(
|
36
|
-
sql_statements = seed_sql_statements(
|
35
|
+
def seed!(fixtures)
|
36
|
+
sql_statements = seed_sql_statements(fixtures)
|
37
37
|
|
38
38
|
shameless_execute(sql_statements)
|
39
39
|
|
@@ -56,14 +56,8 @@ module Simmer
|
|
56
56
|
Array(columns).any? ? Array(columns).map { |c| client.escape(c) }.join(',') : '*'
|
57
57
|
end
|
58
58
|
|
59
|
-
def seed_sql_statements(
|
60
|
-
|
61
|
-
|
62
|
-
fixture_names.map do |fixture_name|
|
63
|
-
fixture = fixture_set.get!(fixture_name)
|
64
|
-
|
65
|
-
SqlFixture.new(client, fixture).to_sql
|
66
|
-
end
|
59
|
+
def seed_sql_statements(fixtures)
|
60
|
+
fixtures.map { |fixture| SqlFixture.new(client, fixture).to_sql }
|
67
61
|
end
|
68
62
|
|
69
63
|
def clean_sql_statements
|
data/lib/simmer/externals.rb
CHANGED
data/lib/simmer/runner.rb
CHANGED
@@ -13,9 +13,10 @@ require_relative 'runner/result'
|
|
13
13
|
module Simmer
|
14
14
|
# Runs a single specification.
|
15
15
|
class Runner
|
16
|
-
def initialize(database:, file_system:, out:, spoon_client:)
|
16
|
+
def initialize(database:, file_system:, fixture_set:, out:, spoon_client:)
|
17
17
|
@database = database
|
18
18
|
@file_system = file_system
|
19
|
+
@fixture_set = fixture_set
|
19
20
|
@judge = Judge.new(database)
|
20
21
|
@out = out
|
21
22
|
@spoon_client = spoon_client
|
@@ -44,7 +45,7 @@ module Simmer
|
|
44
45
|
|
45
46
|
private
|
46
47
|
|
47
|
-
attr_reader :database, :file_system, :judge, :out, :spoon_client
|
48
|
+
attr_reader :database, :file_system, :fixture_set, :judge, :out, :spoon_client
|
48
49
|
|
49
50
|
def clean_db
|
50
51
|
print_waiting('Stage', 'Cleaning database')
|
@@ -56,7 +57,10 @@ module Simmer
|
|
56
57
|
|
57
58
|
def seed_db(specification)
|
58
59
|
print_waiting('Stage', 'Seeding database')
|
59
|
-
|
60
|
+
|
61
|
+
fixtures = specification.stage.fixtures.map { |f| fixture_set.get!(f) }
|
62
|
+
count = database.seed!(fixtures)
|
63
|
+
|
60
64
|
print("#{count} record(s) inserted")
|
61
65
|
|
62
66
|
count
|
@@ -72,7 +76,7 @@ module Simmer
|
|
72
76
|
|
73
77
|
def seed_file_system(specification)
|
74
78
|
print_waiting('Stage', 'Seeding File System')
|
75
|
-
count = file_system.write!(specification)
|
79
|
+
count = file_system.write!(specification.stage.files)
|
76
80
|
print("#{count} file(s) uploaded")
|
77
81
|
|
78
82
|
count
|
data/lib/simmer/version.rb
CHANGED
data/lib/simmer.rb
CHANGED
@@ -35,7 +35,6 @@ require_relative 'simmer/database'
|
|
35
35
|
require_relative 'simmer/externals'
|
36
36
|
require_relative 'simmer/runner'
|
37
37
|
require_relative 'simmer/specification'
|
38
|
-
require_relative 'simmer/spoon_mock'
|
39
38
|
require_relative 'simmer/suite'
|
40
39
|
|
41
40
|
# The main entry-point API for the library.
|
@@ -51,9 +50,8 @@ module Simmer
|
|
51
50
|
simmer_dir: DEFAULT_SIMMER_DIR
|
52
51
|
)
|
53
52
|
configuration = make_configuration(config_path: config_path, simmer_dir: simmer_dir)
|
54
|
-
fixtures = make_fixtures(configuration)
|
55
53
|
specs = make_specifications(path, configuration.tests_dir)
|
56
|
-
runner = make_runner(configuration, out
|
54
|
+
runner = make_runner(configuration, out)
|
57
55
|
suite = make_suite(configuration, out, runner)
|
58
56
|
|
59
57
|
suite.run(specs)
|
@@ -84,14 +82,16 @@ module Simmer
|
|
84
82
|
end
|
85
83
|
end
|
86
84
|
|
87
|
-
def make_runner(configuration, out
|
88
|
-
database = make_mysql_database(configuration
|
89
|
-
file_system =
|
85
|
+
def make_runner(configuration, out)
|
86
|
+
database = make_mysql_database(configuration)
|
87
|
+
file_system = make_file_system(configuration)
|
88
|
+
fixture_set = make_fixture_set(configuration)
|
90
89
|
spoon_client = make_spoon_client(configuration)
|
91
90
|
|
92
91
|
Runner.new(
|
93
92
|
database: database,
|
94
93
|
file_system: file_system,
|
94
|
+
fixture_set: fixture_set,
|
95
95
|
out: out,
|
96
96
|
spoon_client: spoon_client
|
97
97
|
)
|
@@ -103,22 +103,28 @@ module Simmer
|
|
103
103
|
Database::FixtureSet.new(config)
|
104
104
|
end
|
105
105
|
|
106
|
-
def make_mysql_database(configuration
|
107
|
-
config =
|
106
|
+
def make_mysql_database(configuration)
|
107
|
+
config = configuration.mysql_database_config.symbolize_keys
|
108
108
|
client = Mysql2::Client.new(config)
|
109
109
|
exclude_tables = config[:exclude_tables]
|
110
110
|
|
111
|
-
Externals::MysqlDatabase.new(client, exclude_tables
|
111
|
+
Externals::MysqlDatabase.new(client, exclude_tables)
|
112
112
|
end
|
113
113
|
|
114
|
-
def
|
115
|
-
|
114
|
+
def make_file_system(configuration)
|
115
|
+
if configuration.aws_file_system?
|
116
|
+
make_aws_file_system(configuration)
|
117
|
+
elsif configuration.local_file_system?
|
118
|
+
make_local_file_system(configuration)
|
119
|
+
else
|
120
|
+
raise ArgumentError, 'cannot determine file system'
|
121
|
+
end
|
122
|
+
end
|
116
123
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
)
|
124
|
+
def make_aws_file_system(configuration)
|
125
|
+
config = configuration.aws_file_system_config.symbolize_keys
|
126
|
+
client_args = config.slice(:access_key_id, :secret_access_key, :region)
|
127
|
+
client = Aws::S3::Client.new(client_args)
|
122
128
|
|
123
129
|
Externals::AwsFileSystem.new(
|
124
130
|
client,
|
@@ -128,25 +134,18 @@ module Simmer
|
|
128
134
|
)
|
129
135
|
end
|
130
136
|
|
131
|
-
def
|
132
|
-
config =
|
133
|
-
|
134
|
-
spoon =
|
135
|
-
if config[:mock]
|
136
|
-
SpoonMock.new
|
137
|
-
elsif config[:mock_err]
|
138
|
-
SpoonMock.new(false)
|
139
|
-
else
|
140
|
-
Pdi::Spoon.new(dir: config[:dir])
|
141
|
-
end
|
137
|
+
def make_local_file_system(configuration)
|
138
|
+
config = configuration.local_file_system_config.symbolize_keys
|
142
139
|
|
143
|
-
Externals::
|
140
|
+
Externals::LocalFileSystem.new(config[:dir], configuration.files_dir)
|
144
141
|
end
|
145
142
|
|
146
|
-
def
|
147
|
-
|
143
|
+
def make_spoon_client(configuration)
|
144
|
+
config = (configuration.spoon_client_config || {}).symbolize_keys
|
145
|
+
spoon_args = config.slice(:args, :dir, :kitchen, :pan)
|
146
|
+
spoon = Pdi::Spoon.new(spoon_args)
|
148
147
|
|
149
|
-
|
148
|
+
Externals::SpoonClient.new(configuration.files_dir, spoon)
|
150
149
|
end
|
151
150
|
|
152
151
|
def make_suite(configuration, out, runner)
|
data/simmer.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.homepage = 'https://github.com/bluemarblepayroll/simmer'
|
20
20
|
s.license = 'MIT'
|
21
21
|
|
22
|
-
s.required_ruby_version = '>= 2.
|
22
|
+
s.required_ruby_version = '>= 2.5'
|
23
23
|
|
24
24
|
s.add_dependency('acts_as_hashable', '~>1')
|
25
25
|
s.add_dependency('aws-sdk-s3', '~>1.6')
|
data/spec/config/simmer.yaml.ci
CHANGED
@@ -5,3 +5,18 @@ mysql_database:
|
|
5
5
|
host: 127.0.0.1
|
6
6
|
port: 3306
|
7
7
|
flags: MULTI_STATEMENTS
|
8
|
+
|
9
|
+
spoon_client:
|
10
|
+
dir: spec/mocks/spoon
|
11
|
+
args: 0
|
12
|
+
|
13
|
+
local_file_system:
|
14
|
+
dir: tmp/store_test
|
15
|
+
|
16
|
+
# aws_file_system:
|
17
|
+
# access_key_id:
|
18
|
+
# bucket:
|
19
|
+
# default_expires_in_seconds: 3600
|
20
|
+
# encryption: AES256
|
21
|
+
# region:
|
22
|
+
# secret_access_key:
|
@@ -0,0 +1,56 @@
|
|
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
|
+
class AwsS3Client
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators :client,
|
14
|
+
:list_objects,
|
15
|
+
:delete_objects,
|
16
|
+
:put_object
|
17
|
+
|
18
|
+
attr_reader :client, :store
|
19
|
+
|
20
|
+
def initialize(store = {})
|
21
|
+
@store = store
|
22
|
+
@client = make_client
|
23
|
+
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# rubocop:disable Metrics/AbcSize
|
30
|
+
def make_client
|
31
|
+
Aws::S3::Client.new(stub_responses: true).tap do |client|
|
32
|
+
client.stub_responses(:get_object, lambda { |context|
|
33
|
+
obj = store[context.params[:key]]
|
34
|
+
obj || 'NoSuchKey'
|
35
|
+
})
|
36
|
+
|
37
|
+
client.stub_responses(:put_object, lambda { |context|
|
38
|
+
store[context.params[:key]] = { body: context.params[:body] }
|
39
|
+
{}
|
40
|
+
})
|
41
|
+
|
42
|
+
client.stub_responses(:list_objects, lambda { |_context|
|
43
|
+
contents = store.keys.map { |k| OpenStruct.new(key: k) }
|
44
|
+
|
45
|
+
OpenStruct.new(contents: contents)
|
46
|
+
})
|
47
|
+
|
48
|
+
client.stub_responses(:delete_objects, lambda { |context|
|
49
|
+
keys = context.params.dig(:delete, :objects).map { |k| k[:key] }
|
50
|
+
|
51
|
+
keys.each { |key| store.delete(key) }
|
52
|
+
})
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/AbcSize
|
56
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require './spec/spec_helper'
|
5
|
+
require './spec/db_helper'
|
6
|
+
|
7
|
+
puts 'output to stdout'
|
8
|
+
|
9
|
+
db_helper_client.query("UPDATE agents SET first = 'bruce', last = 'banner' WHERE call_sign = 'hulk'")
|
10
|
+
db_helper_client.query("UPDATE agents SET first = 'tony', last = 'stark' WHERE call_sign = 'iron_man'")
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require './spec/spec_helper'
|
5
|
+
require './spec/db_helper'
|
6
|
+
|
7
|
+
puts 'bad output!'
|
8
|
+
|
9
|
+
db_helper_client.query("UPDATE agents SET first = 'bruce', last = 'banner' WHERE call_sign = 'hulk'")
|
10
|
+
db_helper_client.query("UPDATE agents SET first = 'tony', last = 'stark' WHERE call_sign = 'iron_man'")
|
data/spec/mocks/out.rb
ADDED
@@ -0,0 +1,14 @@
|
|
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
|
+
class Out
|
11
|
+
def puts(msg); end
|
12
|
+
|
13
|
+
def print(msg); end
|
14
|
+
end
|
@@ -8,46 +8,29 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
require 'spec_helper'
|
11
|
+
require 'mocks/aws_s3_client'
|
11
12
|
|
12
13
|
describe Simmer::Externals::AwsFileSystem do
|
13
|
-
let(:bucket_store) { {} }
|
14
14
|
let(:bucket_name) { 'test' }
|
15
15
|
let(:encryption) { 'AES256' }
|
16
16
|
let(:files_dir) { File.join('spec', 'fixtures') }
|
17
17
|
let(:specification_path) { File.join('specifications', 'load_noc_list.yaml') }
|
18
18
|
let(:specification_config) { yaml_fixture(specification_path).merge(path: specification_path) }
|
19
19
|
let(:specification) { Simmer::Specification.make(specification_config) }
|
20
|
+
let(:aws_s3_client_stub) { AwsS3Client.new }
|
20
21
|
|
21
|
-
|
22
|
-
Aws::S3::Client.new(stub_responses: true).tap do |client|
|
23
|
-
client.stub_responses(:get_object, lambda { |context|
|
24
|
-
obj = bucket_store[context.params[:key]]
|
25
|
-
obj || 'NoSuchKey'
|
26
|
-
})
|
27
|
-
|
28
|
-
client.stub_responses(:put_object, lambda { |context|
|
29
|
-
bucket_store[context.params[:key]] = { body: context.params[:body] }
|
30
|
-
{}
|
31
|
-
})
|
32
|
-
|
33
|
-
client.stub_responses(:list_objects, lambda { |_context|
|
34
|
-
contents = bucket_store.keys.map { |k| OpenStruct.new(key: k) }
|
35
|
-
|
36
|
-
OpenStruct.new(contents: contents)
|
37
|
-
})
|
38
|
-
|
39
|
-
client.stub_responses(:delete_objects, lambda { |context|
|
40
|
-
keys = context.params.dig(:delete, :objects).map { |k| k[:key] }
|
22
|
+
subject { described_class.new(aws_s3_client_stub, bucket_name, encryption, files_dir) }
|
41
23
|
|
42
|
-
|
43
|
-
|
24
|
+
describe 'initialization' do
|
25
|
+
it "requires bucket ends in 'test'" do
|
26
|
+
expect do
|
27
|
+
described_class.new(aws_s3_client_stub, 'hehe', encryption, files_dir)
|
28
|
+
end.to raise_error(ArgumentError)
|
44
29
|
end
|
45
30
|
end
|
46
31
|
|
47
|
-
subject { described_class.new(aws_s3_client_stub, bucket_name, encryption, files_dir) }
|
48
|
-
|
49
32
|
specify '#write transfers all files' do
|
50
|
-
subject.write!(specification)
|
33
|
+
subject.write!(specification.stage.files)
|
51
34
|
|
52
35
|
expected = {
|
53
36
|
'input/noc_list.csv' => {
|
@@ -55,7 +38,7 @@ describe Simmer::Externals::AwsFileSystem do
|
|
55
38
|
}
|
56
39
|
}
|
57
40
|
|
58
|
-
expect(
|
41
|
+
expect(aws_s3_client_stub.store).to eq(expected)
|
59
42
|
end
|
60
43
|
|
61
44
|
specify '#clean! deletes all files' do
|
@@ -70,6 +53,6 @@ describe Simmer::Externals::AwsFileSystem do
|
|
70
53
|
|
71
54
|
expected = {}
|
72
55
|
|
73
|
-
expect(
|
56
|
+
expect(aws_s3_client_stub.store).to eq(expected)
|
74
57
|
end
|
75
58
|
end
|
@@ -18,14 +18,24 @@ describe Simmer::Externals::MysqlDatabase do
|
|
18
18
|
let(:spec_config) { yaml_fixture(spec_path).merge(path: spec_path) }
|
19
19
|
let(:specification) { Simmer::Specification.make(spec_config) }
|
20
20
|
|
21
|
-
subject { described_class.new(db_helper_client, exclude_tables
|
21
|
+
subject { described_class.new(db_helper_client, exclude_tables) }
|
22
22
|
|
23
23
|
before(:each) do
|
24
24
|
db_helper_clean_schema
|
25
25
|
end
|
26
26
|
|
27
|
+
describe 'initialization' do
|
28
|
+
it "requires database ends in 'test'" do
|
29
|
+
expect do
|
30
|
+
described_class.new(OpenStruct.new(query_options: { database: 'hehe' }))
|
31
|
+
end.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
specify '#seed! adds records' do
|
28
|
-
|
36
|
+
fixtures = specification.stage.fixtures.map { |f| fixture_set.get!(f) }
|
37
|
+
|
38
|
+
subject.seed!(fixtures)
|
29
39
|
|
30
40
|
actual = db_helper_client.query('SELECT * FROM agents ORDER BY call_sign').to_a
|
31
41
|
|
@@ -11,57 +11,37 @@ require 'spec_helper'
|
|
11
11
|
require 'db_helper'
|
12
12
|
|
13
13
|
describe Simmer::Externals::SpoonClient do
|
14
|
-
class Mock
|
15
|
-
attr_reader :result
|
16
|
-
|
17
|
-
def initialize(result)
|
18
|
-
@result = result
|
19
|
-
end
|
20
|
-
|
21
|
-
def run(*)
|
22
|
-
raise Pdi::Spoon::KitchenError, OpenStruct.new(code: 1) if result == 'KitchenError'
|
23
|
-
raise Pdi::Spoon::PanError, OpenStruct.new(code: 1) if result == 'PanError'
|
24
|
-
|
25
|
-
Pdi::Executor::Result.new(result)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
14
|
let(:files_dir) { File.join('spec', 'fixtures') }
|
30
15
|
let(:specification_path) { File.join('specifications', 'load_noc_list.yaml') }
|
31
16
|
let(:specification_config) { yaml_fixture(specification_path).merge(path: specification_path) }
|
32
17
|
let(:specification) { Simmer::Specification.make(specification_config) }
|
33
18
|
|
19
|
+
let(:spoon) do
|
20
|
+
Pdi::Spoon.new(
|
21
|
+
dir: File.join('spec', 'mocks', 'spoon'),
|
22
|
+
args: arg
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
34
26
|
subject { described_class.new(files_dir, spoon) }
|
35
27
|
|
36
28
|
context 'when PDI executes successfully' do
|
37
|
-
let(:
|
38
|
-
Mock.new(
|
39
|
-
args: [],
|
40
|
-
status: {
|
41
|
-
code: 0,
|
42
|
-
err: 'Some error output from PDI',
|
43
|
-
out: 'Some output from PDI',
|
44
|
-
pid: 123
|
45
|
-
}
|
46
|
-
)
|
47
|
-
end
|
29
|
+
let(:arg) { 0 }
|
48
30
|
|
49
|
-
specify '#run
|
50
|
-
|
31
|
+
specify '#run returns code 0 from executor' do
|
32
|
+
result = subject.run(specification, simmer_config)
|
33
|
+
|
34
|
+
expect(result.execution_result.code).to eq(0)
|
35
|
+
end
|
36
|
+
end
|
51
37
|
|
52
|
-
|
53
|
-
|
54
|
-
name: 'load_noc_list',
|
55
|
-
params: {
|
56
|
-
'input_file' => expected_path,
|
57
|
-
'code' => 'The secret code is: '
|
58
|
-
},
|
59
|
-
type: 'transformation'
|
60
|
-
}
|
38
|
+
context 'when PDI executes un-successfully' do
|
39
|
+
let(:arg) { 1 }
|
61
40
|
|
62
|
-
|
41
|
+
specify '#run returns non-zero code from executor' do
|
42
|
+
result = subject.run(specification, simmer_config)
|
63
43
|
|
64
|
-
|
44
|
+
expect(result.execution_result.code).to eq(1)
|
65
45
|
end
|
66
46
|
end
|
67
47
|
end
|
@@ -21,7 +21,7 @@ describe Simmer::Specification::Assert do
|
|
21
21
|
expect(subject.assertions.length).to eq(2)
|
22
22
|
expect(subject.assertions.first.name).to eq('agents')
|
23
23
|
expect(subject.assertions.first.record_set.length).to eq(2)
|
24
|
-
expect(subject.assertions.last.value).to eq('
|
24
|
+
expect(subject.assertions.last.value).to eq('output to stdout')
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -17,6 +17,13 @@ describe Simmer::Util::RecordSet do
|
|
17
17
|
}
|
18
18
|
end
|
19
19
|
|
20
|
+
let(:string_hash1) do
|
21
|
+
{
|
22
|
+
'a' => 'a',
|
23
|
+
'b' => 'b'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
20
27
|
let(:hash2) do
|
21
28
|
{
|
22
29
|
c: 'c',
|
@@ -24,6 +31,13 @@ describe Simmer::Util::RecordSet do
|
|
24
31
|
}
|
25
32
|
end
|
26
33
|
|
34
|
+
let(:string_hash2) do
|
35
|
+
{
|
36
|
+
'c' => 'c',
|
37
|
+
'd' => 'd'
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
27
41
|
subject { described_class.new([hash1, hash2]) }
|
28
42
|
|
29
43
|
describe 'equality' do
|
@@ -38,4 +52,16 @@ describe Simmer::Util::RecordSet do
|
|
38
52
|
specify '#keys includes all record keys' do
|
39
53
|
expect(subject.keys).to eq(%w[a b c d])
|
40
54
|
end
|
55
|
+
|
56
|
+
specify '#to_h includes record hashes' do
|
57
|
+
expect(subject.to_h).to eq('records' => [string_hash1, string_hash2])
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'initialization' do
|
61
|
+
specify 'can accept a hash' do
|
62
|
+
subject = described_class.new(hash1)
|
63
|
+
|
64
|
+
expect(subject.records.length).to eq(1)
|
65
|
+
end
|
66
|
+
end
|
41
67
|
end
|
data/spec/simmer_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
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 'spec_helper'
|
11
|
+
require './spec/mocks/out'
|
12
|
+
|
13
|
+
describe Simmer do
|
14
|
+
def stage_simmer_config(spoon_dir, args)
|
15
|
+
src_config_path = File.join('spec', 'config', 'simmer.yaml')
|
16
|
+
dest_config_dir = File.join('tmp')
|
17
|
+
dest_config_path = File.join(dest_config_dir, 'simmer.yaml')
|
18
|
+
|
19
|
+
FileUtils.rm(dest_config_path) if File.exist?(dest_config_path)
|
20
|
+
FileUtils.mkdir_p(dest_config_dir)
|
21
|
+
|
22
|
+
config = yaml_read(src_config_path)
|
23
|
+
|
24
|
+
new_config = config.merge('spoon_client' => {
|
25
|
+
'dir' => spoon_dir,
|
26
|
+
'args' => args
|
27
|
+
})
|
28
|
+
|
29
|
+
IO.write(dest_config_path, new_config.to_yaml)
|
30
|
+
|
31
|
+
dest_config_path
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when externally mocking pdi and using local file system' do
|
35
|
+
let(:spec_path) { File.join('spec', 'fixtures', 'specifications', 'load_noc_list.yaml') }
|
36
|
+
let(:simmer_dir) { File.join('spec', 'simmer_spec') }
|
37
|
+
let(:out) { Out.new }
|
38
|
+
let(:config_path) { stage_simmer_config(spoon_path, args) }
|
39
|
+
|
40
|
+
context 'when pdi does not do anything but does not fail' do
|
41
|
+
let(:spoon_path) { File.join('spec', 'mocks', 'spoon') }
|
42
|
+
let(:args) { 0 }
|
43
|
+
|
44
|
+
specify 'judge determines it does not pass' do
|
45
|
+
results = described_class.run(
|
46
|
+
spec_path,
|
47
|
+
config_path: config_path,
|
48
|
+
out: out,
|
49
|
+
simmer_dir: simmer_dir
|
50
|
+
)
|
51
|
+
|
52
|
+
expect(results.pass?).to be false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when pdi fails' do
|
57
|
+
let(:spoon_path) { File.join('spec', 'mocks', 'spoon') }
|
58
|
+
let(:args) { 1 }
|
59
|
+
|
60
|
+
specify 'judge determines it does not pass' do
|
61
|
+
results = described_class.run(
|
62
|
+
spec_path,
|
63
|
+
config_path: config_path,
|
64
|
+
out: out,
|
65
|
+
simmer_dir: simmer_dir
|
66
|
+
)
|
67
|
+
|
68
|
+
expect(results.pass?).to be false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when pdi acts correctly' do
|
73
|
+
let(:spoon_path) { File.join('spec', 'mocks', 'load_noc_list') }
|
74
|
+
let(:args) { '' }
|
75
|
+
|
76
|
+
specify 'judge determines it to pass' do
|
77
|
+
results = described_class.run(
|
78
|
+
spec_path,
|
79
|
+
config_path: config_path,
|
80
|
+
out: out,
|
81
|
+
simmer_dir: simmer_dir
|
82
|
+
)
|
83
|
+
|
84
|
+
expect(results.pass?).to be true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when pdi accts correctly but judge fails on output assert' do
|
89
|
+
let(:spoon_path) { File.join('spec', 'mocks', 'load_noc_list_bad_output') }
|
90
|
+
let(:args) { '' }
|
91
|
+
|
92
|
+
specify 'judge determines it to pass' do
|
93
|
+
results = described_class.run(
|
94
|
+
spec_path,
|
95
|
+
config_path: config_path,
|
96
|
+
out: out,
|
97
|
+
simmer_dir: simmer_dir
|
98
|
+
)
|
99
|
+
|
100
|
+
expect(results.pass?).to be false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.alpha.
|
4
|
+
version: 1.0.0.pre.alpha.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acts_as_hashable
|
@@ -223,6 +223,8 @@ files:
|
|
223
223
|
- lib/simmer/database/fixture_set.rb
|
224
224
|
- lib/simmer/externals.rb
|
225
225
|
- lib/simmer/externals/aws_file_system.rb
|
226
|
+
- lib/simmer/externals/file_system.rb
|
227
|
+
- lib/simmer/externals/local_file_system.rb
|
226
228
|
- lib/simmer/externals/mysql_database.rb
|
227
229
|
- lib/simmer/externals/mysql_database/sql_fixture.rb
|
228
230
|
- lib/simmer/externals/spoon_client.rb
|
@@ -242,7 +244,6 @@ files:
|
|
242
244
|
- lib/simmer/specification/assert/assertions/table.rb
|
243
245
|
- lib/simmer/specification/stage.rb
|
244
246
|
- lib/simmer/specification/stage/input_file.rb
|
245
|
-
- lib/simmer/spoon_mock.rb
|
246
247
|
- lib/simmer/suite.rb
|
247
248
|
- lib/simmer/suite/reporter.rb
|
248
249
|
- lib/simmer/suite/result.rb
|
@@ -265,6 +266,12 @@ files:
|
|
265
266
|
- spec/fixtures/yaml_reader/bar.yaml
|
266
267
|
- spec/fixtures/yaml_reader/baz/baz.yaml
|
267
268
|
- spec/fixtures/yaml_reader/foo.yaml
|
269
|
+
- spec/mocks/aws_s3_client.rb
|
270
|
+
- spec/mocks/load_noc_list/pan.sh
|
271
|
+
- spec/mocks/load_noc_list_bad_output/pan.sh
|
272
|
+
- spec/mocks/out.rb
|
273
|
+
- spec/mocks/spoon/kitchen.sh
|
274
|
+
- spec/mocks/spoon/pan.sh
|
268
275
|
- spec/simmer/configuration_spec.rb
|
269
276
|
- spec/simmer/core_ext/hash_spec.rb
|
270
277
|
- spec/simmer/database/fixture_set_spec.rb
|
@@ -281,6 +288,9 @@ files:
|
|
281
288
|
- spec/simmer/util/record_set_spec.rb
|
282
289
|
- spec/simmer/util/record_spec.rb
|
283
290
|
- spec/simmer/util/yaml_reader_spec.rb
|
291
|
+
- spec/simmer_spec.rb
|
292
|
+
- spec/simmer_spec/files/noc_list.csv
|
293
|
+
- spec/simmer_spec/fixtures/agent_fixtures.yaml
|
284
294
|
- spec/spec_helper.rb
|
285
295
|
homepage: https://github.com/bluemarblepayroll/simmer
|
286
296
|
licenses:
|
@@ -294,7 +304,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
294
304
|
requirements:
|
295
305
|
- - ">="
|
296
306
|
- !ruby/object:Gem::Version
|
297
|
-
version: 2.
|
307
|
+
version: '2.5'
|
298
308
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
299
309
|
requirements:
|
300
310
|
- - ">"
|
@@ -317,6 +327,12 @@ test_files:
|
|
317
327
|
- spec/fixtures/yaml_reader/bar.yaml
|
318
328
|
- spec/fixtures/yaml_reader/baz/baz.yaml
|
319
329
|
- spec/fixtures/yaml_reader/foo.yaml
|
330
|
+
- spec/mocks/aws_s3_client.rb
|
331
|
+
- spec/mocks/load_noc_list/pan.sh
|
332
|
+
- spec/mocks/load_noc_list_bad_output/pan.sh
|
333
|
+
- spec/mocks/out.rb
|
334
|
+
- spec/mocks/spoon/kitchen.sh
|
335
|
+
- spec/mocks/spoon/pan.sh
|
320
336
|
- spec/simmer/configuration_spec.rb
|
321
337
|
- spec/simmer/core_ext/hash_spec.rb
|
322
338
|
- spec/simmer/database/fixture_set_spec.rb
|
@@ -333,4 +349,7 @@ test_files:
|
|
333
349
|
- spec/simmer/util/record_set_spec.rb
|
334
350
|
- spec/simmer/util/record_spec.rb
|
335
351
|
- spec/simmer/util/yaml_reader_spec.rb
|
352
|
+
- spec/simmer_spec.rb
|
353
|
+
- spec/simmer_spec/files/noc_list.csv
|
354
|
+
- spec/simmer_spec/fixtures/agent_fixtures.yaml
|
336
355
|
- spec/spec_helper.rb
|
data/lib/simmer/spoon_mock.rb
DELETED
@@ -1,35 +0,0 @@
|
|
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
|
-
module Simmer
|
11
|
-
# Provides a simple mock for the underlying Pdi::Spoon class.
|
12
|
-
class SpoonMock
|
13
|
-
attr_reader :pass
|
14
|
-
|
15
|
-
def initialize(pass = true)
|
16
|
-
@pass = pass
|
17
|
-
|
18
|
-
freeze
|
19
|
-
end
|
20
|
-
|
21
|
-
def run(*)
|
22
|
-
raise Pdi::Spoon::KitchenError, 'mocked' unless pass
|
23
|
-
|
24
|
-
Pdi::Executor::Result.new(
|
25
|
-
args: [],
|
26
|
-
status: {
|
27
|
-
code: 0,
|
28
|
-
err: 'Some error output from PDI',
|
29
|
-
out: 'Some output from PDI',
|
30
|
-
pid: 123
|
31
|
-
}
|
32
|
-
)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|