| 1 | using Xunit; |
| 2 | |
| 3 | namespace CascadeIDE.Tests; |
| 4 | |
| 5 | public sealed class IntentMelodyCatalogWireTests |
| 6 | { |
| 7 | [Fact] |
| 8 | public void ParseBundle_unknown_wire_class_throws() |
| 9 | { |
| 10 | const string toml = |
| 11 | """ |
| 12 | intent_catalog_schema_version = 2 |
| 13 | melody_catalog_schema_version = 1 |
| 14 | [[tail_wire_class]] |
| 15 | id = "ok" |
| 16 | kind = "delimited_slots" |
| 17 | between_slots_any_of = [":"] |
| 18 | [[command]] |
| 19 | command_id = "select" |
| 20 | melody_slug = "x" |
| 21 | melody_shape = "parametric" |
| 22 | melody_tail_signature = "<start:int>:<end:int>" |
| 23 | melody_wire_class = "missing" |
| 24 | melody_chord_commit = "enter" |
| 25 | """; |
| 26 | |
| 27 | var ex = Assert.Throws<InvalidOperationException>(() => |
| 28 | IntentMelodyAliases.ParseBundleForTests(toml.Trim())); |
| 29 | |
| 30 | Assert.Contains("missing", ex.Message, StringComparison.OrdinalIgnoreCase); |
| 31 | } |
| 32 | |
| 33 | [Fact] |
| 34 | public void ParseBundle_wire_kind_mismatch_delimited_requires_two_int_slots() |
| 35 | { |
| 36 | const string toml = |
| 37 | """ |
| 38 | intent_catalog_schema_version = 2 |
| 39 | melody_catalog_schema_version = 1 |
| 40 | [[tail_wire_class]] |
| 41 | id = "del" |
| 42 | kind = "delimited_slots" |
| 43 | between_slots_any_of = [":"] |
| 44 | [[command]] |
| 45 | command_id = "cmd" |
| 46 | melody_slug = "x" |
| 47 | melody_shape = "parametric" |
| 48 | melody_tail_signature = "<url:url>" |
| 49 | melody_wire_class = "del" |
| 50 | melody_chord_commit = "enter" |
| 51 | """; |
| 52 | |
| 53 | var ex = Assert.Throws<InvalidOperationException>(() => |
| 54 | IntentMelodyAliases.ParseBundleForTests(toml.Trim())); |
| 55 | |
| 56 | Assert.Contains("DelimitedSlots", ex.Message, StringComparison.Ordinal); |
| 57 | } |
| 58 | |
| 59 | [Fact] |
| 60 | public void ParseBundle_wire_kind_mismatch_single_remainder_requires_url_slot() |
| 61 | { |
| 62 | const string toml = |
| 63 | """ |
| 64 | intent_catalog_schema_version = 2 |
| 65 | melody_catalog_schema_version = 1 |
| 66 | [[tail_wire_class]] |
| 67 | id = "sr" |
| 68 | kind = "single_remainder" |
| 69 | [[command]] |
| 70 | command_id = "cmd" |
| 71 | melody_slug = "x" |
| 72 | melody_shape = "parametric" |
| 73 | melody_tail_signature = "<a:int>:<b:int>" |
| 74 | melody_wire_class = "sr" |
| 75 | melody_chord_commit = "enter" |
| 76 | """; |
| 77 | |
| 78 | var ex = Assert.Throws<InvalidOperationException>(() => |
| 79 | IntentMelodyAliases.ParseBundleForTests(toml.Trim())); |
| 80 | |
| 81 | Assert.Contains("SingleRemainder", ex.Message, StringComparison.Ordinal); |
| 82 | } |
| 83 | |
| 84 | [Fact] |
| 85 | public void ChordDefersInstantExecute_respects_enter_vs_instant_commit() |
| 86 | { |
| 87 | var enterCommit = new MelodyRootEntry( |
| 88 | "p", |
| 89 | "cmd", |
| 90 | IntentMelodyShape.Parametric, |
| 91 | ShowUsageHintIfBareSlug: false, |
| 92 | TailSignature: "<start:ln>:<end:ln>", |
| 93 | WireClass: "int_chain_colon_space", |
| 94 | ChordCommit: "enter", |
| 95 | PaletteHintSlug: null); |
| 96 | |
| 97 | var instantCommit = enterCommit with { ChordCommit = "instant" }; |
| 98 | |
| 99 | Assert.True(ParametricIntentMelody.ChordDefersInstantExecuteForMelodyRoot(enterCommit)); |
| 100 | Assert.False(ParametricIntentMelody.ChordDefersInstantExecuteForMelodyRoot(instantCommit)); |
| 101 | } |
| 102 | |
| 103 | [Fact] |
| 104 | public void ResolvePaletteHintKey_falls_back_to_slug_when_field_absent() |
| 105 | { |
| 106 | Assert.Equal("gs", ParametricIntentMelody.ResolvePaletteHintKey("gs")); |
| 107 | } |
| 108 | |
| 109 | [Fact] |
| 110 | public void ResolvePaletteHintKey_uses_bundled_palette_hint_slug_for_wai() |
| 111 | { |
| 112 | Assert.Equal("wai-url", ParametricIntentMelody.ResolvePaletteHintKey("wai")); |
| 113 | } |
| 114 | |
| 115 | [Fact] |
| 116 | public void Bundled_int_chain_colon_space_supports_space_after_slug() |
| 117 | { |
| 118 | Assert.True(IntentMelodyCatalog.TryGetTailWireClass("int_chain_colon_space", out var wire)); |
| 119 | Assert.Contains(wire.BetweenSlotsSeparators, s => s.Length == 1 && s[0] == ' '); |
| 120 | Assert.True(ParametricIntentMelody.TryParseLineRangeTail("els 7", out _)); |
| 121 | } |
| 122 | } |
| 123 | |