--!nonstrict
local Util = require("format/util")
describe("format/util · busca", function()
test("termo vazio ou nil sempre casa (a busca é filtro, não requisito)", function()
expect(Util.matches("", { "qualquer" })):toBe(true)
expect(Util.matches(nil, {})):toBe(true)
end)
test("casa por trecho, sem diferenciar caixa do campo", function()
expect(Util.matches("api", { "Minha-API-gateway" })):toBe(true)
expect(Util.matches("worker", { "api", "db" })):toBe(false)
end)
test("o termo é literal, não padrão Lua", function()
expect(Util.matches("a.b", { "a.b" })):toBe(true)
expect(Util.matches("a.b", { "axb" })):toBe(false)
expect(Util.matches("%", { "100%" })):toBe(true)
end)
test("campos que não são texto são ignorados", function()
expect(Util.matches("8080", { 8080, nil, true })):toBe(false)
expect(Util.matches("8080", { 8080, "porta 8080" })):toBe(true)
end)
end)
describe("format/util · texto", function()
test("ellipsis não mexe no que cabe", function()
expect(Util.ellipsis("curto", 10)):toBe("curto")
expect(Util.ellipsis("exato", 5)):toBe("exato")
expect(Util.ellipsis(nil, 5)):toBe("")
end)
test("ellipsis corta em caracteres, não em bytes, e anexa …", function()
expect(Util.ellipsis("abcdefgh", 3)):toBe("abc…")
expect(Util.ellipsis("ação-longa", 4)):toBe("ação…")
end)
test("short_reason fica com a primeira linha não vazia, aparada", function()
expect(Util.short_reason("\n\n falhou o build \nlinha dois")):toBe("falhou o build")
end)
test("short_reason trunca no limite pedido e tem padrão de 140", function()
expect(Util.short_reason("abcdefghij", 4)):toBe("abcd…")
local longa = string.rep("x", 200)
expect(utf8.len(Util.short_reason(longa))):toBe(141)
end)
test("short_reason devolve vazio sem motivo", function()
expect(Util.short_reason(nil)):toBe("")
expect(Util.short_reason(42)):toBe("")
expect(Util.short_reason(" \n \r\n")):toBe("")
end)
test("strip_ansi tira cor, cursor, OSC, CR e NUL", function()
expect(Util.strip_ansi("\27[2m\27[32mok\27[0m")):toBe("ok")
expect(Util.strip_ansi("a\27[2Kb\27[1;5Hc")):toBe("abc")
expect(Util.strip_ansi("\27]0;título\7texto")):toBe("texto")
expect(Util.strip_ansi("linha\r\0")):toBe("linha")
expect(Util.strip_ansi(nil)):toBe("")
end)
test("strip_ansi não mexe em texto limpo", function()
expect(Util.strip_ansi("sem [colchetes] nem ESC")):toBe("sem [colchetes] nem ESC")
end)
end)
describe("format/util · arrays", function()
test("encode_array força [] quando vazio (tabela vazia viraria {})", function()
expect(Util.encode_array({})):toBe("[]")
expect(json.encode({})).never:toBe("[]")
end)
test("encode_array codifica a lista quando há itens", function()
local back = json.decode(Util.encode_array({ { a = 1 }, { a = 2 } }))
expect(#back):toBe(2)
expect(back[2].a):toBe(2)
end)
end)
describe("format/util · estados", function()
test("status_label_color e status_kind concordam em todos os estados", function()
local casos = {
{ "Running", "Running", "ok" },
{ "Deploying", "Deploying", "info" },
{ "Queued", "Na fila", "muted" },
{ "Degraded", "Degraded", "warn" },
{ "Stopping", "Stopping", "muted" },
{ "Stopped", "Stopped", "muted" },
}
for _, caso in casos do
local label = Util.status_label_color(caso[1])
expect(label):toBe(caso[2])
expect(Util.status_kind(caso[1])):toBe(caso[3])
end
end)
test("status com erro (tabela) ou desconhecido cai em Error/bad", function()
local label, cor = Util.status_label_color({ Error = "boom" })
expect(label):toBe("Error")
expect(cor):toBe("#F85149")
expect(Util.status_kind({ Error = "boom" })):toBe("bad")
expect(Util.status_kind("Qualquer")):toBe("bad")
expect(Util.status_kind(nil)):toBe("bad")
end)
test("estado de deploy: rótulo, cor e kind", function()
local label, cor = Util.state_label_color("Live")
expect(label):toBe("LIVE")
expect(cor):toBe("#3FB950")
expect(Util.state_label_color("Failed")):toBe("FAILED")
expect(Util.state_label_color("PreDeployCheck")):toBe("PRÉ-DEPLOY CHECK")
expect(Util.state_label_color("BuildingImage")):toBe("BUILDING")
expect(Util.state_label_color(nil)):toBe("BUILDING")
expect(Util.state_kind("Live")):toBe("ok")
expect(Util.state_kind("Failed")):toBe("bad")
expect(Util.state_kind("Stopped")):toBe("muted")
expect(Util.state_kind("BuildingImage")):toBe("info")
end)
test("in_use_kind", function()
expect(Util.in_use_kind(true)):toBe("ok")
expect(Util.in_use_kind(false)):toBe("muted")
end)
test("container_state normaliza a caixa e classifica", function()
local rotulo, kind = Util.container_state("RUNNING")
expect(rotulo):toBe("running")
expect(kind):toBe("ok")
expect(select(2, Util.container_state("restarting"))):toBe("info")
expect(select(2, Util.container_state("paused"))):toBe("warn")
expect(select(2, Util.container_state("created"))):toBe("info")
expect(select(2, Util.container_state("dead"))):toBe("bad")
end)
test("container_state: parado ou desconhecido é muted; vazio vira exited", function()
local rotulo, kind = Util.container_state("removing")
expect(rotulo):toBe("removing")
expect(kind):toBe("muted")
expect(Util.container_state(nil)):toBe("exited")
expect(Util.container_state("")):toBe("exited")
end)
test("container_stopped: só ativo (running/restarting/paused) não é removível", function()
expect(Util.container_stopped("running")):toBe(false)
expect(Util.container_stopped("Restarting")):toBe(false)
expect(Util.container_stopped("paused")):toBe(false)
expect(Util.container_stopped("exited")):toBe(true)
expect(Util.container_stopped(nil)):toBe(true)
end)
end)
describe("format/util · domínio do serviço", function()
test("source_summary de cada origem", function()
expect(Util.source_summary({ Registry = { image = "nginx:1" } })):toBe("nginx:1")
expect(Util.source_summary({ Git = { url = "https://x/r.git", branch = "main" } })):toBe("https://x/r.git @ main")
expect(Util.source_summary({ Archive = { original_filename = "app.zip" } })):toBe("app.zip")
expect(Util.source_summary({ Archive = { archive_id = "a1" } })):toBe("a1")
expect(Util.source_summary({ Archive = {} })):toBe("zip enviado")
expect(Util.source_summary({ Compose = {} })):toBe("docker-compose")
end)
test("source_summary de origem ausente ou desconhecida é —", function()
expect(Util.source_summary(nil)):toBe("—")
expect(Util.source_summary("Git")):toBe("—")
expect(Util.source_summary({})):toBe("—")
expect(Util.source_summary({ Registry = {} })):toBe("—")
end)
test("domain_routes usa a lista nova e converte tls para booleano", function()
local rotas = Util.domain_routes({
domains = { { domain = "a.com", port = 80, tls = true }, { domain = "b.com" } },
} :: any)
expect(#rotas):toBe(2)
expect(rotas[1].tls):toBe(true)
expect(rotas[2].tls):toBe(false)
expect(rotas[1].port):toBe(80)
end)
test("domain_routes cai no domain/tls_enabled legado", function()
local rotas = Util.domain_routes({ domain = "velho.com", tls_enabled = true } :: any)
expect(#rotas):toBe(1)
expect(rotas[1].domain):toBe("velho.com")
expect(rotas[1].tls):toBe(true)
expect(rotas[1].port):toBeNil()
end)
test("a lista nova vence o legado, e vazio não gera rota", function()
local rotas = Util.domain_routes({ domains = { { domain = "novo.com" } }, domain = "velho.com" } :: any)
expect(#rotas):toBe(1)
expect(rotas[1].domain):toBe("novo.com")
expect(#Util.domain_routes({ domain = "" } :: any)):toBe(0)
expect(#Util.domain_routes({} :: any)):toBe(0)
end)
test("pre_deploy_checks: a fila nova vence o id legado", function()
expect(Util.pre_deploy_checks({ pre_deploy_job_ids = { "a", "b" }, pre_deploy_job_id = "z" })):toEqual({ "a", "b" })
expect(Util.pre_deploy_checks({ pre_deploy_job_ids = {}, pre_deploy_job_id = "z" })):toEqual({ "z" })
expect(Util.pre_deploy_checks({ pre_deploy_job_id = "" })):toEqual({})
expect(Util.pre_deploy_checks({})):toEqual({})
end)
test("service_pair_list normaliza o snapshot", function()
local pares = Util.service_pair_list({
{ service = { name = "api" }, project_name = "loja" },
{ service = { name = "db" } },
})
expect(#pares):toBe(2)
expect(pares[1].proj):toBe("loja")
expect(pares[2].proj):toBe("")
expect(#Util.service_pair_list(nil)):toBe(0)
end)
end)
describe("format/util · orçamento de colunas", function()
test("segue os breakpoints 560/900 da largura da janela", function()
local largura = viewport().width
local b = Util.column_budgets()
if largura < 560 then
expect(b.svc):toBe(12)
elseif largura < 900 then
expect(b.svc):toBe(17)
else
expect(b.svc):toBe(28)
end
for _, campo in { "svc", "proj", "state", "img" } do
expect(type((b :: any)[campo])):toBe("number")
end
end)
end)