fog-aws 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -163,10 +163,13 @@ module Fog
163
163
  # expect eventual consistency
164
164
  if (tags = server.tags) && tags.size > 0
165
165
  Fog.wait_for { server.reload rescue nil }
166
- service.create_tags(
167
- server.identity,
168
- tags
169
- )
166
+ Fog.wait_for {
167
+ begin
168
+ service.create_tags(server.identity, tags)
169
+ rescue Fog::Compute::AWS::NotFound
170
+ false
171
+ end
172
+ }
170
173
  end
171
174
  server
172
175
  end
@@ -7,6 +7,8 @@ module Fog
7
7
  attribute :owner_id, :aliases => 'OwnerId'
8
8
  attribute :creation_token, :aliases => 'CreationToken'
9
9
  attribute :performance_mode, :aliases => 'PerformanceMode'
10
+ attribute :encrypted, :aliases => 'Encrypted'
11
+ attribute :kms_key_id, :aliases => 'KmsKeyId'
10
12
  attribute :creation_time, :aliases => 'CreationTime'
11
13
  attribute :state, :aliases => 'LifeCycleState'
12
14
  attribute :name, :aliases => 'Name'
@@ -33,6 +35,8 @@ module Fog
33
35
  def save
34
36
  params = {}
35
37
  params.merge!(:performance_mode => self.performance_mode) if self.performance_mode
38
+ params.merge!(:encrypted => self.encrypted) if self.encrypted
39
+ params.merge!(:kms_key_id => self.kms_key_id) if self.kms_key_id
36
40
 
37
41
  merge_attributes(service.create_file_system(self.creation_token || Fog::Mock.random_hex(32), params).body)
38
42
  end
@@ -4,40 +4,41 @@ module Fog
4
4
  class Server < Fog::Model
5
5
  identity :id, :aliases => 'DBInstanceIdentifier'
6
6
 
7
- attribute :allocated_storage, :aliases => 'AllocatedStorage', :type => :integer
8
- attribute :auto_minor_version_upgrade, :aliases => 'AutoMinorVersionUpgrade'
9
- attribute :availability_zone, :aliases => 'AvailabilityZone'
10
- attribute :backup_retention_period, :aliases => 'BackupRetentionPeriod', :type => :integer
11
- attribute :ca_certificate_id, :aliases => 'CACertificateIdentifier'
12
- attribute :character_set_name, :aliases => 'CharacterSetName'
13
- attribute :cluster_id, :aliases => 'DBClusterIdentifier'
14
- attribute :created_at, :aliases => 'InstanceCreateTime', :type => :time
15
- attribute :db_name, :aliases => 'DBName'
16
- attribute :db_parameter_groups, :aliases => 'DBParameterGroups'
17
- attribute :db_security_groups, :aliases => 'DBSecurityGroups', :type => :array
18
- attribute :db_subnet_group_name, :aliases => 'DBSubnetGroupName'
19
- attribute :dbi_resource_id, :aliases => 'DbiResourceId'
20
- attribute :endpoint, :aliases => 'Endpoint'
21
- attribute :engine, :aliases => 'Engine'
22
- attribute :engine_version, :aliases => 'EngineVersion'
23
- attribute :flavor_id, :aliases => 'DBInstanceClass'
24
- attribute :iops, :aliases => 'Iops', :type => :integer
25
- attribute :kms_key_id, :aliases => 'KmsKeyId'
26
- attribute :last_restorable_time, :aliases => 'LatestRestorableTime', :type => :time
27
- attribute :license_model, :aliases => 'LicenseModel'
28
- attribute :master_username, :aliases => 'MasterUsername'
29
- attribute :multi_az, :aliases => 'MultiAZ', :type => :boolean
30
- attribute :pending_modified_values, :aliases => 'PendingModifiedValues'
31
- attribute :preferred_backup_window, :aliases => 'PreferredBackupWindow'
32
- attribute :preferred_maintenance_window, :aliases => 'PreferredMaintenanceWindow'
33
- attribute :publicly_accessible, :aliases => 'PubliclyAccessible'
34
- attribute :read_replica_identifiers, :aliases => 'ReadReplicaDBInstanceIdentifiers', :type => :array
35
- attribute :read_replica_source, :aliases => 'ReadReplicaSourceDBInstanceIdentifier'
36
- attribute :state, :aliases => 'DBInstanceStatus'
37
- attribute :storage_encrypted, :aliases => 'StorageEncrypted', :type => :boolean
38
- attribute :storage_type, :aliases => 'StorageType'
39
- attribute :tde_credential_arn, :aliases => 'TdeCredentialArn'
40
- attribute :vpc_security_groups, :aliases => 'VpcSecurityGroups', :type => :array
7
+ attribute :allocated_storage, :aliases => 'AllocatedStorage', :type => :integer
8
+ attribute :auto_minor_version_upgrade, :aliases => 'AutoMinorVersionUpgrade'
9
+ attribute :availability_zone, :aliases => 'AvailabilityZone'
10
+ attribute :backup_retention_period, :aliases => 'BackupRetentionPeriod', :type => :integer
11
+ attribute :ca_certificate_id, :aliases => 'CACertificateIdentifier'
12
+ attribute :character_set_name, :aliases => 'CharacterSetName'
13
+ attribute :cluster_id, :aliases => 'DBClusterIdentifier'
14
+ attribute :created_at, :aliases => 'InstanceCreateTime', :type => :time
15
+ attribute :db_name, :aliases => 'DBName'
16
+ attribute :db_parameter_groups, :aliases => 'DBParameterGroups'
17
+ attribute :db_security_groups, :aliases => 'DBSecurityGroups', :type => :array
18
+ attribute :db_subnet_group_name, :aliases => 'DBSubnetGroupName'
19
+ attribute :dbi_resource_id, :aliases => 'DbiResourceId'
20
+ attribute :enable_iam_database_authentication, :aliases => 'EnableIAMDatabaseAuthentication', :type => :boolean
21
+ attribute :endpoint, :aliases => 'Endpoint'
22
+ attribute :engine, :aliases => 'Engine'
23
+ attribute :engine_version, :aliases => 'EngineVersion'
24
+ attribute :flavor_id, :aliases => 'DBInstanceClass'
25
+ attribute :iops, :aliases => 'Iops', :type => :integer
26
+ attribute :kms_key_id, :aliases => 'KmsKeyId'
27
+ attribute :last_restorable_time, :aliases => 'LatestRestorableTime', :type => :time
28
+ attribute :license_model, :aliases => 'LicenseModel'
29
+ attribute :master_username, :aliases => 'MasterUsername'
30
+ attribute :multi_az, :aliases => 'MultiAZ', :type => :boolean
31
+ attribute :pending_modified_values, :aliases => 'PendingModifiedValues'
32
+ attribute :preferred_backup_window, :aliases => 'PreferredBackupWindow'
33
+ attribute :preferred_maintenance_window, :aliases => 'PreferredMaintenanceWindow'
34
+ attribute :publicly_accessible, :aliases => 'PubliclyAccessible'
35
+ attribute :read_replica_identifiers, :aliases => 'ReadReplicaDBInstanceIdentifiers', :type => :array
36
+ attribute :read_replica_source, :aliases => 'ReadReplicaSourceDBInstanceIdentifier'
37
+ attribute :state, :aliases => 'DBInstanceStatus'
38
+ attribute :storage_encrypted, :aliases => 'StorageEncrypted', :type => :boolean
39
+ attribute :storage_type, :aliases => 'StorageType'
40
+ attribute :tde_credential_arn, :aliases => 'TdeCredentialArn'
41
+ attribute :vpc_security_groups, :aliases => 'VpcSecurityGroups', :type => :array
41
42
 
42
43
  attr_accessor :password, :parameter_group_name, :security_group_names, :port, :source_snapshot_id
43
44
 
@@ -136,32 +137,33 @@ module Fog
136
137
  # Converts attributes to a parameter hash suitable for requests
137
138
  def attributes_to_params
138
139
  options = {
139
- 'AllocatedStorage' => allocated_storage,
140
- 'AutoMinorVersionUpgrade' => auto_minor_version_upgrade,
141
- 'AvailabilityZone' => availability_zone,
142
- 'BackupRetentionPeriod' => backup_retention_period,
143
- 'DBClusterIdentifier' => cluster_id,
144
- 'DBInstanceClass' => flavor_id,
145
- 'DBInstanceIdentifier' => id,
146
- 'DBName' => db_name,
147
- 'DBParameterGroupName' => parameter_group_name || attributes['DBParameterGroupName'],
148
- 'DBSecurityGroups' => security_group_names,
149
- 'DBSubnetGroupName' => db_subnet_group_name,
150
- 'Engine' => engine,
151
- 'EngineVersion' => engine_version,
152
- 'Iops' => iops,
153
- 'KmsKeyId' => kms_key_id,
154
- 'LicenseModel' => license_model,
155
- 'MasterUserPassword' => password || attributes['MasterUserPassword'],
156
- 'MasterUsername' => master_username,
157
- 'MultiAZ' => multi_az,
158
- 'Port' => port || attributes['Port'],
159
- 'PreferredBackupWindow' => preferred_backup_window,
160
- 'PreferredMaintenanceWindow' => preferred_maintenance_window,
161
- 'PubliclyAccessible' => publicly_accessible,
162
- 'StorageEncrypted' => storage_encrypted,
163
- 'StorageType' => storage_type,
164
- 'VpcSecurityGroups' => vpc_security_groups,
140
+ 'AllocatedStorage' => allocated_storage,
141
+ 'AutoMinorVersionUpgrade' => auto_minor_version_upgrade,
142
+ 'AvailabilityZone' => availability_zone,
143
+ 'BackupRetentionPeriod' => backup_retention_period,
144
+ 'DBClusterIdentifier' => cluster_id,
145
+ 'DBInstanceClass' => flavor_id,
146
+ 'DBInstanceIdentifier' => id,
147
+ 'DBName' => db_name,
148
+ 'DBParameterGroupName' => parameter_group_name || attributes['DBParameterGroupName'],
149
+ 'DBSecurityGroups' => security_group_names,
150
+ 'DBSubnetGroupName' => db_subnet_group_name,
151
+ 'EnableIAMDatabaseAuthentication' => enable_iam_database_authentication,
152
+ 'Engine' => engine,
153
+ 'EngineVersion' => engine_version,
154
+ 'Iops' => iops,
155
+ 'KmsKeyId' => kms_key_id,
156
+ 'LicenseModel' => license_model,
157
+ 'MasterUserPassword' => password || attributes['MasterUserPassword'],
158
+ 'MasterUsername' => master_username,
159
+ 'MultiAZ' => multi_az,
160
+ 'Port' => port || attributes['Port'],
161
+ 'PreferredBackupWindow' => preferred_backup_window,
162
+ 'PreferredMaintenanceWindow' => preferred_maintenance_window,
163
+ 'PubliclyAccessible' => publicly_accessible,
164
+ 'StorageEncrypted' => storage_encrypted,
165
+ 'StorageType' => storage_type,
166
+ 'VpcSecurityGroups' => vpc_security_groups,
165
167
  }
166
168
 
167
169
  options.delete_if {|key, value| value.nil?}
@@ -51,7 +51,8 @@ module Fog
51
51
  'CharacterSetName', 'DbiResourceId', 'LicenseModel', 'KmsKeyId',
52
52
  'DBClusterIdentifier'
53
53
  @db_instance[name] = value
54
- when 'MultiAZ', 'AutoMinorVersionUpgrade', 'PubliclyAccessible', 'StorageEncrypted'
54
+ when 'MultiAZ', 'AutoMinorVersionUpgrade', 'PubliclyAccessible',
55
+ 'StorageEncrypted', 'EnableIAMDatabaseAuthentication'
55
56
  @db_instance[name] = (value == 'true')
56
57
  when 'DBParameterGroups'
57
58
  @in_db_parameter_groups = false
@@ -0,0 +1,67 @@
1
+ module Fog
2
+ module AWS
3
+ class AutoScaling
4
+ class Real
5
+ require 'fog/aws/parsers/auto_scaling/basic'
6
+
7
+ # Sets or removes scale in instance protection from one or more instances from the specified
8
+ # Auto Scaling group.
9
+ #
10
+ # cli equiv:
11
+ # `aws autoscaling set-instance-protection --instance-ids i-5f2e8a0d --auto-scaling-group-name my-asg --protected-from-scale-in`
12
+ #
13
+ # ==== Parameters
14
+ #
15
+ # * AutoScalingGroupName<~String> - The name of the Auto Scaling group``
16
+ # * 'InstanceIds'<~Array> - The list of Auto Scaling instances to set or remove protection on.
17
+ # * 'ProtectedFromScaleIn'<~Boolean> - Protection state
18
+ #
19
+ # ==== See Also
20
+ #
21
+ # https://docs.aws.amazon.com/autoscaling/latest/APIReference/API_SetInstanceProtection.html
22
+
23
+ ExpectedOptions[:asg_name] = %w[AutoScalingGroupName]
24
+ ExpectedOptions[:instance_ids] = %w[InstanceIds]
25
+ ExpectedOptions[:protected_from_scale_in] = %w[ProtectedFromScaleIn]
26
+
27
+ def set_instance_protection(auto_scaling_group_name, options = {})
28
+ if instance_ids = options.delete('InstanceIds')
29
+ options.merge!(AWS.indexed_param('InstanceIds.member.%d', [*instance_ids]))
30
+ end
31
+ protected_from_scale_in = options.delete('ProtectedFromScaleIn')
32
+
33
+ request({
34
+ 'Action' => 'SetInstanceProtection',
35
+ 'AutoScalingGroupName' => auto_scaling_group_name,
36
+ 'ProtectedFromScaleIn' => protected_from_scale_in,
37
+ :parser => Fog::Parsers::AWS::AutoScaling::Basic.new
38
+ }.merge!(options))
39
+ end
40
+ end
41
+
42
+ class Mock
43
+ def set_instance_protection(auto_scaling_group_name, options = {})
44
+ unexpected_options = options.keys - \
45
+ ExpectedOptions[:asg_name] - \
46
+ ExpectedOptions[:instance_ids] - \
47
+ ExpectedOptions[:protected_from_scale_in]
48
+
49
+ unless unexpected_options.empty?
50
+ raise Fog::AWS::AutoScaling::ValidationError.new("Options #{unexpected_options.join(',')} should not be included in request")
51
+ end
52
+
53
+ unless self.data[:auto_scaling_groups].key?(auto_scaling_group_name)
54
+ raise Fog::AWS::AutoScaling::ValidationError.new('AutoScalingGroup name not found - null')
55
+ end
56
+
57
+ response = Excon::Response.new
58
+ response.status = 200
59
+ response.body = {
60
+ 'ResponseMetadata' => { 'RequestId' => Fog::AWS::Mock.request_id }
61
+ }
62
+ response
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -28,14 +28,13 @@ module Fog
28
28
  }
29
29
  end
30
30
 
31
- # See https://forums.aws.amazon.com/message.jspa?messageID=612414
32
- # Note: Oddly, to update this list, I have had to revert to going to
33
- # dev console at https://console.aws.amazon.com/route53/home, then using
34
- # search all sources for Z14GRHDCWA56QT to find this in a HashMap
31
+ # See https://docs.aws.amazon.com/general/latest/gr/rande.html#elb_region
32
+ # This needs to be kept in sync manually sadly for now as seemingly this data is not available via an API
35
33
  def self.elb_dualstack_hosted_zone_mapping
36
34
  @elb_dualstack_hosted_zone_mapping ||= {
37
35
  "ap-northeast-1" => "Z14GRHDCWA56QT",
38
36
  "ap-northeast-2" => "ZWKZPGTI48KDX",
37
+ "ap-northeast-3" => "Z5LXEXXYW11ES",
39
38
  "ap-south-1" => "ZP97RAFLXTNZK",
40
39
  "ap-southeast-1" => "Z1LMS91P8CMLE5",
41
40
  "ap-southeast-2" => "Z1GM3OXH4ZPM65",
@@ -43,6 +42,7 @@ module Fog
43
42
  "eu-central-1" => "Z215JYRZR1TBD5",
44
43
  "eu-west-1" => "Z32O12XQLNTSW2",
45
44
  "eu-west-2" => "ZHURV8PSTC4K8",
45
+ "eu-west-3" => "Z3Q77PNBQS71R4",
46
46
  "us-east-1" => "Z35SXDOTRQ7X7K",
47
47
  "us-east-2" => "Z3AADJGX6KTTL2",
48
48
  "us-west-1" => "Z368ELLRRE2KJ0",
@@ -7,17 +7,27 @@ module Fog
7
7
  # ==== Parameters
8
8
  # * CreationToken <~String> - String of up to 64 ASCII characters. Amazon EFS uses this to ensure idempotent creation.
9
9
  # * PerformanceMode <~String> - (Optional) The PerformanceMode of the file system. We recommend generalPurpose performance mode for most file systems. File systems using the maxIO performance mode can scale to higher levels of aggregate throughput and operations per second with a tradeoff of slightly higher latencies for most file operations. This can't be changed after the file system has been created.
10
+ # * Encrypted <~Boolean> - (Optional) A Boolean value that, if true, creates an encrypted file system. When creating an encrypted file system, you have the option of specifying a CreateFileSystem:KmsKeyId for an existing AWS Key Management Service (AWS KMS) customer master key (CMK). If you don't specify a CMK, then the default CMK for Amazon EFS, /aws/elasticfilesystem, is used to protect the encrypted file system.
11
+ # * KmsKeyId <~String> - (Optional) The ID of the AWS KMS CMK to be used to protect the encrypted file system. This parameter is only required if you want to use a non-default CMK. If this parameter is not specified, the default CMK for Amazon EFS is used. This ID can be in one of the following formats:
12
+ # - Key ID - A unique identifier of the key, for example, 1234abcd-12ab-34cd-56ef-1234567890ab.
13
+ # - ARN - An Amazon Resource Name (ARN) for the key, for example, arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab.
14
+ # - Key alias - A previously created display name for a key. For example, alias/projectKey1.
15
+ # - Key alias ARN - An ARN for a key alias, for example, arn:aws:kms:us-west-2:444455556666:alias/projectKey1.
16
+ # If KmsKeyId is specified, the CreateFileSystem:Encrypted parameter must be set to true.
10
17
  # ==== Returns
11
18
  # * response<~Excon::Response>
12
19
  # * body<~Hash>
13
20
  def create_file_system(creation_token, options={})
14
- request({
21
+ params = {
15
22
  :path => "file-systems",
16
23
  :method => 'POST',
17
24
  :expects => 201,
18
25
  'CreationToken' => creation_token,
19
- 'PerformanceMode' => options[:peformance_mode] || 'generalPurpose'
20
- })
26
+ 'PerformanceMode' => options[:peformance_mode] || 'generalPurpose',
27
+ 'Encrypted' => options[:encrypted] || false
28
+ }
29
+ params[:kms_key_id] = options[:kms_key_id] if options.key?(:kms_key_id)
30
+ request(params)
21
31
  end
22
32
  end
23
33
 
@@ -30,6 +40,7 @@ module Fog
30
40
  "OwnerId" => Fog::AWS::Mock.owner_id,
31
41
  "CreationToken" => creation_token,
32
42
  "PerformanceMode" => options[:performance_mode] || "generalPurpose",
43
+ "Encrypted" => options[:encrypted] || false,
33
44
  "FileSystemId" => id,
34
45
  "CreationTime" => Time.now.to_i.to_f,
35
46
  "LifeCycleState" => "creating",
@@ -39,6 +50,7 @@ module Fog
39
50
  "Timestamp" => Time.now.to_i.to_f
40
51
  }
41
52
  }
53
+ file_system[:kms_key_id] = options[:kms_key_id] if options.key?(:kms_key_id)
42
54
 
43
55
  self.data[:file_systems][id] = file_system
44
56
  response.body = file_system
@@ -221,8 +221,8 @@ module Fog
221
221
  case region.to_s
222
222
  when DEFAULT_REGION, ''
223
223
  's3.amazonaws.com'
224
- when 'cn-north-1'
225
- 's3.cn-north-1.amazonaws.com.cn'
224
+ when %r{\Acn-.*}
225
+ "s3.#{region}.amazonaws.com.cn"
226
226
  else
227
227
  "s3-#{region}.amazonaws.com"
228
228
  end
@@ -316,7 +316,8 @@ module Fog
316
316
  def params_to_url(params)
317
317
  query = params[:query] && params[:query].map do |key, value|
318
318
  if value
319
- [key, escape(value.to_s)].join('=')
319
+ # URL parameters need / to be escaped
320
+ [key, Fog::AWS.escape(value.to_s)].join('=')
320
321
  else
321
322
  key
322
323
  end
@@ -1,5 +1,5 @@
1
1
  module Fog
2
2
  module AWS
3
- VERSION = "3.0.0"
3
+ VERSION = "3.1.0"
4
4
  end
5
5
  end
@@ -0,0 +1,17 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 60
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: 7
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - pinned
8
+ - security
9
+ # Label to use when marking an issue as stale
10
+ staleLabel: wontfix
11
+ # Comment to post when marking an issue as stale. Set to `false` to disable
12
+ markComment: >
13
+ This issue has been automatically marked stale due to inactivity.
14
+ It will be closed if no further activity occurs.
15
+ Thank you for your contributions.
16
+ # Comment to post when closing a stale issue. Set to `false` to disable
17
+ closeComment: false
@@ -51,6 +51,14 @@ Shindo.tests('AWS::AutoScaling | auto_scaling_tests', ['aws', 'auto_scaling']) d
51
51
  tests("#attach_instances").formats(AWS::AutoScaling::Formats::BASIC) do
52
52
  Fog::AWS[:auto_scaling].attach_instances(@asg_name, 'InstanceIds' => 'i-deadbeef').body
53
53
  end
54
+
55
+ tests("#set_instance_protection").formats(AWS::AutoScaling::Formats::BASIC) do
56
+ Fog::AWS[:auto_scaling].set_instance_protection(
57
+ @asg_name,
58
+ 'InstanceIds' => 'i-deadbeef',
59
+ 'ProtectedFromScaleIn' => true
60
+ ).body
61
+ end
54
62
  end
55
63
 
56
64
  tests("#describe_auto_scaling_groups").formats(AWS::AutoScaling::Formats::DESCRIBE_AUTO_SCALING_GROUPS) do
@@ -10,6 +10,8 @@ class AWS
10
10
  "NumberOfMountTargets" => Integer,
11
11
  "OwnerId" => String,
12
12
  "PerformanceMode" => String,
13
+ "Encrypted" => Fog::Nullable::Boolean,
14
+ "KmsKeyId" => Fog::Nullable::String,
13
15
  "SizeInBytes" => {
14
16
  "Timestamp" => Fog::Nullable::Float,
15
17
  "Value" => Integer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lane
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-04-23 00:00:00.000000000 Z
12
+ date: 2018-09-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -773,6 +773,7 @@ files:
773
773
  - lib/fog/aws/requests/auto_scaling/resume_processes.rb
774
774
  - lib/fog/aws/requests/auto_scaling/set_desired_capacity.rb
775
775
  - lib/fog/aws/requests/auto_scaling/set_instance_health.rb
776
+ - lib/fog/aws/requests/auto_scaling/set_instance_protection.rb
776
777
  - lib/fog/aws/requests/auto_scaling/suspend_processes.rb
777
778
  - lib/fog/aws/requests/auto_scaling/terminate_instance_in_auto_scaling_group.rb
778
779
  - lib/fog/aws/requests/auto_scaling/update_auto_scaling_group.rb
@@ -1424,6 +1425,7 @@ files:
1424
1425
  - lib/fog/aws/sts.rb
1425
1426
  - lib/fog/aws/support.rb
1426
1427
  - lib/fog/aws/version.rb
1428
+ - stale.yml
1427
1429
  - tests/credentials_tests.rb
1428
1430
  - tests/helper.rb
1429
1431
  - tests/helpers/collection_helper.rb