local createLibTest = require('/apis/libtest'); local testlib = createLibTest({ ... }); local function containsLine(lines, expected) for _, line in ipairs(lines) do if line == expected then return true; end end return false; end local function containsText(lines, expected) for _, line in ipairs(lines) do if string.find(line, expected, 1, true) then return true; end end return false; end local function fakeSettings(values) return { values = values or {}, saveCount = 0, get = function(self, key) return self.values[key]; end, set = function(self, key, value) self.values[key] = value; end, save = function(self) self.saveCount = self.saveCount + 1; return true; end, }; end local function runCloud(args, opts) opts = opts or {}; local lines = {}; local writes = {}; local readMasks = {}; local cloudRequests = {}; local settingsLib = opts.settings or fakeSettings(); local readValues = opts.readValues or {}; local env = setmetatable({ print = function(line) lines[#lines + 1] = line or ''; end, write = function(text) writes[#writes + 1] = text or ''; end, read = function(mask) readMasks[#readMasks + 1] = mask; local value = readValues[#readMasks]; if value == nil then return ''; end return value; end, settings = { get = function(key) return settingsLib:get(key); end, set = function(key, value) return settingsLib:set(key, value); end, save = function() return settingsLib:save(); end, }, require = function(path) if path == '/apis/libcloud' then return function() return { request = function(msgType, payload, requestOpts) cloudRequests[#cloudRequests + 1] = { msgType = msgType, payload = payload, opts = requestOpts, }; return opts.requestOk ~= false, opts.requestResult or { serverOk = true, opencodeOk = true }; end, }; end; end if path == '/apis/libversion' then return function() return { forSelf = function() return '0.0.0-test'; end }; end; end return require(path); end, }, { __index = _G }); local chunk, loadErr = loadfile('/programs/cloud.lua', 't', env); if not chunk then error(loadErr, 0); end local ok, err = pcall(chunk, table.unpack(args or {})); if not ok then error(err, 0); end return { lines = lines, writes = writes, readMasks = readMasks, cloudRequests = cloudRequests, settings = settingsLib, }; end testlib.test('cloud help lists status and set-password without health', function() local ctx = runCloud({ '--help' }); testlib.assertTrue(containsLine(ctx.lines, ' cloud status')); testlib.assertTrue(containsLine(ctx.lines, ' cloud set-password [--force|-f]')); testlib.assertTrue(not containsLine(ctx.lines, ' cloud health')); end); testlib.test('cloud health is not kept as an alias', function() local ctx = runCloud({ 'health' }, { settings = fakeSettings({ ['cloud.url'] = 'wss://example.test' }), }); testlib.assertEquals(#ctx.cloudRequests, 0); testlib.assertTrue(containsLine(ctx.lines, 'cloud usage:')); end); testlib.test('cloud status probes the server', function() local ctx = runCloud({ 'status' }, { settings = fakeSettings({ ['cloud.url'] = 'wss://example.test' }), requestResult = { serverOk = true, opencodeOk = false, opencodeDetail = 'missing key' }, }); testlib.assertEquals(#ctx.cloudRequests, 1); testlib.assertEquals(ctx.cloudRequests[1].msgType, 'probe_server'); testlib.assertTrue(containsLine(ctx.lines, 'serverOk: true')); testlib.assertTrue(containsLine(ctx.lines, 'opencodeOk: false')); testlib.assertTrue(containsLine(ctx.lines, 'opencode: missing key')); end); testlib.test('cloud set-password stores a missing password with masked input', function() local settingsLib = fakeSettings(); local ctx = runCloud({ 'set-password' }, { settings = settingsLib, readValues = { 'hunter2' }, }); testlib.assertEquals(ctx.writes[1], 'Password: '); testlib.assertEquals(ctx.readMasks[1], '*'); testlib.assertEquals(settingsLib.values['cloud.password'], 'hunter2'); testlib.assertEquals(settingsLib.saveCount, 1); testlib.assertTrue(containsLine(ctx.lines, 'cloud.password set')); end); testlib.test('cloud set-password rejects replacing without force', function() local settingsLib = fakeSettings({ ['cloud.password'] = 'old' }); local ctx = runCloud({ 'set-password' }, { settings = settingsLib, readValues = { 'new' }, }); testlib.assertEquals(#ctx.readMasks, 0); testlib.assertEquals(settingsLib.values['cloud.password'], 'old'); testlib.assertEquals(settingsLib.saveCount, 0); testlib.assertTrue(containsText(ctx.lines, 'use cloud set-password --force')); end); testlib.test('cloud set-password --force replaces an existing password', function() local settingsLib = fakeSettings({ ['cloud.password'] = 'old' }); runCloud({ 'set-password', '--force' }, { settings = settingsLib, readValues = { 'new' }, }); testlib.assertEquals(settingsLib.values['cloud.password'], 'new'); testlib.assertEquals(settingsLib.saveCount, 1); end); testlib.test('cloud set-password -f replaces an existing password', function() local settingsLib = fakeSettings({ ['cloud.password'] = 'old' }); runCloud({ 'set-password', '-f' }, { settings = settingsLib, readValues = { 'new' }, }); testlib.assertEquals(settingsLib.values['cloud.password'], 'new'); testlib.assertEquals(settingsLib.saveCount, 1); end); testlib.test('cloud set-password rejects an empty password', function() local settingsLib = fakeSettings(); local ctx = runCloud({ 'set-password' }, { settings = settingsLib, readValues = { '' }, }); testlib.assertEquals(settingsLib.values['cloud.password'], nil); testlib.assertEquals(settingsLib.saveCount, 0); testlib.assertTrue(containsLine(ctx.lines, 'cloud.password not changed: password cannot be empty')); end); testlib.run();