podman/test/podman_client_containers_test.dart

233 lines
7.1 KiB
Dart

import 'package:podman/podman.dart';
import 'package:test/test.dart';
import 'support/fake_podman_transport.dart';
void main() {
group('PodmanClient container API', () {
test('lists containers', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.get,
path: '/v5.0.0/libpod/containers/json',
queryParameters: const <String, List<String>>{
'all': <String>['true'],
},
responseBody: <Object?>[
<String, Object?>{
'Id': 'abc123',
'Image': 'hello-world:latest',
'Names': <String>['web'],
'State': 'running',
'Status': 'Up 4m',
},
],
);
final client = PodmanClient(transport: transport);
final containers = await client.listContainers();
expect(containers, hasLength(1));
expect(containers.first.id, 'abc123');
expect(containers.first.image, 'hello-world:latest');
expect(containers.first.name, 'web');
transport.expectNoPending();
});
test('creates and starts container via run', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/create',
queryParameters: const <String, List<String>>{
'name': <String>['web'],
},
body: const <String, Object?>{
'Image': 'docker.io/library/hello-world:latest',
},
statusCode: 201,
responseBody: const <String, Object?>{'Id': 'container-id-123'},
)
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/container-id-123/start',
statusCode: 204,
);
final client = PodmanClient(transport: transport);
final id = await client.run(
const RunOptions(
image: 'docker.io/library/hello-world:latest',
name: 'web',
),
);
expect(id, 'container-id-123');
transport.expectNoPending();
});
test('inspects container details', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.get,
path: '/v5.0.0/libpod/containers/web/json',
responseBody: <String, Object?>{
'Id': 'abc',
'Name': '/web',
'ImageName': 'hello-world:latest',
'State': <String, Object?>{'Status': 'running'},
'Config': <String, Object?>{
'Labels': <String, String>{'role': 'frontend'},
},
},
);
final client = PodmanClient(transport: transport);
final details = await client.inspectContainer('web');
expect(details.id, 'abc');
expect(details.name, 'web');
expect(details.state, 'running');
expect(details.labels['role'], 'frontend');
transport.expectNoPending();
});
test('containerExists returns false for missing container', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.get,
path: '/v5.0.0/libpod/containers/missing/exists',
statusCode: 404,
);
final client = PodmanClient(transport: transport);
final exists = await client.containerExists('missing');
expect(exists, isFalse);
transport.expectNoPending();
});
test('supports lifecycle operations', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/web/stop',
queryParameters: const <String, List<String>>{
't': <String>['10'],
},
statusCode: 204,
)
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/web/start',
statusCode: 204,
)
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/web/restart',
queryParameters: const <String, List<String>>{
't': <String>['5'],
},
statusCode: 204,
)
..enqueue(
method: HttpMethod.delete,
path: '/v5.0.0/libpod/containers/web',
queryParameters: const <String, List<String>>{
'force': <String>['true'],
'v': <String>['true'],
},
statusCode: 204,
)
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/prune',
statusCode: 200,
responseBody: const <String, Object?>{},
);
final client = PodmanClient(transport: transport);
await client.stop('web', timeoutSeconds: 10);
await client.start('web');
await client.restart('web', timeoutSeconds: 5);
await client.removeContainer('web', force: true, removeVolumes: true);
await client.pruneContainers();
transport.expectNoPending();
});
test('supports logs query options and log polling', () async {
final since = DateTime.utc(2026, 4, 6, 10, 0, 0);
final until = DateTime.utc(2026, 4, 6, 11, 0, 0);
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.get,
path: '/v5.0.0/libpod/containers/web/logs',
queryParameters: <String, List<String>>{
'stdout': <String>['true'],
'stderr': <String>['false'],
'timestamps': <String>['true'],
'since': <String>[since.toIso8601String()],
'until': <String>[until.toIso8601String()],
'tail': <String>['25'],
},
responseBody: 'log-one\n',
)
..enqueue(
method: HttpMethod.get,
path: '/v5.0.0/libpod/containers/web/logs',
queryParameters: <String, List<String>>{
'stdout': <String>['true'],
'stderr': <String>['true'],
'timestamps': <String>['true'],
'since': <String>[since.toIso8601String()],
'tail': <String>['10'],
},
responseBody: 'log-two\n',
);
final client = PodmanClient(transport: transport);
final logs = await client.logs(
'web',
tail: 25,
since: since,
until: until,
timestamps: true,
stdout: true,
stderr: false,
);
expect(logs, 'log-one\n');
final chunk = await client
.watchLogs(
'web',
since: since,
tail: 10,
pollInterval: const Duration(days: 1),
reconnect: false,
)
.first;
expect(chunk, 'log-two\n');
transport.expectNoPending();
});
test('throws on unexpected API status', () async {
final transport = FakePodmanTransport()
..enqueue(
method: HttpMethod.post,
path: '/v5.0.0/libpod/containers/web/start',
statusCode: 500,
responseBody: const <String, Object?>{'message': 'boom'},
);
final client = PodmanClient(transport: transport);
await expectLater(
client.start('web'),
throwsA(isA<PodmanApiException>()),
);
});
});
}