typescript-virtual-container 1.4.3 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/.vscode/settings.json +2 -1
  2. package/README.md +137 -19
  3. package/benchmark-results.txt +21 -21
  4. package/builds/self-standalone.js +322 -266
  5. package/builds/self-standalone.js.map +4 -4
  6. package/builds/standalone-wo-sftp.js +268 -212
  7. package/builds/standalone-wo-sftp.js.map +4 -4
  8. package/builds/standalone.js +268 -212
  9. package/builds/standalone.js.map +4 -4
  10. package/builds/web-full-api.min.js +5 -5
  11. package/builds/web-full-api.min.js.map +3 -3
  12. package/builds/web.min.js +5 -5
  13. package/builds/web.min.js.map +3 -3
  14. package/bun.lock +101 -6
  15. package/dist/Honeypot/index.js +1 -0
  16. package/dist/Honeypot/index.js.map +1 -0
  17. package/dist/SSHClient/index.js +1 -0
  18. package/dist/SSHClient/index.js.map +1 -0
  19. package/dist/SSHMimic/exec.js +1 -0
  20. package/dist/SSHMimic/exec.js.map +1 -0
  21. package/dist/SSHMimic/executor.js +1 -0
  22. package/dist/SSHMimic/executor.js.map +1 -0
  23. package/dist/SSHMimic/hostKey.js +1 -0
  24. package/dist/SSHMimic/hostKey.js.map +1 -0
  25. package/dist/SSHMimic/index.js +1 -0
  26. package/dist/SSHMimic/index.js.map +1 -0
  27. package/dist/SSHMimic/loginBanner.js +1 -0
  28. package/dist/SSHMimic/loginBanner.js.map +1 -0
  29. package/dist/SSHMimic/loginFormat.js +1 -0
  30. package/dist/SSHMimic/loginFormat.js.map +1 -0
  31. package/dist/SSHMimic/prompt.js +1 -0
  32. package/dist/SSHMimic/prompt.js.map +1 -0
  33. package/dist/SSHMimic/sftp.js +1 -0
  34. package/dist/SSHMimic/sftp.js.map +1 -0
  35. package/dist/VirtualFileSystem/binaryPack.js +1 -0
  36. package/dist/VirtualFileSystem/binaryPack.js.map +1 -0
  37. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  38. package/dist/VirtualFileSystem/index.js +1 -0
  39. package/dist/VirtualFileSystem/index.js.map +1 -0
  40. package/dist/VirtualFileSystem/internalTypes.js +1 -0
  41. package/dist/VirtualFileSystem/internalTypes.js.map +1 -0
  42. package/dist/VirtualFileSystem/path.js +1 -0
  43. package/dist/VirtualFileSystem/path.js.map +1 -0
  44. package/dist/VirtualPackageManager/index.js +1 -0
  45. package/dist/VirtualPackageManager/index.js.map +1 -0
  46. package/dist/VirtualShell/index.js +1 -0
  47. package/dist/VirtualShell/index.js.map +1 -0
  48. package/dist/VirtualShell/shell.js +1 -0
  49. package/dist/VirtualShell/shell.js.map +1 -0
  50. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  51. package/dist/VirtualShell/shellParser.js +3 -1
  52. package/dist/VirtualShell/shellParser.js.map +1 -0
  53. package/dist/VirtualUserManager/index.js +1 -0
  54. package/dist/VirtualUserManager/index.js.map +1 -0
  55. package/dist/commands/adduser.js +1 -0
  56. package/dist/commands/adduser.js.map +1 -0
  57. package/dist/commands/alias.js +1 -0
  58. package/dist/commands/alias.js.map +1 -0
  59. package/dist/commands/apt.js +1 -0
  60. package/dist/commands/apt.js.map +1 -0
  61. package/dist/commands/awk.d.ts.map +1 -1
  62. package/dist/commands/awk.js +2 -2
  63. package/dist/commands/awk.js.map +1 -0
  64. package/dist/commands/base64.js +1 -0
  65. package/dist/commands/base64.js.map +1 -0
  66. package/dist/commands/cat.js +1 -0
  67. package/dist/commands/cat.js.map +1 -0
  68. package/dist/commands/cd.js +3 -2
  69. package/dist/commands/cd.js.map +1 -0
  70. package/dist/commands/chmod.js +1 -0
  71. package/dist/commands/chmod.js.map +1 -0
  72. package/dist/commands/clear.js +1 -0
  73. package/dist/commands/clear.js.map +1 -0
  74. package/dist/commands/command-helpers.js +1 -0
  75. package/dist/commands/command-helpers.js.map +1 -0
  76. package/dist/commands/cp.js +1 -0
  77. package/dist/commands/cp.js.map +1 -0
  78. package/dist/commands/curl.js +1 -0
  79. package/dist/commands/curl.js.map +1 -0
  80. package/dist/commands/cut.js +1 -0
  81. package/dist/commands/cut.js.map +1 -0
  82. package/dist/commands/date.js +1 -0
  83. package/dist/commands/date.js.map +1 -0
  84. package/dist/commands/declare.js +1 -0
  85. package/dist/commands/declare.js.map +1 -0
  86. package/dist/commands/deluser.js +1 -0
  87. package/dist/commands/deluser.js.map +1 -0
  88. package/dist/commands/df.js +1 -0
  89. package/dist/commands/df.js.map +1 -0
  90. package/dist/commands/diff.js +1 -0
  91. package/dist/commands/diff.js.map +1 -0
  92. package/dist/commands/dpkg.js +1 -0
  93. package/dist/commands/dpkg.js.map +1 -0
  94. package/dist/commands/du.js +1 -0
  95. package/dist/commands/du.js.map +1 -0
  96. package/dist/commands/echo.js +1 -0
  97. package/dist/commands/echo.js.map +1 -0
  98. package/dist/commands/env.js +1 -0
  99. package/dist/commands/env.js.map +1 -0
  100. package/dist/commands/exit.js +1 -0
  101. package/dist/commands/exit.js.map +1 -0
  102. package/dist/commands/export.js +1 -0
  103. package/dist/commands/export.js.map +1 -0
  104. package/dist/commands/find.js +1 -0
  105. package/dist/commands/find.js.map +1 -0
  106. package/dist/commands/free.js +1 -0
  107. package/dist/commands/free.js.map +1 -0
  108. package/dist/commands/grep.js +1 -0
  109. package/dist/commands/grep.js.map +1 -0
  110. package/dist/commands/groups.js +1 -0
  111. package/dist/commands/groups.js.map +1 -0
  112. package/dist/commands/gzip.js +1 -0
  113. package/dist/commands/gzip.js.map +1 -0
  114. package/dist/commands/head.js +1 -0
  115. package/dist/commands/head.js.map +1 -0
  116. package/dist/commands/help.js +1 -0
  117. package/dist/commands/help.js.map +1 -0
  118. package/dist/commands/helpers.d.ts.map +1 -1
  119. package/dist/commands/helpers.js +4 -0
  120. package/dist/commands/helpers.js.map +1 -0
  121. package/dist/commands/history.js +1 -0
  122. package/dist/commands/history.js.map +1 -0
  123. package/dist/commands/hostname.js +1 -0
  124. package/dist/commands/hostname.js.map +1 -0
  125. package/dist/commands/htop.js +1 -0
  126. package/dist/commands/htop.js.map +1 -0
  127. package/dist/commands/id.js +1 -0
  128. package/dist/commands/id.js.map +1 -0
  129. package/dist/commands/index.js +1 -0
  130. package/dist/commands/index.js.map +1 -0
  131. package/dist/commands/kill.js +1 -0
  132. package/dist/commands/kill.js.map +1 -0
  133. package/dist/commands/ln.js +1 -0
  134. package/dist/commands/ln.js.map +1 -0
  135. package/dist/commands/ls.d.ts.map +1 -1
  136. package/dist/commands/ls.js +171 -37
  137. package/dist/commands/ls.js.map +1 -0
  138. package/dist/commands/lsb-release.js +1 -0
  139. package/dist/commands/lsb-release.js.map +1 -0
  140. package/dist/commands/man.js +1 -0
  141. package/dist/commands/man.js.map +1 -0
  142. package/dist/commands/mkdir.js +1 -0
  143. package/dist/commands/mkdir.js.map +1 -0
  144. package/dist/commands/mv.js +1 -0
  145. package/dist/commands/mv.js.map +1 -0
  146. package/dist/commands/nano.js +1 -0
  147. package/dist/commands/nano.js.map +1 -0
  148. package/dist/commands/neofetch.js +1 -0
  149. package/dist/commands/neofetch.js.map +1 -0
  150. package/dist/commands/node.js +1 -0
  151. package/dist/commands/node.js.map +1 -0
  152. package/dist/commands/npm.js +1 -0
  153. package/dist/commands/npm.js.map +1 -0
  154. package/dist/commands/passwd.js +1 -0
  155. package/dist/commands/passwd.js.map +1 -0
  156. package/dist/commands/ping.js +1 -0
  157. package/dist/commands/ping.js.map +1 -0
  158. package/dist/commands/printf.js +1 -0
  159. package/dist/commands/printf.js.map +1 -0
  160. package/dist/commands/ps.js +1 -0
  161. package/dist/commands/ps.js.map +1 -0
  162. package/dist/commands/pwd.js +1 -0
  163. package/dist/commands/pwd.js.map +1 -0
  164. package/dist/commands/python.js +1 -0
  165. package/dist/commands/python.js.map +1 -0
  166. package/dist/commands/read.js +1 -0
  167. package/dist/commands/read.js.map +1 -0
  168. package/dist/commands/registry.js +1 -0
  169. package/dist/commands/registry.js.map +1 -0
  170. package/dist/commands/rm.js +1 -0
  171. package/dist/commands/rm.js.map +1 -0
  172. package/dist/commands/runtime.d.ts.map +1 -1
  173. package/dist/commands/runtime.js +37 -0
  174. package/dist/commands/runtime.js.map +1 -0
  175. package/dist/commands/sed.js +1 -0
  176. package/dist/commands/sed.js.map +1 -0
  177. package/dist/commands/seq.js +1 -0
  178. package/dist/commands/seq.js.map +1 -0
  179. package/dist/commands/set.js +1 -0
  180. package/dist/commands/set.js.map +1 -0
  181. package/dist/commands/sh.d.ts.map +1 -1
  182. package/dist/commands/sh.js +9 -4
  183. package/dist/commands/sh.js.map +1 -0
  184. package/dist/commands/shift.js +1 -0
  185. package/dist/commands/shift.js.map +1 -0
  186. package/dist/commands/sleep.js +1 -0
  187. package/dist/commands/sleep.js.map +1 -0
  188. package/dist/commands/sort.js +1 -0
  189. package/dist/commands/sort.js.map +1 -0
  190. package/dist/commands/source.js +1 -0
  191. package/dist/commands/source.js.map +1 -0
  192. package/dist/commands/stat.js +1 -0
  193. package/dist/commands/stat.js.map +1 -0
  194. package/dist/commands/su.js +1 -0
  195. package/dist/commands/su.js.map +1 -0
  196. package/dist/commands/sudo.js +1 -0
  197. package/dist/commands/sudo.js.map +1 -0
  198. package/dist/commands/tail.js +1 -0
  199. package/dist/commands/tail.js.map +1 -0
  200. package/dist/commands/tar.js +1 -0
  201. package/dist/commands/tar.js.map +1 -0
  202. package/dist/commands/tee.js +1 -0
  203. package/dist/commands/tee.js.map +1 -0
  204. package/dist/commands/test.d.ts.map +1 -1
  205. package/dist/commands/test.js +1 -0
  206. package/dist/commands/test.js.map +1 -0
  207. package/dist/commands/touch.js +1 -0
  208. package/dist/commands/touch.js.map +1 -0
  209. package/dist/commands/tr.js +1 -0
  210. package/dist/commands/tr.js.map +1 -0
  211. package/dist/commands/tree.js +1 -0
  212. package/dist/commands/tree.js.map +1 -0
  213. package/dist/commands/true.js +1 -0
  214. package/dist/commands/true.js.map +1 -0
  215. package/dist/commands/type.js +1 -0
  216. package/dist/commands/type.js.map +1 -0
  217. package/dist/commands/uname.js +1 -0
  218. package/dist/commands/uname.js.map +1 -0
  219. package/dist/commands/uniq.js +1 -0
  220. package/dist/commands/uniq.js.map +1 -0
  221. package/dist/commands/unset.js +1 -0
  222. package/dist/commands/unset.js.map +1 -0
  223. package/dist/commands/uptime.js +1 -0
  224. package/dist/commands/uptime.js.map +1 -0
  225. package/dist/commands/wc.js +2 -1
  226. package/dist/commands/wc.js.map +1 -0
  227. package/dist/commands/wget.js +1 -0
  228. package/dist/commands/wget.js.map +1 -0
  229. package/dist/commands/which.js +1 -0
  230. package/dist/commands/which.js.map +1 -0
  231. package/dist/commands/who.js +1 -0
  232. package/dist/commands/who.js.map +1 -0
  233. package/dist/commands/whoami.js +1 -0
  234. package/dist/commands/whoami.js.map +1 -0
  235. package/dist/commands/xargs.js +1 -0
  236. package/dist/commands/xargs.js.map +1 -0
  237. package/dist/index.js +1 -0
  238. package/dist/index.js.map +1 -0
  239. package/dist/modules/linuxRootfs.d.ts +35 -17
  240. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  241. package/dist/modules/linuxRootfs.js +332 -152
  242. package/dist/modules/linuxRootfs.js.map +1 -0
  243. package/dist/modules/neofetch.js +1 -0
  244. package/dist/modules/neofetch.js.map +1 -0
  245. package/dist/modules/shellInteractive.js +1 -0
  246. package/dist/modules/shellInteractive.js.map +1 -0
  247. package/dist/modules/shellRuntime.js +1 -0
  248. package/dist/modules/shellRuntime.js.map +1 -0
  249. package/dist/self-standalone.js +1 -0
  250. package/dist/self-standalone.js.map +1 -0
  251. package/dist/standalone-wo-sftp.js +1 -0
  252. package/dist/standalone-wo-sftp.js.map +1 -0
  253. package/dist/standalone.js +1 -0
  254. package/dist/standalone.js.map +1 -0
  255. package/dist/types/commands.d.ts +1 -1
  256. package/dist/types/commands.d.ts.map +1 -1
  257. package/dist/types/commands.js +1 -0
  258. package/dist/types/commands.js.map +1 -0
  259. package/dist/types/pipeline.js +1 -0
  260. package/dist/types/pipeline.js.map +1 -0
  261. package/dist/types/streams.js +1 -0
  262. package/dist/types/streams.js.map +1 -0
  263. package/dist/types/vfs.js +1 -0
  264. package/dist/types/vfs.js.map +1 -0
  265. package/dist/utils/expand.d.ts +2 -2
  266. package/dist/utils/expand.d.ts.map +1 -1
  267. package/dist/utils/expand.js +336 -124
  268. package/dist/utils/expand.js.map +1 -0
  269. package/dist/utils/perfLogger.js +1 -0
  270. package/dist/utils/perfLogger.js.map +1 -0
  271. package/dist/utils/tokenize.js +1 -0
  272. package/dist/utils/tokenize.js.map +1 -0
  273. package/dist/utils/vfsDiff.js +1 -0
  274. package/dist/utils/vfsDiff.js.map +1 -0
  275. package/dist/web-api.js +1 -0
  276. package/dist/web-api.js.map +1 -0
  277. package/dist/web-full.js +1 -0
  278. package/dist/web-full.js.map +1 -0
  279. package/dist/web.js +1 -0
  280. package/dist/web.js.map +1 -0
  281. package/docs/.nojekyll +1 -0
  282. package/docs/assets/hierarchy.js +1 -0
  283. package/docs/assets/highlight.css +162 -0
  284. package/docs/assets/icons.js +18 -0
  285. package/docs/assets/icons.svg +1 -0
  286. package/docs/assets/main.js +60 -0
  287. package/docs/assets/navigation.js +1 -0
  288. package/docs/assets/search.js +1 -0
  289. package/docs/assets/style.css +1633 -0
  290. package/docs/classes/HoneyPot.html +31 -0
  291. package/docs/classes/SshClient.html +66 -0
  292. package/docs/classes/VirtualFileSystem.html +262 -0
  293. package/docs/classes/VirtualPackageManager.html +63 -0
  294. package/docs/classes/VirtualSftpServer.html +169 -0
  295. package/docs/classes/VirtualShell.html +265 -0
  296. package/docs/classes/VirtualSshServer.html +177 -0
  297. package/docs/classes/VirtualUserManager.html +276 -0
  298. package/docs/docs/.nojekyll +1 -0
  299. package/docs/docs/assets/hierarchy.js +1 -0
  300. package/docs/docs/assets/highlight.css +162 -0
  301. package/docs/docs/assets/icons.js +18 -0
  302. package/docs/docs/assets/icons.svg +1 -0
  303. package/docs/docs/assets/main.js +60 -0
  304. package/docs/docs/assets/navigation.js +1 -0
  305. package/docs/docs/assets/search.js +1 -0
  306. package/docs/docs/assets/style.css +1633 -0
  307. package/docs/docs/hierarchy.html +1 -0
  308. package/docs/docs/index.html +1842 -0
  309. package/docs/docs/media/LICENSE +21 -0
  310. package/docs/docs/modules.html +1 -0
  311. package/docs/functions/assertDiff.html +6 -0
  312. package/docs/functions/diffSnapshots.html +7 -0
  313. package/docs/functions/formatDiff.html +6 -0
  314. package/docs/functions/getArg.html +13 -0
  315. package/docs/functions/getFlag.html +15 -0
  316. package/docs/functions/ifFlag.html +11 -0
  317. package/docs/hierarchy.html +1 -0
  318. package/docs/index.html +1842 -0
  319. package/docs/interfaces/AuditLogEntry.html +6 -0
  320. package/docs/interfaces/CommandContext.html +22 -0
  321. package/docs/interfaces/CommandResult.html +26 -0
  322. package/docs/interfaces/ExecStream.html +11 -0
  323. package/docs/interfaces/HoneyPotStats.html +14 -0
  324. package/docs/interfaces/InstalledPackage.html +20 -0
  325. package/docs/interfaces/NanoEditorSession.html +8 -0
  326. package/docs/interfaces/PackageDefinition.html +30 -0
  327. package/docs/interfaces/PackageFile.html +8 -0
  328. package/docs/interfaces/RemoveOptions.html +4 -0
  329. package/docs/interfaces/ShellEnv.html +6 -0
  330. package/docs/interfaces/ShellModule.html +14 -0
  331. package/docs/interfaces/ShellProperties.html +14 -0
  332. package/docs/interfaces/ShellStream.html +11 -0
  333. package/docs/interfaces/SudoChallenge.html +24 -0
  334. package/docs/interfaces/VfsBaseNode.html +12 -0
  335. package/docs/interfaces/VfsDiff.html +10 -0
  336. package/docs/interfaces/VfsDiffEntry.html +6 -0
  337. package/docs/interfaces/VfsDiffModified.html +10 -0
  338. package/docs/interfaces/VfsDirectoryNode.html +15 -0
  339. package/docs/interfaces/VfsFileNode.html +17 -0
  340. package/docs/interfaces/VfsOptions.html +12 -0
  341. package/docs/interfaces/VfsSnapshot.html +3 -0
  342. package/docs/interfaces/VfsSnapshotBaseNode.html +8 -0
  343. package/docs/interfaces/VfsSnapshotDirectoryNode.html +10 -0
  344. package/docs/interfaces/VfsSnapshotFileNode.html +12 -0
  345. package/docs/interfaces/WriteFileOptions.html +6 -0
  346. package/docs/media/LICENSE +21 -0
  347. package/docs/modules.html +1 -0
  348. package/docs/types/CommandMode.html +2 -0
  349. package/docs/types/CommandOutcome.html +2 -0
  350. package/docs/types/VfsNodeStats.html +2 -0
  351. package/docs/types/VfsNodeType.html +2 -0
  352. package/docs/types/VfsPersistenceMode.html +5 -0
  353. package/docs/types/VfsSnapshotNode.html +2 -0
  354. package/examples/web.min.js +5 -5
  355. package/package.json +7 -4
  356. package/src/VirtualFileSystem/index.ts +11 -9
  357. package/src/VirtualShell/shellParser.ts +3 -2
  358. package/src/bun.d.ts +1 -0
  359. package/src/commands/awk.ts +1 -2
  360. package/src/commands/cd.ts +2 -2
  361. package/src/commands/helpers.ts +3 -0
  362. package/src/commands/ls.ts +210 -41
  363. package/src/commands/runtime.ts +56 -3
  364. package/src/commands/sh.ts +7 -4
  365. package/src/commands/test.ts +4 -2
  366. package/src/commands/wc.ts +1 -1
  367. package/src/modules/linuxRootfs.ts +420 -231
  368. package/src/types/commands.ts +1 -1
  369. package/src/utils/expand.ts +256 -76
  370. package/tests/command-helpers.test.ts +80 -0
  371. package/tests/commands-admin-net.test.ts +441 -0
  372. package/tests/commands-advanced.test.ts +456 -0
  373. package/tests/commands-core.test.ts +562 -0
  374. package/tests/commands-missing.test.ts +570 -0
  375. package/tests/commands-specific-units.test.ts +327 -0
  376. package/tests/commands-text-sys.test.ts +445 -0
  377. package/tests/expand.test.ts +170 -0
  378. package/tests/helpers.test.ts +75 -0
  379. package/tests/test-helper.ts +79 -0
  380. package/tsconfig.json +3 -0
  381. package/typedoc.json +8 -0
  382. package/tests/bun-test-shim.ts +0 -9
@@ -0,0 +1,441 @@
1
+ import { beforeAll, describe, expect, test } from "bun:test";
2
+ import { SshClient } from "../src";
3
+ import { createTestEnv, runCmd } from "./test-helper";
4
+
5
+ let client: InstanceType<typeof SshClient>;
6
+
7
+ beforeAll(async () => {
8
+ const env = await createTestEnv("test-admin");
9
+ client = env.client;
10
+ });
11
+
12
+ // ─── USERADD tests ────────────────────────────────────────────────────────
13
+
14
+ describe("useradd command", () => {
15
+ test("useradd creates user", async () => {
16
+ const r = await runCmd(client, "useradd -m testuser || echo 'created'");
17
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
18
+ });
19
+
20
+ test("useradd with comment", async () => {
21
+ const r = await runCmd(client, "useradd -c 'Test User' -m testuser2 || echo 'created'");
22
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
23
+ });
24
+
25
+ test("useradd with specific uid", async () => {
26
+ const r = await runCmd(client, "useradd -u 2000 -m testuser3 || echo 'created'");
27
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
28
+ });
29
+
30
+ test("useradd duplicate fails", async () => {
31
+ await runCmd(client, "useradd -m duplicate");
32
+ const r = await runCmd(client, "useradd -m duplicate");
33
+ expect(r.exitCode).not.toBe(0);
34
+ });
35
+
36
+ test("useradd non-root fails", async () => {
37
+ const env = await createTestEnv("test-admin-nonroot");
38
+ const nonrootClient = new SshClient(env.shell, "testuser");
39
+ const r = await runCmd(nonrootClient, "useradd -m newuser");
40
+ expect(r.exitCode).not.toBe(0);
41
+ });
42
+ });
43
+
44
+ // ─── DELUSER tests ────────────────────────────────────────────────────────
45
+
46
+ describe("deluser command", () => {
47
+ test("deluser removes user", async () => {
48
+ await runCmd(client, "useradd -m deltest 2>/dev/null || true");
49
+ const r = await runCmd(client, "deluser deltest 2>&1 || echo 'deleted'");
50
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
51
+ });
52
+
53
+ test("deluser with home removal", async () => {
54
+ await runCmd(client, "useradd -m delhome 2>/dev/null || true");
55
+ const r = await runCmd(client, "deluser --remove-home delhome 2>&1 || echo 'deleted'");
56
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
57
+ });
58
+
59
+ test("deluser non-existent user", async () => {
60
+ const r = await runCmd(client, "deluser nonexistent");
61
+ expect(r.exitCode).not.toBe(0);
62
+ });
63
+ });
64
+
65
+ // ─── PASSWD tests ────────────────────────────────────────────────────────
66
+
67
+ describe("passwd command", () => {
68
+ test("passwd requires root", async () => {
69
+ const r = await runCmd(client, "passwd root");
70
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
71
+ });
72
+
73
+ test("passwd non-existent user fails", async () => {
74
+ const r = await runCmd(client, "passwd nonexistent");
75
+ expect(r.exitCode).not.toBe(0);
76
+ });
77
+ });
78
+
79
+ // ─── SUDO tests ───────────────────────────────────────────────────────────
80
+
81
+ describe("sudo command", () => {
82
+ test("sudo as root runs command", async () => {
83
+ const r = await runCmd(client, "sudo whoami");
84
+ expect(r.exitCode).toBe(0);
85
+ expect(r.stdout?.trim()).toBe("root");
86
+ });
87
+
88
+ test("sudo -u changes user", async () => {
89
+ await runCmd(client, "useradd -m sudouser");
90
+ const r = await runCmd(client, "sudo -u sudouser whoami");
91
+ expect(r.exitCode).toBe(0);
92
+ expect(r.stdout?.trim()).toBe("sudouser");
93
+ });
94
+
95
+ test("sudo preserves args", async () => {
96
+ const r = await runCmd(client, "sudo echo test");
97
+ expect(r.exitCode).toBe(0);
98
+ expect(r.stdout?.trim()).toBe("test");
99
+ });
100
+ });
101
+
102
+ // ─── SU tests ──────────────────────────────────────────────────────────────
103
+
104
+ describe("su command", () => {
105
+ test("su as root", async () => {
106
+ const r = await runCmd(client, "su - -c whoami");
107
+ expect(r.exitCode).toBe(0);
108
+ });
109
+
110
+ test("su with command execution", async () => {
111
+ const r = await runCmd(client, "su - root -c 'echo hello'");
112
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
113
+ });
114
+ });
115
+
116
+ // ─── CURL tests ───────────────────────────────────────────────────────────
117
+
118
+ describe("curl command", () => {
119
+ test("curl basic request", async () => {
120
+ const r = await runCmd(client, "curl http://example.com 2>/dev/null | head -c 10 || echo 'response'");
121
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
122
+ });
123
+
124
+ test("curl localhost", async () => {
125
+ const r = await runCmd(client, "curl -s http://localhost:8080 2>/dev/null || echo 'no server'");
126
+ expect(r.exitCode).toBe(0);
127
+ });
128
+
129
+ test("curl invalid URL", async () => {
130
+ const r = await runCmd(client, "curl http://invalid.example.test 2>&1 | grep -i error || echo 'error'");
131
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
132
+ });
133
+
134
+ test("curl -o saves to file", async () => {
135
+ const r = await runCmd(client, "curl -s -o /tmp/curlout.html http://example.com 2>/dev/null || echo 'done'");
136
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
137
+ });
138
+
139
+ test("curl -I head request", async () => {
140
+ const r = await runCmd(client, "curl -I http://example.com 2>/dev/null | head -1 || echo 'header'");
141
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
142
+ });
143
+
144
+ test("curl with headers", async () => {
145
+ const r = await runCmd(client, "curl -H 'User-Agent: test' http://example.com 2>/dev/null | head -c 5 || echo 'data'");
146
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
147
+ });
148
+ });
149
+
150
+ // ─── WGET tests ───────────────────────────────────────────────────────────
151
+
152
+ describe("wget command", () => {
153
+ test("wget basic download", async () => {
154
+ const r = await runCmd(client, "wget -q http://example.com -O /tmp/wget.html 2>/dev/null || echo 'done'");
155
+ expect(r.exitCode).toBe(0);
156
+ });
157
+
158
+ test("wget -q quiet mode", async () => {
159
+ const r = await runCmd(client, "wget -q http://example.com -O /tmp/wgetq.html 2>/dev/null || echo 'done'");
160
+ expect(r.exitCode).toBe(0);
161
+ });
162
+
163
+ test("wget --spider check url", async () => {
164
+ const r = await runCmd(client, "wget --spider http://example.com 2>/dev/null || echo 'checked'");
165
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
166
+ });
167
+ });
168
+
169
+ // ─── PING tests ───────────────────────────────────────────────────────────
170
+
171
+ describe("ping command", () => {
172
+ test("ping localhost", async () => {
173
+ const r = await runCmd(client, "ping -c 1 localhost");
174
+ expect(r.exitCode).toBe(0);
175
+ expect(r.stdout).toContain("localhost");
176
+ });
177
+
178
+ test("ping -c count", async () => {
179
+ const r = await runCmd(client, "ping -c 2 127.0.0.1");
180
+ expect(r.exitCode).toBe(0);
181
+ });
182
+
183
+ test("ping invalid host", async () => {
184
+ const r = await runCmd(client, "ping -c 1 invalid.test 2>&1 || echo 'error'");
185
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
186
+ });
187
+
188
+ test("ping ipv4", async () => {
189
+ const r = await runCmd(client, "ping -c 1 127.0.0.1");
190
+ expect(r.exitCode).toBe(0);
191
+ expect(r.stdout).toContain("127.0.0.1");
192
+ });
193
+ });
194
+
195
+ // ─── NETSTAT-like tests ────────────────────────────────────────────────────
196
+
197
+ describe("network info commands", () => {
198
+ test("hostname shows system name", async () => {
199
+ const r = await runCmd(client, "hostname");
200
+ expect(r.exitCode).toBe(0);
201
+ expect(r.stdout?.length).toBeGreaterThan(0);
202
+ });
203
+
204
+ test("host lookup", async () => {
205
+ const r = await runCmd(client, "host localhost 2>/dev/null || echo 'no DNS'");
206
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
207
+ });
208
+
209
+ test("nslookup query", async () => {
210
+ const r = await runCmd(client, "nslookup localhost 2>/dev/null || echo 'no DNS'");
211
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
212
+ });
213
+ });
214
+
215
+ // ─── ADDUSER tests ────────────────────────────────────────────────────────
216
+
217
+ describe("adduser command", () => {
218
+ test("adduser creates user", async () => {
219
+ const r = await runCmd(client, "adduser -m -s /bin/bash newuser || echo 'created'");
220
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
221
+ });
222
+
223
+ test("adduser duplicate", async () => {
224
+ await runCmd(client, "adduser -m addup 2>/dev/null || true");
225
+ const r = await runCmd(client, "adduser -m addup 2>&1 || echo 'exists'");
226
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
227
+ });
228
+ });
229
+
230
+ // ─── DPKG tests ───────────────────────────────────────────────────────────
231
+
232
+ describe("dpkg command", () => {
233
+ test("dpkg -l lists packages", async () => {
234
+ const r = await runCmd(client, "dpkg -l");
235
+ expect(r.exitCode).toBe(0);
236
+ expect(r.stdout?.length).toBeGreaterThan(0);
237
+ });
238
+
239
+ test("dpkg -s check package", async () => {
240
+ const r = await runCmd(client, "dpkg -s bash 2>/dev/null || echo 'not found'");
241
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
242
+ });
243
+
244
+ test("dpkg status output", async () => {
245
+ const r = await runCmd(client, "dpkg -l | head");
246
+ expect(r.exitCode).toBe(0);
247
+ });
248
+ });
249
+
250
+ // ─── APT tests ────────────────────────────────────────────────────────────
251
+
252
+ describe("apt command", () => {
253
+ test("apt update list", async () => {
254
+ const r = await runCmd(client, "apt update 2>&1 | head -5");
255
+ expect(r.exitCode).toBe(0);
256
+ });
257
+
258
+ test("apt list packages", async () => {
259
+ const r = await runCmd(client, "apt list --installed 2>/dev/null | head");
260
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
261
+ });
262
+
263
+ test("apt search package", async () => {
264
+ const r = await runCmd(client, "apt search bash 2>&1 | grep -i bash || echo 'searched'");
265
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
266
+ });
267
+ });
268
+
269
+ // ─── NPM tests ────────────────────────────────────────────────────────────
270
+
271
+ describe("npm command", () => {
272
+ test("npm -v version", async () => {
273
+ const r = await runCmd(client, "npm -v");
274
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
275
+ });
276
+
277
+ test("npm list global", async () => {
278
+ const r = await runCmd(client, "npm list -g 2>/dev/null || npm list -g --depth 0 2>/dev/null || echo 'packages'");
279
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
280
+ });
281
+
282
+ test("npm help", async () => {
283
+ const r = await runCmd(client, "npm help 2>&1 | head");
284
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
285
+ });
286
+ });
287
+
288
+ // ─── PYTHON tests ────────────────────────────────────────────────────────
289
+
290
+ describe("python command", () => {
291
+ test("python -c executes code", async () => {
292
+ const r = await runCmd(client, "python -c 'print(1+1)'");
293
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
294
+ });
295
+
296
+ test("python -v version", async () => {
297
+ const r = await runCmd(client, "python --version 2>&1");
298
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
299
+ });
300
+ });
301
+
302
+ // ─── NODE tests ───────────────────────────────────────────────────────────
303
+
304
+ describe("node command", () => {
305
+ test("node -v version", async () => {
306
+ const r = await runCmd(client, "node -v");
307
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
308
+ });
309
+
310
+ test("node -e executes code", async () => {
311
+ const r = await runCmd(client, "node -e 'console.log(1+1)'");
312
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
313
+ });
314
+
315
+ test("node eval arithmetic", async () => {
316
+ const r = await runCmd(client, "node -e 'console.log(2*3)'");
317
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
318
+ if (r.stdout) expect(r.stdout).toContain("6");
319
+ });
320
+ });
321
+
322
+ // ─── WHICH tests ───────────────────────────────────────────────────────────
323
+
324
+ describe("which command", () => {
325
+ test("which finds command", async () => {
326
+ const r = await runCmd(client, "which ls");
327
+ expect(r.exitCode).toBe(0);
328
+ expect(r.stdout?.trim()).toMatch(/ls|bin/);
329
+ });
330
+
331
+ test("which multiple commands", async () => {
332
+ const r = await runCmd(client, "which ls cat grep");
333
+ expect(r.exitCode).toBe(0);
334
+ });
335
+
336
+ test("which non-existent", async () => {
337
+ const r = await runCmd(client, "which nonexistcommand");
338
+ expect(r.exitCode).not.toBe(0);
339
+ });
340
+ });
341
+
342
+ // ─── TYPE tests ───────────────────────────────────────────────────────────
343
+
344
+ describe("type command", () => {
345
+ test("type shows command type", async () => {
346
+ const r = await runCmd(client, "type ls");
347
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
348
+ });
349
+
350
+ test("type builtin", async () => {
351
+ const r = await runCmd(client, "type echo");
352
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
353
+ });
354
+
355
+ test("type alias", async () => {
356
+ const r = await runCmd(client, "alias myls=ls && type myls");
357
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
358
+ });
359
+ });
360
+
361
+ // ─── HELP tests ───────────────────────────────────────────────────────────
362
+
363
+ describe("help command", () => {
364
+ test("help lists commands", async () => {
365
+ const r = await runCmd(client, "help");
366
+ expect(r.exitCode).toBe(0);
367
+ expect(r.stdout?.length).toBeGreaterThan(0);
368
+ });
369
+
370
+ test("help specific command", async () => {
371
+ const r = await runCmd(client, "help echo");
372
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
373
+ });
374
+
375
+ test("help lists builtins", async () => {
376
+ const r = await runCmd(client, "help | wc -l");
377
+ expect(r.exitCode).toBe(0);
378
+ });
379
+ });
380
+
381
+ // ─── MAN tests ────────────────────────────────────────────────────────────
382
+
383
+ describe("man command", () => {
384
+ test("man ls shows manual", async () => {
385
+ const r = await runCmd(client, "man ls 2>/dev/null | head -5 || echo 'no man'");
386
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
387
+ });
388
+
389
+ test("man non-existent", async () => {
390
+ const r = await runCmd(client, "man nonexistent 2>&1");
391
+ expect(r.exitCode).not.toBe(0);
392
+ });
393
+ });
394
+
395
+ // ─── ALIAS tests ───────────────────────────────────────────────────────────
396
+
397
+ describe("alias command", () => {
398
+ test("alias creates shortcut", async () => {
399
+ const r = await runCmd(client, "alias myecho=echo && myecho hello");
400
+ expect(r.exitCode).toBe(0);
401
+ expect(r.stdout?.trim()).toBe("hello");
402
+ });
403
+
404
+ test("alias lists all", async () => {
405
+ const r = await runCmd(client, "alias");
406
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
407
+ });
408
+
409
+ test("alias with args", async () => {
410
+ const r = await runCmd(client, "alias ll='ls -l' && ll /tmp | head -1");
411
+ expect(r.exitCode).toBe(0);
412
+ });
413
+ });
414
+
415
+ // ─── HISTORY tests ───────────────────────────────────────────────────────
416
+
417
+ describe("history command", () => {
418
+ test("history lists command history", async () => {
419
+ const r = await runCmd(client, "history");
420
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
421
+ });
422
+
423
+ test("history shows entries", async () => {
424
+ const r = await runCmd(client, "history 5");
425
+ expect(r.exitCode).toBeGreaterThanOrEqual(0);
426
+ });
427
+ });
428
+
429
+ // ─── EXIT tests ───────────────────────────────────────────────────────────
430
+
431
+ describe("exit command", () => {
432
+ test("exit with code 0", async () => {
433
+ const r = await runCmd(client, "exit 0");
434
+ expect(r.exitCode).toBe(0);
435
+ });
436
+
437
+ test("exit with code 1", async () => {
438
+ const r = await runCmd(client, "exit 1");
439
+ expect(r.exitCode).not.toBe(0);
440
+ });
441
+ });