rspec-protobuf 0.4.0 → 0.4.1

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: 4c19a7d0b802fd19e5b78f4ae4398fcffc6c6a1127a5056530bbaaee35f53192
4
- data.tar.gz: 689f283251f4cf3c54266289a38ae49019616739070030ff71edfe69d79a674f
3
+ metadata.gz: e8d21cd1780e94292bac712a3d3245599858e31eb5c75bd3dfa653798c0c1469
4
+ data.tar.gz: 6d01c76cd09476bddb44966292e9a13407e60fb17b8d707d225eff344bc00c5a
5
5
  SHA512:
6
- metadata.gz: 7dbb6a8c87f67f42213a1bd424ccb257c17ebbb4f152a57c969c55705422e55c6e781cfceaa83287fee1286c83593753d9ad3ca5dceb495785ade39c36b37985
7
- data.tar.gz: 5d5e00fa0481969d581125f6b793a695695ea7e6f30db3bc88b5b050de8f16a107d910d58e2ef8e7104baf53ab40583f7002ae84e477042a80618325e5111f6b
6
+ metadata.gz: 1c0cf6543957003dffde24c5887fec8ac59f3dd50bc537434af7ee3cd0abe6f8fa9df8e7c931391a89cc9965447a92478bc7b5a210b19235da7176bd4504de7d
7
+ data.tar.gz: 732cd107abf1fd848641b16f654b55db60b28621fcf8c163065b6590ac33ec5c7afb9ca45e834e08227d6a468e656d91d6c4116f58141aa5d3d741cc3c4e990d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 0.4.1 (2026-11-08)
2
+ - Fix map fields: naming one in `be_a_protobuf` raised `TypeError`, and a hash never matched one.
3
+ - RSpec matchers on repeated and map fields see plain Ruby values with defaults kept, e.g. `be_a_protobuf(messages: include(a_hash_including(msg: "")))`, the same on google-protobuf 3 and 4.
4
+ - Clearer failure output: given a type, the diff shows only the attributes (it showed `[Type, {...}]`); a type mismatch no longer diffs; the negation message names the message rather than a hash; `expect(nil).to be_a_protobuf` says `nil is not a Protobuf`.
5
+ - Fix `NoMethodError` when an expectation names something that isn't a field, such as a oneof or a method like `to_h`; it now fails to match.
6
+
1
7
  ### 0.4.0 (2026-04-19)
2
8
  - ruby version / gem upgrades
3
9
 
data/Gemfile.lock CHANGED
@@ -1,22 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-protobuf (0.4.0)
4
+ rspec-protobuf (0.4.1)
5
5
  google-protobuf (>= 3)
6
6
  rspec-expectations (>= 3)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- bigdecimal (4.1.2)
11
+ bigdecimal (4.1.3)
12
12
  date (3.5.1)
13
13
  debug (1.11.1)
14
14
  irb (~> 1.10)
15
15
  reline (>= 0.3.8)
16
16
  diff-lcs (1.6.2)
17
- docile (1.4.1)
18
- erb (6.0.3)
19
- google-protobuf (4.34.1)
17
+ erb (6.0.4)
18
+ google-protobuf (4.36.2)
20
19
  bigdecimal
21
20
  rake (~> 13.3)
22
21
  io-console (0.8.2)
@@ -52,12 +51,7 @@ GEM
52
51
  diff-lcs (>= 1.2.0, < 2.0)
53
52
  rspec-support (~> 3.13.0)
54
53
  rspec-support (3.13.7)
55
- simplecov (0.22.0)
56
- docile (~> 1.1)
57
- simplecov-html (~> 0.11)
58
- simplecov_json_formatter (~> 0.1)
59
- simplecov-html (0.13.2)
60
- simplecov_json_formatter (0.1.4)
54
+ simplecov (1.3.2)
61
55
  stringio (3.2.0)
62
56
  tsort (0.2.0)
63
57
 
@@ -68,16 +62,16 @@ DEPENDENCIES
68
62
  debug
69
63
  rspec (>= 3)
70
64
  rspec-protobuf!
71
- simplecov
65
+ simplecov (>= 1)
72
66
 
73
67
  CHECKSUMS
74
- bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
68
+ bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
69
+ bundler (4.0.14) sha256=d09a0a965cf772266a7e49e83610be7c2f4e49e61134c42a56804bb383cc24b8
75
70
  date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
76
71
  debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
77
72
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
78
- docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
79
- erb (6.0.3) sha256=e43685a8a0a0ea6a924871b2162e8953ef73147ce46b75b36d1f6774fd286e91
80
- google-protobuf (4.34.1) sha256=347181542b8d659c60f028fa3791c9cccce651a91ad27782dbc5c5e374796cdc
73
+ erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
74
+ google-protobuf (4.36.2) sha256=2f8daff50ae14aad36ab0ccdcf284065fa71d726c444efe114e6db82ffe62494
81
75
  io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
82
76
  irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae
83
77
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
@@ -91,11 +85,9 @@ CHECKSUMS
91
85
  rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
92
86
  rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
93
87
  rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
94
- rspec-protobuf (0.4.0)
88
+ rspec-protobuf (0.4.1)
95
89
  rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
96
- simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
97
- simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
98
- simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
90
+ simplecov (1.3.2) sha256=2a22ec1759f1b8db60881a2e038758956fc69ef93ed669be0bde0678de6d0e85
99
91
  stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
100
92
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
101
93
 
data/README.md CHANGED
@@ -29,11 +29,11 @@ it { expect(result).to include(data: a_protobuf(MyProtoMessage, msg: starting_wi
29
29
  Before
30
30
  ```ruby
31
31
  Failure/Error: is_expected.to have_attributes(date: { month: 2 })
32
- expected <ComplexMessage: complex: false, date: <DateMessage: type: :DATE_DEFAULT, month: 1, day: 0, year: 0>> to have attributes {:date => {:month => 2}} but had attributes {:date => <DateMessage: type: :DATE_DEFAULT, month: 1, day: 0, year: 0>}
32
+ expected <ComplexMessage: date: <DateMessage: month: 1>, numbers: [], messages: []> to have attributes {date: {month: 2}} but had attributes {date: <DateMessage: month: 1>}
33
33
  Diff:
34
34
  @@ -1 +1 @@
35
- -:date => {:month => 2},
36
- +:date => <DateMessage: type: :DATE_DEFAULT, month: 1, day: 0, year: 0>,
35
+ -:date => {month: 2},
36
+ +:date => <DateMessage: month: 1>,
37
37
  ```
38
38
 
39
39
  After
@@ -41,18 +41,20 @@ After
41
41
  Failure/Error: is_expected.to be_a_protobuf(date: { month: 2 })
42
42
  Diff:
43
43
  @@ -1 +1 @@
44
- -:date => {:month => 2},
45
- +:date => {:month => 1},
44
+ -:date => {month: 2},
45
+ +:date => {month: 1},
46
46
  ```
47
47
 
48
48
  ----
49
- ## Contributing
49
+ ## Installation
50
50
 
51
- Yes please :)
51
+ ```ruby
52
+ # Gemfile
53
+ gem "rspec-protobuf"
54
+ ```
55
+
56
+ or
52
57
 
53
- 1. Fork it
54
- 1. Create your feature branch (`git checkout -b my-feature`)
55
- 1. Ensure the tests pass (`bundle exec rspec`)
56
- 1. Commit your changes (`git commit -am 'awesome new feature'`)
57
- 1. Push your branch (`git push origin my-feature`)
58
- 1. Create a Pull Request
58
+ ```
59
+ gem install rspec-protobuf
60
+ ```
@@ -1,29 +1,42 @@
1
1
  module RSpec
2
2
  module Protobuf
3
3
  module Refinements
4
+ # Message#to_h, but keeping default-valued fields, which google-protobuf 4 drops
5
+ def self.to_h_with_defaults(value)
6
+ case value
7
+ when Google::Protobuf::MessageExts
8
+ value.class.descriptor.to_h do |field|
9
+ [ field.name.to_sym, to_h_with_defaults(field.get(value)) ]
10
+ end
11
+ when Google::Protobuf::RepeatedField
12
+ value.map { |x| to_h_with_defaults(x) }
13
+ when Google::Protobuf::Map
14
+ # Map#to_h ignores a block
15
+ res = {}
16
+ value.each { |k, v| res[k] = to_h_with_defaults(v) }
17
+ res
18
+ else
19
+ value
20
+ end
21
+ end
22
+
4
23
  refine Google::Protobuf::MessageExts do
5
24
  def include?(*args)
6
25
  expected_attrs = Hash === args.last ? args.pop : {}
7
26
 
8
27
  # ensure all enumerated keys are indeed attributes
9
- return false unless args.all? { |attr| respond_to?(attr) }
10
-
11
- # ensure all enumerated attributes are present
12
- return false unless expected_attrs.keys.all? { |attr| respond_to?(attr) }
13
-
14
- fields = Hash[
15
- self.class.descriptor.map { |f| [ f.name.to_sym, f ] }
16
- ]
28
+ return false unless args.all? { |attr| field_for(attr) }
17
29
 
18
30
  # ensure that expected attribute matches
19
- expected_attrs.all? do |expected_attr, expected_value|
20
- field_matches?(fields[expected_attr], expected_value)
31
+ expected_attrs.all? do |attr, expected|
32
+ field = field_for(attr)
33
+ field && field_matches?(field, expected)
21
34
  end
22
35
  end
23
36
 
24
37
  def matches?(**attrs)
25
38
  # ensure all enumerated attributes are present
26
- return false unless attrs.keys.all? { |attr| respond_to?(attr) }
39
+ return false unless attrs.keys.all? { |attr| field_for(attr) }
27
40
 
28
41
  # ensure that each field matches
29
42
  self.class.descriptor.all? do |field|
@@ -31,21 +44,16 @@ module RSpec
31
44
  end
32
45
  end
33
46
 
34
- def normalized_hash(include_defaults: false)
47
+ def normalized_hash
35
48
  res = {}
36
49
 
37
50
  self.class.descriptor.each do |field|
38
51
  value = field.get(self)
39
52
 
40
- if value.is_a?(Google::Protobuf::MessageExts)
41
- # recursively serialize sub-message
42
- value = value.normalized_hash(include_defaults:)
43
- elsif include_defaults && field.label == :repeated && field.type == :message
44
- value = value.map { |v| v.normalized_hash(include_defaults:) }
45
- end
53
+ # recursively serialize sub-message
54
+ value = value.normalized_hash if value.is_a?(Google::Protobuf::MessageExts)
46
55
 
47
- default = field.label == :repeated ? [] : field.default
48
- res[field.name.to_sym] = value if include_defaults || default != value
56
+ res[field.name.to_sym] = value unless default_value?(field, value)
49
57
  end
50
58
 
51
59
  res
@@ -53,13 +61,18 @@ module RSpec
53
61
 
54
62
  private
55
63
 
64
+ # nil unless attr is a Symbol naming a field, not eg. a oneof or method
65
+ def field_for(attr)
66
+ self.class.descriptor.lookup(attr.to_s) if attr.is_a?(Symbol)
67
+ end
68
+
56
69
  def field_matches?(field, expected)
57
70
  actual = field.get(self)
58
71
 
59
- if expected.nil?
60
- # mimic protobuf behavior and convert to default value
61
- expected = field.label == :repeated ? [] : field.default
62
- elsif expected.is_a?(Symbol) && field.type == :string
72
+ # mimic protobuf behavior, where nil means the default value
73
+ return default_value?(field, actual) if expected.nil?
74
+
75
+ if expected.is_a?(Symbol) && field.type == :string
63
76
  # convert symbols to strings
64
77
  expected = expected.to_s
65
78
  end
@@ -80,19 +93,23 @@ module RSpec
80
93
  end
81
94
  end
82
95
 
96
+ def default_value?(field, value)
97
+ field.label == :repeated ? value.length.zero? : value == field.default
98
+ end
99
+
83
100
  def values_match?(actual, expected)
84
- expected === actual || if actual.is_a?(Google::Protobuf::MessageExts)
85
- case expected
86
- when Google::Protobuf::MessageExts
87
- # expected === actual
88
- when Hash
89
- # recurse
90
- actual.matches?(**expected)
91
- else
92
- # eg. RSpec matchers
93
- expected === actual.normalized_hash(include_defaults: true)
94
- end
101
+ return true if expected === actual
102
+
103
+ case actual
104
+ when Google::Protobuf::MessageExts
105
+ return actual.matches?(**expected) if expected.is_a?(Hash)
106
+ when Google::Protobuf::RepeatedField, Google::Protobuf::Map
107
+ else
108
+ return false
95
109
  end
110
+
111
+ # eg. RSpec matchers, which expect plain Ruby values
112
+ expected === Refinements.to_h_with_defaults(actual)
96
113
  end
97
114
  end
98
115
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module Protobuf
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  end
@@ -12,7 +12,7 @@ RSpec::Matchers.define :be_a_protobuf do |type = nil, **attrs|
12
12
  raise TypeError, "Expected arg to be a Google::Protobuf::MessageExts, found: #{type}"
13
13
  end
14
14
 
15
- @fail_msg = "#{actual} is not a Protobuf"
15
+ @fail_msg = "#{actual.inspect} is not a Protobuf"
16
16
  return false unless actual.is_a?(Google::Protobuf::MessageExts)
17
17
 
18
18
  # match expected message type
@@ -30,16 +30,22 @@ RSpec::Matchers.define :be_a_protobuf do |type = nil, **attrs|
30
30
  end
31
31
  end
32
32
 
33
+ return true if actual.include?(attrs)
34
+
35
+ # let the diff explain: only referenced attrs, defaults removed
33
36
  @fail_msg = ""
37
+ @diff_expected = attrs
38
+ @actual = actual.normalized_hash.slice(*attrs.keys)
39
+ false
40
+ end
34
41
 
35
- # customize differ output by removing unreferenced and default attrs
36
- @actual = actual.normalized_hash.slice(*attrs.keys).reject do |k, v|
37
- field = actual.class.descriptor.lookup(k.to_s)
38
- default = field.label == :repeated ? [] : field.default
39
- v == default
40
- end
42
+ # only an attribute mismatch has a useful diff
43
+ def diffable?
44
+ !@diff_expected.nil?
45
+ end
41
46
 
42
- actual.include?(attrs)
47
+ def expected
48
+ @diff_expected || super
43
49
  end
44
50
 
45
51
  description do
@@ -48,7 +54,11 @@ RSpec::Matchers.define :be_a_protobuf do |type = nil, **attrs|
48
54
 
49
55
  failure_message { @fail_msg }
50
56
 
51
- diffable
57
+ failure_message_when_negated do |actual|
58
+ msg = "expected #{actual} not to be #{description}"
59
+ msg += " with #{attrs}" unless attrs.empty?
60
+ msg
61
+ end
52
62
  end
53
63
 
54
64
  RSpec::Matchers.alias_matcher :a_protobuf, :be_a_protobuf
@@ -1,18 +1,20 @@
1
- package_name = File.basename(__FILE__).split(".")[0]
2
- load Dir.glob("lib/**/version.rb")[0]
3
-
4
- package = RSpec::Protobuf
5
-
1
+ require_relative "lib/rspec/protobuf/version"
6
2
 
7
3
  Gem::Specification.new do |s|
8
- s.name = package_name
9
- s.version = package.const_get "VERSION"
4
+ s.name = "rspec-protobuf"
5
+ s.version = RSpec::Protobuf::VERSION
10
6
  s.authors = ["Daniel Pepper"]
11
- s.summary = package.to_s
7
+ s.summary = "RSpec::Protobuf"
12
8
  s.description = "RSpec matchers for Protobuf"
13
- s.homepage = "https://github.com/dpep/#{package_name}"
9
+ s.homepage = "https://github.com/dpep/rspec-protobuf"
14
10
  s.license = "MIT"
15
- s.files = `git ls-files * ':!:spec'`.split("\n")
11
+ s.files = `git ls-files * ':!:spec' ':!:gemfiles'`.split("\n")
12
+
13
+ s.metadata = {
14
+ "changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
15
+ "rubygems_mfa_required" => "true",
16
+ "source_code_uri" => s.homepage,
17
+ }
16
18
 
17
19
  s.required_ruby_version = ">= 3.3"
18
20
 
@@ -21,5 +23,5 @@ Gem::Specification.new do |s|
21
23
 
22
24
  s.add_development_dependency "debug"
23
25
  s.add_development_dependency "rspec", ">= 3"
24
- s.add_development_dependency "simplecov"
26
+ s.add_development_dependency "simplecov", ">= 1"
25
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-protobuf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Pepper
@@ -71,14 +71,14 @@ dependencies:
71
71
  requirements:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: '0'
74
+ version: '1'
75
75
  type: :development
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '0'
81
+ version: '1'
82
82
  description: RSpec matchers for Protobuf
83
83
  executables: []
84
84
  extensions: []
@@ -97,7 +97,10 @@ files:
97
97
  homepage: https://github.com/dpep/rspec-protobuf
98
98
  licenses:
99
99
  - MIT
100
- metadata: {}
100
+ metadata:
101
+ changelog_uri: https://github.com/dpep/rspec-protobuf/blob/main/CHANGELOG.md
102
+ rubygems_mfa_required: 'true'
103
+ source_code_uri: https://github.com/dpep/rspec-protobuf
101
104
  rdoc_options: []
102
105
  require_paths:
103
106
  - lib
@@ -114,5 +117,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
117
  requirements: []
115
118
  rubygems_version: 3.6.9
116
119
  specification_version: 4
117
- summary: Gem::Specification::RSpec::Protobuf
120
+ summary: RSpec::Protobuf
118
121
  test_files: []