package_protections 1.1.1 → 1.2.0

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 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