package_protections 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27281e8109b1edc0fff691f606a70260be38e130810e088d48e2d73d31424fbb
4
- data.tar.gz: cd99fe90189671daa334be828aaad40b0928742098cfb1430164f5080d3060ca
3
+ metadata.gz: 41803352508cd5d4ede0fe0caf1121516474fe3e95a030e4db7e0f05badfa9a2
4
+ data.tar.gz: bc594511c3b3da35dcd9efcfba0f989928e074e0da429a8f0f538e0537eff5be
5
5
  SHA512:
6
- metadata.gz: b1d22a6dee650ffbe8dd4027d5815b6c104f0825d9d257a40aee08dd44ad4c458f72b502e3ed28caa848f0aa6c468502edb4e4f6c5acd7943b808a8ca43096a3
7
- data.tar.gz: 91ac08c0a0697dad9fbacd7e7d62ab2a22ef058ad074b2d0371b4faf67f9b264aa4b7ed9a597fb09280b44fead58b641aeda165001568ccb7db015dff03d0bc4
6
+ metadata.gz: 352aa3ea22bf0d7cf765023c35ed88c6026fda2b52036aa73fbcf35e4aca1df52df24486ce028a238dcf50db89f2864f4080e77d6da52d9f7be7fa984cd70dc7
7
+ data.tar.gz: 6bb8ab188d27a3656cd6979d00fcaf725165176b17a42704be114ef0bbfbe7150facf21ae44ebc3c1633ce5b21876fd9b8088179c6e702f8addd49f890d7e1e3
@@ -0,0 +1,54 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'fileutils'
5
+
6
+ module ApplicationFixtureHelper
7
+ def write_file(path, content = '')
8
+ pathname = Pathname.new(path)
9
+ FileUtils.mkdir_p(pathname.dirname)
10
+ pathname.write(content)
11
+ path
12
+ end
13
+
14
+ def write_package_yml(
15
+ pack_name,
16
+ dependencies: [],
17
+ enforce_dependencies: true,
18
+ enforce_privacy: true,
19
+ protections: {},
20
+ global_namespaces: [],
21
+ visible_to: []
22
+ )
23
+ defaults = {
24
+ 'prevent_this_package_from_violating_its_stated_dependencies' => 'fail_on_new',
25
+ 'prevent_other_packages_from_using_this_packages_internals' => 'fail_on_new',
26
+ 'prevent_this_package_from_exposing_an_untyped_api' => 'fail_on_new',
27
+ 'prevent_this_package_from_creating_other_namespaces' => 'fail_on_new',
28
+ 'prevent_other_packages_from_using_this_package_without_explicit_visibility' => 'fail_never'
29
+ }
30
+ protections_with_defaults = defaults.merge(protections)
31
+ metadata = { 'protections' => protections_with_defaults }
32
+ if visible_to.any?
33
+ metadata.merge!('visible_to' => visible_to)
34
+ end
35
+
36
+ if global_namespaces.any?
37
+ metadata.merge!('global_namespaces' => global_namespaces)
38
+ end
39
+
40
+ package = ParsePackwerk::Package.new(
41
+ name: pack_name,
42
+ dependencies: dependencies,
43
+ enforce_dependencies: enforce_dependencies,
44
+ enforce_privacy: enforce_privacy,
45
+ metadata: metadata
46
+ )
47
+
48
+ ParsePackwerk.write_package_yml!(package)
49
+ end
50
+
51
+ def delete_app_file(path)
52
+ File.delete(path)
53
+ end
54
+ end
@@ -0,0 +1,99 @@
1
+ def offense(
2
+ package_name, message, file, violation_type
3
+ )
4
+
5
+ package = ParsePackwerk.all.find { |p| p.name == package_name }
6
+ PackageProtections::Offense.new(
7
+ package: package,
8
+ message: message,
9
+ file: file,
10
+ violation_type: violation_type
11
+ )
12
+ end
13
+
14
+ def serialize_offenses_diff(actual_offenses, expected_offense)
15
+ color_by_match = ->(actual, expected) { actual == expected ? Rainbow(actual).green : "#{Rainbow(actual).red} (expected: #{expected})" }
16
+
17
+ actual_offenses.map do |offense|
18
+ # We color each field red or green depending on if the attributes match our expected
19
+ <<~SERIALIZED_OFFENSE
20
+ File: #{color_by_match.call(offense.file, expected_offense.file)}
21
+ Message: #{color_by_match.call(offense.message, expected_offense.message)}
22
+ Violation Type: #{color_by_match.call(offense.violation_type, expected_offense.violation_type)}
23
+ Package: #{color_by_match.call(offense.package.name, expected_offense.package.name)}
24
+ SERIALIZED_OFFENSE
25
+ end
26
+ end
27
+
28
+ def serialize_offenses(actual_offenses)
29
+ actual_offenses.map do |offense|
30
+ <<~SERIALIZED_OFFENSE
31
+ File: #{offense.file}
32
+ Message: #{offense.message}
33
+ Violation Type: #{offense.violation_type}
34
+ Package: #{offense.package.name}
35
+ SERIALIZED_OFFENSE
36
+ end
37
+ end
38
+
39
+ RSpec::Matchers.define(:include_offense) do |expected_offense|
40
+ match do |actual_offenses|
41
+ @actual_offenses = actual_offenses
42
+ @expected_offense = expected_offense
43
+ if ENV['DEBUG']
44
+ PackageProtections.print_offenses(actual_offenses)
45
+ end
46
+ @matching_offense = actual_offenses.find do |actual_offense|
47
+ actual_offense.file == expected_offense.file &&
48
+ actual_offense.message == expected_offense.message &&
49
+ actual_offense.violation_type == expected_offense.violation_type &&
50
+ actual_offense.package.name == expected_offense.package.name
51
+ end
52
+ !@matching_offense.nil?
53
+ end
54
+
55
+ description do
56
+ "to have an offense with type `#{expected_offense.type}` tied to package `#{expected_offense.package_name}` with message `#{expected_offense.message}` and instances `#{expected_offense.submessages.join(', ')}`"
57
+ end
58
+
59
+ failure_message do
60
+ <<~MSG
61
+ Could not find offense! Here are the found offenses:
62
+ #{serialize_offenses_diff(@actual_offenses, expected_offense).join("\n\n")}
63
+ MSG
64
+ end
65
+ end
66
+
67
+ RSpec::Matchers.define(:contain_exactly) do |number_of_offenses|
68
+ match do |actual_offenses|
69
+ @actual_offenses = actual_offenses || []
70
+ @offenses = []
71
+ @actual_offenses.each do |offense|
72
+ @offenses << offense
73
+ end
74
+ @offenses.size == number_of_offenses
75
+ end
76
+
77
+ chain :offense, :number_of_offenses
78
+ chain :offenses, :number_of_offenses
79
+
80
+ description do
81
+ 'to contain offenses'
82
+ end
83
+
84
+ failure_message_when_negated do
85
+ "Found the following offenses:\n#{@offenses.map { |r| "#{r.package_name}: #{r.message}" }}"
86
+ end
87
+
88
+ failure_message do
89
+ if @offenses.empty?
90
+ "Found #{@offenses.size} instead."
91
+ else
92
+ <<~MSG
93
+ Found #{@offenses.size} instead.
94
+
95
+ #{serialize_offenses(@offenses).join("\n")}
96
+ MSG
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Require this file to load code that supports testing using RSpec.
4
+
5
+ require_relative 'application_fixture_helper'
6
+ require_relative 'matchers'
7
+
8
+ def get_resulting_rubocop
9
+ write_file('config/default.yml', <<~YML.strip)
10
+ <%= PackageProtections.rubocop_yml %>
11
+ YML
12
+ YAML.safe_load(ERB.new(File.read('config/default.yml')).result(binding))
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ config.include ApplicationFixtureHelper
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: package_protections
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-30 00:00:00.000000000 Z
11
+ date: 2022-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -186,6 +186,9 @@ files:
186
186
  - lib/package_protections/private/visibility_protection.rb
187
187
  - lib/package_protections/protected_package.rb
188
188
  - lib/package_protections/protection_interface.rb
189
+ - lib/package_protections/rspec/application_fixture_helper.rb
190
+ - lib/package_protections/rspec/matchers.rb
191
+ - lib/package_protections/rspec/support.rb
189
192
  - lib/package_protections/rubocop_protection_interface.rb
190
193
  - lib/package_protections/violation_behavior.rb
191
194
  - lib/rubocop/cop/package_protections/namespaced_under_package_name.rb