2929import org .bukkit .block .Sign ;
3030import org .bukkit .block .data .BlockData ;
3131import org .bukkit .entity .Player ;
32+ import org .bukkit .event .block .SignChangeEvent ;
3233import org .junit .jupiter .api .BeforeEach ;
3334import org .junit .jupiter .api .Test ;
35+ import org .mockito .ArgumentCaptor ;
3436import org .mockito .stubbing .Answer ;
3537
3638import java .util .Locale ;
@@ -120,8 +122,8 @@ private SubcommandContext createContext(SignText signText, ArgParser argParser,
120122 }
121123
122124 @ Test
123- public void cutAllLinesShowsClipboardDump () {
124- // Setup: normal cut with NoopValidator
125+ public void cutAllLinesShowsClipboardDumpOnly () {
126+ // Setup: normal cut with NoopValidator (no external modification)
125127 Sign sign = createSign (defaultSignLines .clone ());
126128 SignShim signShim = new SignShim (sign );
127129 SignEditValidator validator = new NoopSignEditValidator ();
@@ -137,6 +139,110 @@ public void cutAllLinesShowsClipboardDump() {
137139 // Verify clipboard dump is shown
138140 verify (comms ).tell (comms .t ("lines_cut_section" ));
139141 verify (comms ).dumpLines (any ());
142+
143+ // Verify NO sign result section is shown (no external modification)
144+ verify (comms , never ()).tell (contains ("after_section" ));
145+ verify (comms , never ()).tell (argThat (arg ->
146+ arg != null && arg .contains ("Modified by another plugin" )));
147+ }
148+
149+ @ Test
150+ public void cutWithExternalModificationShowsSignResultSection () {
151+ // Setup: validator that restores line 1 when we try to clear it
152+ SignEditValidator modifyingValidator = new SignEditValidator () {
153+ @ Override
154+ public String [] validate (SignShim proposedSign , SideShim side , Player player ) {
155+ String [] lines = proposedSign .getSide (side ).getLines ().clone ();
156+ // Simulate another plugin keeping line 1 non-empty
157+ if (lines [1 ].isEmpty ()) {
158+ lines [1 ] = "KEPT_BY_PLUGIN" ;
159+ }
160+ return lines ;
161+ }
162+
163+ @ Override
164+ public void validate (SignChangeEvent signChangeEvent ) {
165+ }
166+ };
167+
168+ Sign sign = createSign (defaultSignLines .clone ());
169+ SignShim signShim = new SignShim (sign );
170+ SignText signText = new SignText (modifyingValidator );
171+ ArgParser argParser = createArgParser (new int []{0 , 1 , 2 , 3 });
172+
173+ SubcommandContext context = createContext (signText , argParser , modifyingValidator );
174+ CutSignEditInteraction interaction = new CutSignEditInteraction (context );
175+
176+ // Execute the cut
177+ interaction .interact (player , signShim , SideShim .FRONT );
178+
179+ // Verify clipboard dump is shown first
180+ verify (comms ).tell (comms .t ("lines_cut_section" ));
181+ verify (comms ).dumpLines (any ());
182+
183+ // Verify sign result section IS shown (external modification occurred)
184+ ArgumentCaptor <String > messageCaptor = ArgumentCaptor .forClass (String .class );
185+ verify (comms , atLeast (2 )).tell (messageCaptor .capture ());
186+
187+ // Check that one of the messages contains the external modification notice
188+ boolean hasExternalModMessage = messageCaptor .getAllValues ().stream ()
189+ .anyMatch (msg -> msg != null && msg .contains ("Modified by another plugin" ));
190+ assertTrue (hasExternalModMessage , "Expected external modification message to be shown" );
191+
192+ // Verify the modified line is shown
193+ boolean hasModifiedLine = messageCaptor .getAllValues ().stream ()
194+ .anyMatch (msg -> msg != null && msg .contains ("KEPT_BY_PLUGIN" ));
195+ assertTrue (hasModifiedLine , "Expected modified line content to be shown" );
196+ }
197+
198+ @ Test
199+ public void cutPartialLinesWithExternalModificationShowsOnlyAffectedLines () {
200+ // Setup: validator that modifies line 2 when we try to clear it
201+ SignEditValidator modifyingValidator = new SignEditValidator () {
202+ @ Override
203+ public String [] validate (SignShim proposedSign , SideShim side , Player player ) {
204+ String [] lines = proposedSign .getSide (side ).getLines ().clone ();
205+ // Simulate another plugin modifying line 2 to something else
206+ if (lines [2 ].isEmpty ()) {
207+ lines [2 ] = "MODIFIED_LINE_3" ;
208+ }
209+ return lines ;
210+ }
211+
212+ @ Override
213+ public void validate (SignChangeEvent signChangeEvent ) {
214+ }
215+ };
216+
217+ Sign sign = createSign (defaultSignLines .clone ());
218+ SignShim signShim = new SignShim (sign );
219+ SignText signText = new SignText (modifyingValidator );
220+ // Only cut lines 1, 2, 3 (0-indexed: 0, 1, 2)
221+ ArgParser argParser = createArgParser (new int []{0 , 1 , 2 });
222+
223+ SubcommandContext context = createContext (signText , argParser , modifyingValidator );
224+ CutSignEditInteraction interaction = new CutSignEditInteraction (context );
225+
226+ // Execute the cut
227+ interaction .interact (player , signShim , SideShim .FRONT );
228+
229+ // Verify clipboard dump is shown
230+ verify (comms ).tell (comms .t ("lines_cut_section" ));
231+ verify (comms ).dumpLines (any ());
232+
233+ // Capture all messages
234+ ArgumentCaptor <String > messageCaptor = ArgumentCaptor .forClass (String .class );
235+ verify (comms , atLeast (2 )).tell (messageCaptor .capture ());
236+
237+ // Verify external modification message is shown
238+ boolean hasExternalModMessage = messageCaptor .getAllValues ().stream ()
239+ .anyMatch (msg -> msg != null && msg .contains ("Modified by another plugin" ));
240+ assertTrue (hasExternalModMessage , "Expected external modification message" );
241+
242+ // Verify the modified line content is shown
243+ boolean hasModifiedContent = messageCaptor .getAllValues ().stream ()
244+ .anyMatch (msg -> msg != null && msg .contains ("MODIFIED_LINE_3" ));
245+ assertTrue (hasModifiedContent , "Expected modified line content to be shown" );
140246 }
141247
142248 @ Test
@@ -160,7 +266,7 @@ public void cutCopiesOriginalLinesToClipboard() {
160266 }
161267
162268 @ Test
163- public void cutClearsSignLines () {
269+ public void cutWithNoExternalModificationClearsSignLines () {
164270 Sign sign = createSign (defaultSignLines .clone ());
165271 SignShim signShim = new SignShim (sign );
166272 SignEditValidator validator = new NoopSignEditValidator ();
0 commit comments