Merge: resolve conflict in gateway_runtime_bridge_skills_test
This commit is contained in:
commit
ebf315bbef
@ -213,23 +213,35 @@ extension GatewayRuntimeApiInternal on GatewayRuntime {
|
||||
allowErrorPayload: true,
|
||||
),
|
||||
);
|
||||
return asList(payload['skills'])
|
||||
final statusPayload = skillsStatusPayloadInternal(payload);
|
||||
return asList(statusPayload['skills'])
|
||||
.map((item) {
|
||||
final map = asMap(item);
|
||||
return GatewaySkillSummary(
|
||||
name: stringValue(map['name']) ?? 'Skill',
|
||||
name:
|
||||
stringValue(map['name']) ??
|
||||
stringValue(map['title']) ??
|
||||
stringValue(map['id']) ??
|
||||
'Skill',
|
||||
description: stringValue(map['description']) ?? '',
|
||||
source: stringValue(map['source']) ?? 'workspace',
|
||||
skillKey:
|
||||
stringValue(map['skillKey']) ??
|
||||
stringValue(map['skill_key']) ??
|
||||
stringValue(map['key']) ??
|
||||
stringValue(map['id']) ??
|
||||
stringValue(map['name']) ??
|
||||
'skill',
|
||||
primaryEnv: stringValue(map['primaryEnv']),
|
||||
eligible: boolValue(map['eligible']) ?? false,
|
||||
disabled: boolValue(map['disabled']) ?? false,
|
||||
missingBins: stringList(asMap(map['missing'])['bins']),
|
||||
missingEnv: stringList(asMap(map['missing'])['env']),
|
||||
missingConfig: stringList(asMap(map['missing'])['config']),
|
||||
missingBins: skillMissingListInternal(map, 'bins', 'missingBins'),
|
||||
missingEnv: skillMissingListInternal(map, 'env', 'missingEnv'),
|
||||
missingConfig: skillMissingListInternal(
|
||||
map,
|
||||
'config',
|
||||
'missingConfig',
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList(growable: false);
|
||||
@ -548,3 +560,33 @@ extension GatewayRuntimeApiInternal on GatewayRuntime {
|
||||
return result.payload;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> skillsStatusPayloadInternal(Map<String, dynamic> payload) {
|
||||
if (asList(payload['skills']).isNotEmpty) {
|
||||
return payload;
|
||||
}
|
||||
for (final key in const <String>[
|
||||
'status',
|
||||
'skillStatus',
|
||||
'data',
|
||||
'payload',
|
||||
]) {
|
||||
final nested = asMap(payload[key]);
|
||||
if (asList(nested['skills']).isNotEmpty) {
|
||||
return nested;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
List<String> skillMissingListInternal(
|
||||
Map<String, dynamic> skill,
|
||||
String nestedKey,
|
||||
String flatKey,
|
||||
) {
|
||||
final flat = stringList(skill[flatKey]);
|
||||
if (flat.isNotEmpty) {
|
||||
return flat;
|
||||
}
|
||||
return stringList(asMap(skill['missing'])[nestedKey]);
|
||||
}
|
||||
|
||||
@ -3797,7 +3797,7 @@ void main() {
|
||||
controller.assistantSessionHasPendingRun('openclaw-failed-task'),
|
||||
isFalse,
|
||||
);
|
||||
expect(controller.openClawGatewayActiveTasksInternal, 0);
|
||||
await _waitForOpenClawActiveTaskCount(controller, 0);
|
||||
expect(
|
||||
controller
|
||||
.requireTaskThreadForSessionInternal('openclaw-failed-task')
|
||||
@ -4384,6 +4384,22 @@ Future<void> _waitForThreadLastResultCode(
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _waitForOpenClawActiveTaskCount(
|
||||
AppController controller,
|
||||
int expectedCount,
|
||||
) async {
|
||||
final deadline = DateTime.now().add(const Duration(seconds: 15));
|
||||
while (DateTime.now().isBefore(deadline)) {
|
||||
if (controller.openClawGatewayActiveTasksInternal == expectedCount) {
|
||||
return;
|
||||
}
|
||||
await Future<void>.delayed(const Duration(milliseconds: 10));
|
||||
}
|
||||
throw StateError(
|
||||
'Timed out waiting for OpenClaw active task count $expectedCount. Current count: ${controller.openClawGatewayActiveTasksInternal}.',
|
||||
);
|
||||
}
|
||||
|
||||
class _RecordingGoTaskServiceClient implements GoTaskServiceClient {
|
||||
int executeCount = 0;
|
||||
final List<GoTaskServiceRequest> requests = <GoTaskServiceRequest>[];
|
||||
|
||||
@ -236,4 +236,109 @@ void main() {
|
||||
expect(controller.items.single.eligible, isFalse);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'GatewayRuntime loads skills from nested bridge status payload',
|
||||
() async {
|
||||
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
||||
final subscription = server.listen((request) async {
|
||||
final body = await utf8.decoder.bind(request).join();
|
||||
final rpc = jsonDecode(body) as Map<String, dynamic>;
|
||||
final method = rpc['method']?.toString().trim() ?? '';
|
||||
request.response.headers.contentType = ContentType.json;
|
||||
|
||||
if (method == 'xworkmate.gateway.connect') {
|
||||
request.response.write(
|
||||
jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': rpc['id'],
|
||||
'result': <String, dynamic>{
|
||||
'ok': true,
|
||||
'snapshot': <String, dynamic>{
|
||||
'status': 'connected',
|
||||
'mode': 'remote',
|
||||
'statusText': 'Connected',
|
||||
'mainSessionKey': 'main',
|
||||
},
|
||||
'auth': <String, dynamic>{'role': 'operator'},
|
||||
'returnedDeviceToken': '',
|
||||
},
|
||||
}),
|
||||
);
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == 'xworkmate.gateway.request') {
|
||||
request.response.write(
|
||||
jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': rpc['id'],
|
||||
'result': <String, dynamic>{
|
||||
'ok': true,
|
||||
'payload': <String, dynamic>{
|
||||
'status': <String, dynamic>{
|
||||
'workspaceDir': '/home/ubuntu/.openclaw/workspace',
|
||||
'managedSkillsDir': '/home/ubuntu/.openclaw/skills',
|
||||
'skills': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'name': 'Browser Automation',
|
||||
'description': 'Drive browser workflows.',
|
||||
'source': 'agent',
|
||||
'id': 'browser-automation',
|
||||
'eligible': true,
|
||||
'disabled': false,
|
||||
'missingBins': <String>[],
|
||||
'missingEnv': <String>[],
|
||||
'missingConfig': <String>[],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
request.response.statusCode = HttpStatus.badRequest;
|
||||
await request.response.close();
|
||||
});
|
||||
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-bridge-skills-nested-test-',
|
||||
);
|
||||
final store = SecureConfigStore(
|
||||
enableSecureStorage: false,
|
||||
appDataRootPathResolver: () async => '${tempDir.path}/settings.sqlite3',
|
||||
secretRootPathResolver: () async => tempDir.path,
|
||||
);
|
||||
final acpClient = GatewayAcpClient(
|
||||
endpointResolver: () => Uri.parse('http://127.0.0.1:${server.port}'),
|
||||
authorizationResolver: (_) async => 'bridge-token',
|
||||
);
|
||||
final identityStore = DeviceIdentityStore(store);
|
||||
final runtime = GatewayRuntime(
|
||||
store: store,
|
||||
identityStore: identityStore,
|
||||
sessionClient: GatewayAcpRuntimeSessionClient(client: acpClient),
|
||||
);
|
||||
await runtime.initialize();
|
||||
addTearDown(() async {
|
||||
runtime.dispose();
|
||||
await subscription.cancel();
|
||||
await server.close(force: true);
|
||||
await tempDir.delete(recursive: true);
|
||||
});
|
||||
|
||||
final controller = SkillsController(runtime);
|
||||
await controller.refresh(agentId: 'main');
|
||||
|
||||
expect(controller.error, isNull);
|
||||
expect(controller.items, hasLength(1));
|
||||
expect(controller.items.single.skillKey, 'browser-automation');
|
||||
expect(controller.items.single.missingBins, isEmpty);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user