lutaml-model 0.8.44 → 0.8.46

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.
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model#88: `unmatched: :raise` on a when_attribute rule fails the
6
+ # parse when an occurrence is claimed by no rule — no discriminator match
7
+ # and no plain sibling on the wire name. The default (:drop) keeps the
8
+ # silent-drop behavior.
9
+ class UnmatchedComponent < Lutaml::Model::Serializable
10
+ attribute :text, :string
11
+
12
+ xml do
13
+ element "component"
14
+ map_element "text", to: :text
15
+ end
16
+ end
17
+
18
+ class UnmatchedRequirement < Lutaml::Model::Serializable
19
+ attribute :guidance, UnmatchedComponent, collection: true
20
+ attribute :purpose, UnmatchedComponent, collection: true
21
+
22
+ xml do
23
+ element "requirement"
24
+ map_element "component", to: :guidance,
25
+ when_attribute: { "type" => "guidance" },
26
+ unmatched: :raise
27
+ map_element "component", to: :purpose,
28
+ when_attribute: { "type" => "purpose" }
29
+ end
30
+ end
31
+
32
+ RSpec.describe "when_attribute unmatched policy" do
33
+ before do
34
+ stub_const("Unmatched::Component", UnmatchedComponent)
35
+ stub_const("Unmatched::Requirement", UnmatchedRequirement)
36
+ end
37
+
38
+ it "parses when every occurrence is covered" do
39
+ req = UnmatchedRequirement.from_xml(<<~XML)
40
+ <requirement>
41
+ <component type="guidance"><text>g1</text></component>
42
+ <component type="purpose"><text>p1</text></component>
43
+ </requirement>
44
+ XML
45
+
46
+ expect(req.guidance.map(&:text)).to eq(["g1"])
47
+ expect(req.purpose.map(&:text)).to eq(["p1"])
48
+ end
49
+
50
+ it "raises on an unknown discriminator value" do
51
+ xml = <<~XML
52
+ <requirement>
53
+ <component type="guidance"><text>g1</text></component>
54
+ <component type="unknown"><text>x</text></component>
55
+ </requirement>
56
+ XML
57
+
58
+ expect { UnmatchedRequirement.from_xml(xml) }
59
+ .to raise_error(Lutaml::Model::UnknownDiscriminatorError, /type="unknown"/)
60
+ end
61
+
62
+ it "raises when the discriminator attribute is missing entirely" do
63
+ xml = "<requirement><component><text>x</text></component></requirement>"
64
+
65
+ expect { UnmatchedRequirement.from_xml(xml) }
66
+ .to raise_error(Lutaml::Model::UnknownDiscriminatorError, /<component>/)
67
+ end
68
+
69
+ it "names the tested attributes of the unclaimed occurrence" do
70
+ xml = '<requirement><component type="later"><text>x</text></component></requirement>'
71
+
72
+ expect { UnmatchedRequirement.from_xml(xml) }
73
+ .to raise_error(Lutaml::Model::UnknownDiscriminatorError, /unmatched: :drop/)
74
+ end
75
+
76
+ it "does not raise when a plain rule claims the leftovers" do
77
+ both = Class.new(Lutaml::Model::Serializable) do
78
+ attribute :special, UnmatchedComponent, collection: true
79
+ attribute :plain, UnmatchedComponent, collection: true
80
+
81
+ xml do
82
+ element "holder"
83
+ map_element "item", to: :special,
84
+ when_attribute: { "kind" => "special" },
85
+ unmatched: :raise
86
+ map_element "item", to: :plain
87
+ end
88
+ end
89
+ stub_const("Unmatched::Holder", both)
90
+
91
+ doc = both.from_xml(
92
+ '<holder><item kind="special"><text>b</text></item><item><text>a</text></item></holder>',
93
+ )
94
+
95
+ expect(doc.special.map(&:text)).to eq(["b"])
96
+ expect(doc.plain.map(&:text)).to eq(["a"])
97
+ end
98
+
99
+ it "drops unmatched occurrences by default" do
100
+ lenient = Class.new(Lutaml::Model::Serializable) do
101
+ attribute :guidance, UnmatchedComponent, collection: true
102
+
103
+ xml do
104
+ element "requirement"
105
+ map_element "component", to: :guidance,
106
+ when_attribute: { "type" => "guidance" }
107
+ end
108
+ end
109
+ stub_const("Unmatched::Lenient", lenient)
110
+
111
+ doc = lenient.from_xml(
112
+ '<requirement><component type="guidance"><text>g</text></component>' \
113
+ '<component type="other"><text>x</text></component></requirement>',
114
+ )
115
+ expect(doc.guidance.map(&:text)).to eq(["g"])
116
+ end
117
+
118
+ it "survives deep_dup of mappings" do
119
+ rule = UnmatchedRequirement.mappings_for(:xml).mappings.find do |r|
120
+ r.to == :guidance
121
+ end
122
+
123
+ expect(rule.unmatched).to eq(:raise)
124
+ duped = rule.deep_dup
125
+ expect(duped.unmatched).to eq(:raise)
126
+ expect(duped.when_attribute).to eq("type" => "guidance")
127
+ end
128
+
129
+ it "rejects unknown policy values" do
130
+ expect do
131
+ Class.new(Lutaml::Model::Serializable) do
132
+ attribute :x, :string
133
+
134
+ xml do
135
+ element "x"
136
+ map_element "x", to: :x,
137
+ when_attribute: { "type" => "a" },
138
+ unmatched: :explode
139
+ end
140
+ end
141
+ end.to raise_error(Lutaml::Model::IncorrectMappingArgumentsError, /unmatched/)
142
+ end
143
+
144
+ it "rejects unmatched without when_attribute" do
145
+ expect do
146
+ Class.new(Lutaml::Model::Serializable) do
147
+ attribute :x, :string
148
+
149
+ xml do
150
+ element "x"
151
+ map_element "x", to: :x, unmatched: :raise
152
+ end
153
+ end
154
+ end.to raise_error(Lutaml::Model::IncorrectMappingArgumentsError, /when_attribute/)
155
+ end
156
+
157
+ it "is rejected on attribute mappings" do
158
+ expect do
159
+ Class.new(Lutaml::Model::Serializable) do
160
+ attribute :x, :string
161
+
162
+ xml do
163
+ element "x"
164
+ map_attribute "x", to: :x, unmatched: :raise
165
+ end
166
+ end
167
+ end.to raise_error(ArgumentError)
168
+ end
169
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.44
4
+ version: 0.8.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -489,6 +489,7 @@ files:
489
489
  - lib/lutaml/model/error/undefined_attribute_error.rb
490
490
  - lib/lutaml/model/error/union_schema_unsupported_error.rb
491
491
  - lib/lutaml/model/error/unknown_adapter_type_error.rb
492
+ - lib/lutaml/model/error/unknown_discriminator_error.rb
492
493
  - lib/lutaml/model/error/unknown_sequence_mapping_error.rb
493
494
  - lib/lutaml/model/error/unknown_type_error.rb
494
495
  - lib/lutaml/model/error/unresolvable_type_error.rb
@@ -1687,6 +1688,7 @@ files:
1687
1688
  - spec/lutaml/key_value/transformation/rule_compiler_spec.rb
1688
1689
  - spec/lutaml/key_value/transformation/value_serializer_spec.rb
1689
1690
  - spec/lutaml/key_value/transformation_spec.rb
1691
+ - spec/lutaml/key_value/when_attribute_spec.rb
1690
1692
  - spec/lutaml/model/absent_value_contracts_spec.rb
1691
1693
  - spec/lutaml/model/adapter_resolver_configured_spec.rb
1692
1694
  - spec/lutaml/model/attribute_apply_value_map_spec.rb
@@ -2008,6 +2010,7 @@ files:
2008
2010
  - spec/lutaml/xml/w3c_types_spec.rb
2009
2011
  - spec/lutaml/xml/when_attribute_ordered_spec.rb
2010
2012
  - spec/lutaml/xml/when_attribute_spec.rb
2013
+ - spec/lutaml/xml/when_attribute_unmatched_spec.rb
2011
2014
  - spec/lutaml/xml/xml_adapter_spec.rb
2012
2015
  - spec/lutaml/xml/xml_declaration_spec.rb
2013
2016
  - spec/lutaml/xml/xml_element_guard_spec.rb