// The zsh module's shape (novox/hq ADR 0203, ADR 0204, to-be 41 WP3): it claims the mesh's // node-login-shell and declares no seat; its ~/.zshrc block goes at the start and holds the three // slots in order; its ~/.zshenv block sources the path node-environment fixes and nothing else; its // environment is a contribution, never an export in its own lines. import { test } from "node:test"; import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; const m = JSON.parse(readFileSync(new URL("../module.json", import.meta.url), "utf8")); const resource = (id: string) => m.resources.find((r: { id: string }) => r.id === id); test("it claims node-login-shell serving execute, and declares no seat", () => { assert.equal(m.seats, undefined); assert.deepEqual(m.claims, [{ name: "node-login-shell", scope: "node", serves: ["execute"] }]); }); test("the .zshrc block is at the start and holds the three slots in order, after the defaults' header", () => { const rc = resource("rc"); assert.equal(rc.path, "${machine:account-home}/.zshrc"); assert.equal(rc.into, "block"); assert.equal(rc.at, "start"); const c: string = rc.content; const first = c.indexOf("${shell:zsh:first}"); const normal = c.indexOf("${shell:zsh:normal}"); const last = c.indexOf("${shell:zsh:last}"); assert.ok(c.startsWith("# The mesh's block"), "it opens by saying whose it is"); assert.ok(first > 0 && first < normal && normal < last, "first, normal, last, in that order"); assert.ok(c.indexOf("set_terminal_title") > first && c.indexOf("sudo-disksize") < normal, "the defaults sit between first and normal"); for (const slot of ["first", "normal", "last"]) assert.equal(c.split(`\${shell:zsh:${slot}}`).length, 2, `${slot} appears once`); }); test("the .zshrc block carries the shared defaults and none of the operator's own lines", () => { const c: string = resource("rc").content; for (const want of ["precmd_functions+=(set_terminal_title)", "alias egrep='egrep --color=auto'", "alias ll='ls -alhF'", "alias drun=", "disksize()", "sudo-disksize()"]) { assert.ok(c.includes(want), want); } assert.equal((c.match(/^bindkey /gm) ?? []).length, 7); for (const not of ["export ", "p10k", "powerlevel10k", "nvm", ".dotnet", "MY_KV_PATH", "MY_LIB_PATH", "CLAUDE_CODE_DISABLE_TERMINAL_TITLE", "killport", "findport", ".zshrc.local", "zsh-autosuggestions", "zsh-syntax-highlighting"]) { assert.ok(!c.includes(not), `the block holds no ${not}`); } }); test("the .zshenv block sources exactly the environment file node-environment fixes", () => { const env = resource("env"); assert.equal(env.path, "${machine:account-home}/.zshenv"); assert.equal(env.into, "block"); assert.equal(env.at, "start"); const sourced = [...(env.content as string).matchAll(/^\s*(?:source|\.)\s+"?([^"\s]+)"?/gm)].map((x) => x[1]); assert.deepEqual(sourced, ["${machine:account-home}/.config/mesh/environment.sh"]); }); test("its environment is a contribution: the editor, the configuration home, and three PATH entries at the start", () => { assert.deepEqual(m.environment.variables, { EDITOR: "vim", VISUAL: "vim", XDG_CONFIG_HOME: "${machine:account-home}/.config" }); assert.deepEqual(m.environment.path, [ { entry: "${machine:account-home}/.local/bin", at: "start" }, { entry: "${machine:account-home}/scripts", at: "start" }, { entry: "${machine:account-home}/scripts/bin", at: "start" }, ]); }); test("it keeps the package and sets the login shell through the user shape", () => { assert.deepEqual(resource("package"), { id: "package", type: "package", package: "zsh" }); assert.deepEqual(resource("login"), { id: "login", type: "user", name: "${machine:account}", shell: "/usr/bin/zsh" }); assert.ok(m.capabilities.includes("package-manager")); }); test("the tools bundle registers the seat's verb under node-login-shell", () => { const src = readFileSync(new URL("../tools/index.ts", import.meta.url), "utf8"); assert.match(src, /registerModuleTools\("node-login-shell", seatVerbs\)/); assert.doesNotMatch(src, /runuser|"login-shell"/); });