"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const linux_adapter_1 = require("../../../src/adapters/linux-adapter");
const errors_1 = require("../../../src/types/errors");
const os_1 = __importDefault(require("os"));
describe('LinuxAdapter 内部解析逻辑', () => {
    it('应基于差分快照计算 CPU 使用率', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const snapshotA = `cpu  100 20 80 1000 10 5 5 0\n` +
            `cpu0  50 10 40 500 5 2 3 0\n` +
            `cpu1  50 10 40 500 5 3 2 0\n`;
        const snapshotB = `cpu  150 30 120 1100 20 10 10 0\n` +
            `cpu0  80 15 60 550 10 4 5 0\n` +
            `cpu1  70 15 60 550 10 6 5 0\n`;
        const first = internal.parseCpuStat(snapshotA);
        const second = internal.parseCpuStat(snapshotB);
        const usage = internal.calculateCpuUsage(first, second);
        (0, chai_1.expect)(usage.overall).to.be.closeTo(54.54, 0.1);
        (0, chai_1.expect)(usage.user).to.be.closeTo(27.27, 0.1);
        (0, chai_1.expect)(usage.system).to.be.closeTo(18.18, 0.1);
        (0, chai_1.expect)(usage.idle).to.be.closeTo(45.45, 0.1);
        (0, chai_1.expect)(usage.iowait).to.be.closeTo(4.54, 0.1);
        (0, chai_1.expect)(usage.irq).to.be.closeTo(2.27, 0.1);
        (0, chai_1.expect)(usage.softirq).to.be.closeTo(2.27, 0.1);
        (0, chai_1.expect)(usage.cores).to.have.lengthOf(2);
        (0, chai_1.expect)(usage.cores[0]).to.be.closeTo(56.14, 0.2);
        (0, chai_1.expect)(usage.cores[1]).to.be.closeTo(52.83, 0.2);
    });
    it('应正确解析 ip addr 输出中的网卡名称与地址', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const sampleOutput = `1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n` +
            `    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n` +
            `    inet 127.0.0.1/8 scope host lo\n` +
            `       valid_lft forever preferred_lft forever\n` +
            `    inet6 ::1/128 scope host \n` +
            `       valid_lft forever preferred_lft forever\n` +
            `2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n` +
            `    link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff\n` +
            `    inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0\n` +
            `       valid_lft 86325sec preferred_lft 86325sec\n` +
            `    inet6 fe80::aabb:ccff:fedd:eeff/64 scope link \n` +
            `       valid_lft forever preferred_lft forever\n`;
        const interfaces = internal.parseNetworkInterfaces(sampleOutput);
        (0, chai_1.expect)(interfaces).to.have.lengthOf(2);
        const loopback = interfaces[0];
        const eth0 = interfaces[1];
        (0, chai_1.expect)(loopback.name).to.equal('lo');
        (0, chai_1.expect)(loopback.state).to.equal('unknown');
        (0, chai_1.expect)(loopback.internal).to.be.true;
        (0, chai_1.expect)(loopback.addresses.map((item) => item.address)).to.include.members(['127.0.0.1', '::1']);
        (0, chai_1.expect)(eth0.name).to.equal('eth0');
        (0, chai_1.expect)(eth0.state).to.equal('up');
        (0, chai_1.expect)(eth0.internal).to.be.false;
        (0, chai_1.expect)(eth0.addresses.map((item) => item.address)).to.include('192.168.1.10');
    });
    it('应解析进程内存与启动时间', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const stat = '123 (bash) R 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0';
        const status = 'Name:\tbash\nThreads:\t2\nVmRSS:\t2048 kB\nVmSize:\t4096 kB\n';
        const cmdline = 'bash\0--login\0';
        const result = internal.parseProcessInfo(123, stat, status, cmdline);
        (0, chai_1.expect)(result.memoryUsage).to.equal(2048 * 1024);
        (0, chai_1.expect)(result.rss).to.equal(2048 * 1024);
        const now = Date.now();
        let systemUptimeMs = 0;
        try {
            systemUptimeMs = os_1.default.uptime() * 1000;
        }
        catch {
            systemUptimeMs = 0;
        }
        (0, chai_1.expect)(result.startTime).to.be.at.most(now);
        const lowerBound = systemUptimeMs > 0 ? now - systemUptimeMs - 1000 : now - 1000;
        (0, chai_1.expect)(result.startTime).to.be.at.least(lowerBound);
    });
    it('应优先使用 uname -m 解析体系结构', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const uname = 'Linux test-host 5.15.0-89-generic #99-Ubuntu SMP PREEMPT Fri x86_64 x86_64 x86_64 GNU/Linux';
        const version = 'Ubuntu 5.15.0-89-generic';
        const uptime = '12345.67 4567.89';
        const loadavg = '0.10 0.20 0.30 1/234 567';
        const machine = 'x86_64\n';
        const info = internal.parseSystemInfo(uname, version, uptime, loadavg, machine);
        (0, chai_1.expect)(info.arch).to.equal('x86_64');
    });
    it('在缺少 uname -m 时仍能推断常见架构', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const uname = 'Linux test 6.5.0-21 #1 SMP PREEMPT_DYNAMIC Wed aarch64 GNU/Linux';
        const version = '#1 SMP PREEMPT';
        const uptime = '1000.00 2000.00';
        const loadavg = '0.50 0.60 0.70 1/1 2';
        const info = internal.parseSystemInfo(uname, version, uptime, loadavg, '');
        (0, chai_1.expect)(info.arch.toLowerCase()).to.equal('aarch64');
    });
    it('应正确解析多种默认网关格式', () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const viaOutput = 'default via 192.168.1.1 dev eth0 proto dhcp metric 100';
        const directOutput = 'default dev ppp0 scope link';
        const viaResult = internal.parseDefaultGateway(viaOutput);
        (0, chai_1.expect)(viaResult).to.deep.equal({ gateway: '192.168.1.1', interface: 'eth0' });
        const directResult = internal.parseDefaultGateway(directOutput);
        (0, chai_1.expect)(directResult).to.deep.equal({ gateway: null, interface: 'ppp0' });
    });
    it('网络命令回退失败时应保留主次错误', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        let callCount = 0;
        adapter.executeCommand = async (command) => {
            callCount += 1;
            if (command === 'ip addr show') {
                throw errors_1.MonitorError.createCommandFailed('linux', command, { reason: 'ip missing' });
            }
            throw errors_1.MonitorError.createCommandFailed('linux', command, { reason: 'ifconfig missing' });
        };
        try {
            await adapter.getNetworkInterfaces();
            chai_1.expect.fail('应该抛出 MonitorError');
        }
        catch (error) {
            (0, chai_1.expect)(error).to.be.instanceOf(errors_1.MonitorError);
            (0, chai_1.expect)(error.details.primary.details.reason).to.equal('ip missing');
            (0, chai_1.expect)(error.details.fallback.details.reason).to.equal('ifconfig missing');
        }
        (0, chai_1.expect)(callCount).to.equal(2);
    });
    it('容器环境应禁用系统服务能力', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        internal.containerMode = true;
        internal.supportedFeatures.system.services = false;
        internal.executeCommand = async () => {
            throw new Error('systemctl should not be called in container mode');
        };
        (0, chai_1.expect)(adapter.getSupportedFeatures().system.services).to.be.false;
        try {
            await adapter.getSystemServices();
            chai_1.expect.fail('should throw MonitorError');
        }
        catch (error) {
            (0, chai_1.expect)(error).to.be.instanceOf(errors_1.MonitorError);
            (0, chai_1.expect)(error.code).to.equal(errors_1.ErrorCode.PLATFORM_NOT_SUPPORTED);
        }
    });
    it('T022: getCPUInfo() 在读取 /proc/cpuinfo 失败时应降级到 os.cpus() 数据而非抛出异常', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        // stub readFile 使 /proc/cpuinfo 读取失败
        internal.readFile = async () => {
            throw new errors_1.MonitorError('/proc/cpuinfo 不可访问', errors_1.ErrorCode.COMMAND_FAILED, 'linux');
        };
        const result = await adapter.getCPUInfo();
        (0, chai_1.expect)(result).to.be.an('object');
        (0, chai_1.expect)(result.cores).to.be.a('number').and.to.be.greaterThan(0);
        (0, chai_1.expect)(result.threads).to.be.a('number').and.to.be.greaterThan(0);
        (0, chai_1.expect)(result.model).to.be.a('string').and.to.have.length.greaterThan(0);
    });
    // ——— #37 修复：df 遇到无权限挂载点时不应整体失败 ———
    it('getDiskInfo() 在 df 遇到权限错误但 stdout 有效时应正常返回磁盘列表', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        adapter.executeCommand = async () => ({
            stdout: [
                'Filesystem      Size  Used Avail Use% Mounted on',
                '/dev/sda1        50G   20G   30G  40% /',
                '/dev/sdb1       100G   60G   40G  60% /data'
            ].join('\n'),
            stderr: 'df: /run/user/1000/doc: Operation not permitted',
            exitCode: 1,
            platform: 'linux',
            executionTime: 5,
            command: 'df -h'
        });
        const result = await adapter.getDiskInfo();
        (0, chai_1.expect)(result).to.be.an('array').with.lengthOf(2);
        (0, chai_1.expect)(result[0].mountpoint).to.equal('/');
        (0, chai_1.expect)(result[1].mountpoint).to.equal('/data');
    });
    it('getDiskInfo() 在 df stdout 为空时应抛出错误', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        adapter.executeCommand = async () => ({
            stdout: '',
            stderr: 'df: command not found',
            exitCode: 127,
            platform: 'linux',
            executionTime: 0,
            command: 'df -h'
        });
        try {
            await adapter.getDiskInfo();
            chai_1.expect.fail('应该抛出 MonitorError');
        }
        catch (error) {
            (0, chai_1.expect)(error).to.be.instanceOf(errors_1.MonitorError);
        }
    });
    it('getDiskUsage() 在 df 遇到权限错误但 stdout 有效时应正常返回磁盘列表', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        adapter.executeCommand = async () => ({
            stdout: [
                'Filesystem     1B-blocks       Used  Available Use% Mounted on',
                '/dev/sda1     53687091200 21474836480 32212254720  40% /',
                '/dev/sdb1    107374182400 64424509440 42949672960  60% /data'
            ].join('\n'),
            stderr: 'df: /run/user/1000/doc: Operation not permitted',
            exitCode: 1,
            platform: 'linux',
            executionTime: 5,
            command: 'df -B1'
        });
        const result = await adapter.getDiskUsage();
        (0, chai_1.expect)(result).to.be.an('array').with.lengthOf(2);
        (0, chai_1.expect)(result[0].mountPoint).to.equal('/');
        (0, chai_1.expect)(result[1].mountPoint).to.equal('/data');
        (0, chai_1.expect)(result[0].usagePercentage).to.equal(40);
    });
    it('getDiskUsage() 在 df stdout 为空时应抛出错误', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        adapter.executeCommand = async () => ({
            stdout: '',
            stderr: 'df: command not found',
            exitCode: 127,
            platform: 'linux',
            executionTime: 0,
            command: 'df -B1'
        });
        try {
            await adapter.getDiskUsage();
            chai_1.expect.fail('应该抛出 MonitorError');
        }
        catch (error) {
            (0, chai_1.expect)(error).to.be.instanceOf(errors_1.MonitorError);
        }
    });
    it('应在 ss 不可用时回退到 netstat 解析连接', async () => {
        const adapter = new linux_adapter_1.LinuxAdapter();
        const internal = adapter;
        const commands = [];
        internal.executeCommand = async (command) => {
            commands.push(command);
            if (command === 'ss -tuln') {
                throw errors_1.MonitorError.createCommandFailed('linux', command, { reason: 'missing ss' });
            }
            if (command === 'netstat -tuln') {
                return {
                    stdout: 'Proto Recv-Q Send-Q Local Address           Foreign Address         State\n' +
                        'tcp   0      0 0.0.0.0:22              0.0.0.0:*               LISTEN\n',
                    stderr: '',
                    exitCode: 0,
                    platform: 'linux',
                    executionTime: 1,
                    command
                };
            }
            return {
                stdout: '',
                stderr: 'unsupported',
                exitCode: 1,
                platform: 'linux',
                executionTime: 0,
                command
            };
        };
        const results = await adapter.getNetworkConnections();
        (0, chai_1.expect)(results).to.have.lengthOf(1);
        (0, chai_1.expect)(results[0].protocol).to.equal('tcp');
        (0, chai_1.expect)(results[0].state).to.equal('listen');
        (0, chai_1.expect)(results[0].localAddress).to.equal('0.0.0.0:22');
        (0, chai_1.expect)(results[0].foreignAddress).to.equal('0.0.0.0:*');
        (0, chai_1.expect)(commands).to.deep.equal(['ss -tuln', 'netstat -tuln']);
    });
});
//# sourceMappingURL=linux-adapter.test.js.map