Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lib/src/mcp/mcp_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,20 @@ Only one value can be selected.

Future<CallToolResult> _handleTest(CallToolRequest request) {
final args = request.arguments ?? {};
if (args['check_ignore'] == true && args['dart'] != true) {
return Future.value(
StructuredToolError(
toolName: 'test',
reason:
'"check_ignore" is only supported by the Dart test runner and '
'requires "dart": true. Flutter\'s test command does not accept '
'--check-ignore; omit the argument or set "check_ignore": false '
'for Flutter runs.',
failureType: ToolFailureType.validation,
attemptedArguments: args,
).toCallToolResult(),
);
}
final cliArgs = _parseTest(args);
final timeoutSeconds = (args['timeout_seconds'] as num?)?.toInt();
return _runToolCommand(
Expand Down
91 changes: 91 additions & 0 deletions test/src/mcp/mcp_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,97 @@ void main() {
expect(capturedArgs, equals(['test']));
});

group('check_ignore validation', () {
test(
'rejects check_ignore: true without dart: true (dart omitted)',
() async {
// `--check-ignore` is only registered on `very_good dart test`.
// Forwarding it to `very_good test` (Flutter) exits with 64; the
// handler must reject the combination before invoking the runner.
final response = await sendRequest(
CallToolRequest.methodName,
_params(
CallToolRequest(
name: 'test',
arguments: {'check_ignore': true},
),
),
);

expect(response['error'], isNull);
final result = CallToolResult.fromMap(
response['result'] as Map<String, Object?>,
);
expect(result.isError, isTrue);
final payload = _errorPayload(result);
expect(payload['failureType'], equals('validation'));
expect(payload['reason'], contains('check_ignore'));
expect(payload['reason'], contains('dart'));
verifyNever(() => mockCommandRunner.run(any()));
},
);

test('rejects check_ignore: true with dart: false', () async {
final response = await sendRequest(
CallToolRequest.methodName,
_params(
CallToolRequest(
name: 'test',
arguments: {'check_ignore': true, 'dart': false},
),
),
);

final result = CallToolResult.fromMap(
response['result'] as Map<String, Object?>,
);
expect(result.isError, isTrue);
final payload = _errorPayload(result);
expect(payload['failureType'], equals('validation'));
verifyNever(() => mockCommandRunner.run(any()));
});

test(
'allows check_ignore: false on Flutter without validation error',
() async {
await sendRequest(
CallToolRequest.methodName,
_params(
CallToolRequest(
name: 'test',
arguments: {'check_ignore': false},
),
),
);

final capturedArgs =
verify(() => mockCommandRunner.run(captureAny())).captured.first
as List<String>;
expect(capturedArgs, equals(['test']));
},
);

test(
'allows check_ignore: false on Dart without validation error',
() async {
await sendRequest(
CallToolRequest.methodName,
_params(
CallToolRequest(
name: 'test',
arguments: {'dart': true, 'check_ignore': false},
),
),
);

final capturedArgs =
verify(() => mockCommandRunner.run(captureAny())).captured.first
as List<String>;
expect(capturedArgs, equals(['dart', 'test']));
},
);
});

group('timeout_seconds (wall-clock)', () {
// Mimics `_testCommand`'s SIGINT path: wait on
// `ProcessSignalOverrides.current?.sigintWatch` and return
Expand Down
Loading