Skip to content

Commit 0c51354

Browse files
fix: correct extractPositionalArg to not swallow path after boolean --no-ui flag
The arg parser incorrectly set skipNext=true for ALL --flag patterns, causing code-iq serve --no-ui /repo to silently drop /repo (treating it as the value of --no-ui). Boolean flags that take no value must not consume the next token. - Added BOOLEAN_FLAGS set (--no-ui, --help, -h, --version) to CodeIqApplication - extractPositionalArg now skips skipNext for flags in BOOLEAN_FLAGS - Added pathNotSwallowedWhenNoUiPrecedesPath test to ServeCommandTest - Added CodeIqApplicationArgParsingTest with 5 reflection-based unit tests for extractPositionalArg edge cases Co-Authored-By: Paperclip <noreply@paperclip.ing>
1 parent 0dab250 commit 0c51354

3 files changed

Lines changed: 75 additions & 4 deletions

File tree

src/main/java/io/github/randomcodespace/iq/CodeIqApplication.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,18 @@ private static String extractFlag(String[] args, String flagName) {
134134
return null;
135135
}
136136

137+
/**
138+
* Boolean (no-value) flags for the serve command.
139+
* These must NOT consume the next token as their value.
140+
*/
141+
private static final java.util.Set<String> BOOLEAN_FLAGS = java.util.Set.of(
142+
"--no-ui", "--help", "-h", "--version"
143+
);
144+
137145
/**
138146
* Extract the first positional argument after the command name.
139147
* Skips flags (--name value pairs) to find positional args.
148+
* Boolean flags (no value) are not allowed to consume the next token.
140149
*/
141150
private static String extractPositionalArg(String[] args, String command) {
142151
boolean foundCommand = false;
@@ -151,17 +160,17 @@ private static String extractPositionalArg(String[] args, String command) {
151160
continue;
152161
}
153162
if (foundCommand) {
154-
// Skip --flag value pairs
155-
if (arg.startsWith("--") && !arg.contains("=")) {
163+
// Skip --flag value pairs, but not boolean flags that take no value
164+
if (arg.startsWith("--") && !arg.contains("=") && !BOOLEAN_FLAGS.contains(arg)) {
156165
skipNext = true;
157166
continue;
158167
}
159-
if (arg.startsWith("-") && arg.length() == 2) {
168+
if (arg.startsWith("-") && arg.length() == 2 && !BOOLEAN_FLAGS.contains(arg)) {
160169
skipNext = true; // short flag like -p 8080
161170
continue;
162171
}
163172
if (arg.startsWith("-")) {
164-
continue; // --flag=value or -flag
173+
continue; // --flag=value, boolean flag, or unknown short flag
165174
}
166175
return arg;
167176
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.github.randomcodespace.iq;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.lang.reflect.Method;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertNull;
9+
10+
/**
11+
* Unit tests for CodeIqApplication argument parsing helper methods.
12+
* These are called in main() before the Spring context starts.
13+
*/
14+
class CodeIqApplicationArgParsingTest {
15+
16+
private static String extractPositionalArg(String[] args, String command) throws Exception {
17+
Method m = CodeIqApplication.class.getDeclaredMethod("extractPositionalArg", String[].class, String.class);
18+
m.setAccessible(true);
19+
return (String) m.invoke(null, args, command);
20+
}
21+
22+
@Test
23+
void extractsPathAfterCommand() throws Exception {
24+
String result = extractPositionalArg(new String[]{"serve", "/my/repo"}, "serve");
25+
assertEquals("/my/repo", result);
26+
}
27+
28+
@Test
29+
void pathNotSwallowedByBooleanNoUiFlag() throws Exception {
30+
// Regression: --no-ui is boolean; must not consume /repo as its value.
31+
String result = extractPositionalArg(new String[]{"serve", "--no-ui", "/my/repo"}, "serve");
32+
assertEquals("/my/repo", result);
33+
}
34+
35+
@Test
36+
void pathStillExtractedWhenPortFlagPrecedes() throws Exception {
37+
String result = extractPositionalArg(new String[]{"serve", "--port", "9090", "/my/repo"}, "serve");
38+
assertEquals("/my/repo", result);
39+
}
40+
41+
@Test
42+
void pathStillExtractedWithNoUiAndPort() throws Exception {
43+
String result = extractPositionalArg(new String[]{"serve", "--no-ui", "--port", "9090", "/my/repo"}, "serve");
44+
assertEquals("/my/repo", result);
45+
}
46+
47+
@Test
48+
void returnsNullWhenNoPath() throws Exception {
49+
String result = extractPositionalArg(new String[]{"serve", "--no-ui"}, "serve");
50+
assertNull(result);
51+
}
52+
}

src/test/java/io/github/randomcodespace/iq/cli/ServeCommandTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,14 @@ void noUiFlagIsRecognized() {
6464
cmdLine.parseArgs("--no-ui");
6565
assertEquals(true, cmd.isNoUi());
6666
}
67+
68+
@Test
69+
void pathNotSwallowedWhenNoUiPrecedesPath() {
70+
// Regression: --no-ui is boolean and must not consume the next positional arg.
71+
var cmd = new ServeCommand();
72+
var cmdLine = new CommandLine(cmd);
73+
cmdLine.parseArgs("--no-ui", "/some/repo");
74+
assertEquals(true, cmd.isNoUi());
75+
assertEquals("/some/repo", cmd.getPath().toString());
76+
}
6777
}

0 commit comments

Comments
 (0)