import test from 'brittle' import { pickContainerDisplayName, resolveContainerLabel, extractContainerIdFromCgroupName, metricCardSubtitle, chartOptionLabel, isHexId, } from '../shared/container-names.js' import { mapDockerContainerNames, parseHttpBody, parseDockerPsNames, } from '../server/services/collectors/docker.js' import { makeDockerCpuChart, makeDockerMemChart } from '../shared/metrics.js' test('pickContainerDisplayName prefers compose service', (t) => { t.is( pickContainerDisplayName({ Id: 'a'.repeat(64), Names: ['/myproj_web_1'], Labels: { 'com.docker.compose.project': 'myproj', 'com.docker.compose.service': 'web', 'com.docker.compose.container-number': '1', }, }), 'web' ) t.is( pickContainerDisplayName({ Id: 'b'.repeat(64), Names: ['/myproj_web_2'], Labels: { 'com.docker.compose.service': 'web', 'com.docker.compose.container-number': '2', }, }), 'web · 2' ) }) test('pickContainerDisplayName falls back to Names then image', (t) => { t.is( pickContainerDisplayName({ Id: 'c'.repeat(64), Names: ['/friendly-name'], Labels: {}, }), 'friendly-name' ) t.is( pickContainerDisplayName({ Id: 'd'.repeat(64), Names: [], Image: 'ghcr.io/org/redis:7', }), 'redis' ) }) test('mapDockerContainerNames indexes full and short ids', (t) => { const id = 'abcdef0123456789' + '0'.repeat(48) const map = mapDockerContainerNames([ { Id: id, Names: ['/nginx'], Labels: {}, }, ]) t.is(map.get(id), 'nginx') t.is(map.get(id.slice(0, 12)), 'nginx') }) test('parseHttpBody handles Content-Length and chunked Docker replies', (t) => { const json = '[{"Id":"aa","Names":["/x"]}]' const plain = `HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ${json.length}\r\n\r\n` + json t.is(parseHttpBody(plain), json) const chunked = 'HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n' + Number(json.length).toString(16) + '\r\n' + json + '\r\n0\r\n\r\n' t.is(parseHttpBody(chunked), json) }) test('parseDockerPsNames maps id to Names', (t) => { const id = '45b3a7631c68aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' const map = parseDockerPsNames(`${id}\tdozzle\n`) t.is(map.get(id.slice(0, 12)), 'dozzle') t.is(map.get('dozzle'), 'dozzle') }) test('resolveContainerLabel humanizes cgroup docker scopes', (t) => { const id = 'a1b2c3d4e5f6789012345678abcdef01' const map = new Map([ [id, 'api'], [id.slice(0, 12), 'api'], ]) t.is(resolveContainerLabel(`docker-${id}.scope`, map), 'api') t.is(resolveContainerLabel(id, map), 'api') t.is(resolveContainerLabel(`docker-${id}`, new Map()), `container ${id.slice(0, 12)}`) t.ok(extractContainerIdFromCgroupName(`docker-${id}.scope`)?.startsWith('a1b2')) }) test('docker chart titles use display names', (t) => { const cpu = makeDockerCpuChart('abc123def456', 'nginx') t.is(cpu.title, 'nginx · CPU') t.is(cpu.family, 'nginx') const mem = makeDockerMemChart('abc123def456', 'nginx') t.is(mem.title, 'nginx · memory') }) test('metricCardSubtitle hides hashes for container charts', (t) => { const id = 'docker.cpu.abc123def456' t.is( metricCardSubtitle(id, { title: 'nginx · CPU', family: 'nginx', units: 'percentage' }), 'percentage' ) t.absent(metricCardSubtitle(id, { units: 'percentage' }).includes('abc123')) t.ok(isHexId('abc123def456')) t.is(chartOptionLabel(id, { title: 'nginx · CPU' }), 'nginx · CPU') })