@@ -222,4 +222,120 @@ def inspect: () -> String
222222 expect ( alignment ) . to be_an ( Array )
223223 end
224224 end
225+
226+ describe "edge cases in alignment" do
227+ context "with duplicate signatures" do
228+ let ( :template_source ) do
229+ <<~RBS
230+ class Foo
231+ end
232+ class Foo
233+ end
234+ RBS
235+ end
236+ let ( :dest_source ) do
237+ <<~RBS
238+ class Foo
239+ end
240+ RBS
241+ end
242+ let ( :template_analysis ) { Rbs ::Merge ::FileAnalysis . new ( template_source ) }
243+ let ( :dest_analysis ) { Rbs ::Merge ::FileAnalysis . new ( dest_source ) }
244+
245+ it "pairs only matching indices (second template remains unmatched)" do
246+ aligner = described_class . new ( template_analysis , dest_analysis )
247+ alignment = aligner . align
248+
249+ matches = alignment . select { |e | e [ :type ] == :match }
250+ template_only = alignment . select { |e | e [ :type ] == :template_only }
251+
252+ # One match, one template_only
253+ expect ( matches . size ) . to eq ( 1 )
254+ expect ( template_only . size ) . to eq ( 1 )
255+ end
256+ end
257+
258+ context "with more dest matches than template" do
259+ let ( :template_source ) do
260+ <<~RBS
261+ class Foo
262+ end
263+ RBS
264+ end
265+ let ( :dest_source ) do
266+ <<~RBS
267+ class Foo
268+ end
269+ class Foo
270+ end
271+ RBS
272+ end
273+ let ( :template_analysis ) { Rbs ::Merge ::FileAnalysis . new ( template_source ) }
274+ let ( :dest_analysis ) { Rbs ::Merge ::FileAnalysis . new ( dest_source ) }
275+
276+ it "pairs first dest with template, second dest remains unmatched" do
277+ aligner = described_class . new ( template_analysis , dest_analysis )
278+ alignment = aligner . align
279+
280+ matches = alignment . select { |e | e [ :type ] == :match }
281+ dest_only = alignment . select { |e | e [ :type ] == :dest_only }
282+
283+ # One match, one dest_only (zip produces nil for missing element)
284+ expect ( matches . size ) . to eq ( 1 )
285+ expect ( dest_only . size ) . to eq ( 1 )
286+ end
287+ end
288+
289+ context "with nil signature" do
290+ let ( :template_source ) { "class Foo\n end" }
291+ let ( :dest_source ) { "class Foo\n end" }
292+ let ( :template_analysis ) { Rbs ::Merge ::FileAnalysis . new ( template_source ) }
293+ let ( :dest_analysis ) { Rbs ::Merge ::FileAnalysis . new ( dest_source ) }
294+
295+ it "excludes entries with nil signatures from signature map" do
296+ # Mock signature_at to return nil
297+ allow ( template_analysis ) . to receive ( :signature_at ) . and_return ( nil )
298+
299+ aligner = described_class . new ( template_analysis , dest_analysis )
300+ alignment = aligner . align
301+
302+ # With nil signature, no matches can be made
303+ matches = alignment . select { |e | e [ :type ] == :match }
304+ expect ( matches ) . to be_empty
305+ end
306+ end
307+
308+ context "with unknown entry type in sort" do
309+ let ( :template_source ) { "class Foo\n end" }
310+ let ( :dest_source ) { "class Bar\n end" }
311+ let ( :template_analysis ) { Rbs ::Merge ::FileAnalysis . new ( template_source ) }
312+ let ( :dest_analysis ) { Rbs ::Merge ::FileAnalysis . new ( dest_source ) }
313+
314+ it "handles unknown entry types with fallback sort key" do
315+ aligner = described_class . new ( template_analysis , dest_analysis )
316+ alignment = aligner . align
317+
318+ # Inject an unknown type entry to test the else branch
319+ unknown_entry = { type : :unknown , template_index : 0 , dest_index : 0 }
320+ alignment << unknown_entry
321+
322+ # Re-sort (private method, but we can test indirectly)
323+ sorted = alignment . sort_by do |entry |
324+ case entry [ :type ]
325+ when :match
326+ [ 0 , entry [ :dest_index ] , entry [ :template_index ] ]
327+ when :dest_only
328+ [ 1 , entry [ :dest_index ] , 0 ]
329+ when :template_only
330+ [ 2 , entry [ :template_index ] , 0 ]
331+ else
332+ [ 3 , 0 , 0 ]
333+ end
334+ end
335+
336+ # Unknown type should sort last
337+ expect ( sorted . last [ :type ] ) . to eq ( :unknown )
338+ end
339+ end
340+ end
225341end
0 commit comments