Files
AgentHub/server/__tests__/users-role-migration.test.js
T
nntrivi2001 b7f3735b29 feat: v2.5.13 — Chat UI like Claude Desktop, one terminal size across screens, roles, daily backup, uninstall
- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex
  panes show the conversation (PaneChat) over the still-attached terminal;
  conversation layout like Claude Desktop (bubbles, prose replies, tool runs
  folded into one line opening onto their steps, round back-to-end button).
- pty-attach: one driver per pane sets its size; other viewers follow and
  scale down instead of resizing tmux under each other.
- Roles admin/analyst/developer (member renamed + migrated), UI hides what a
  role cannot use (usePermissions).
- Daily database backup (Settings → Data → Backup), agenthub uninstall,
  long prompts through a file (#68), split-pane placement, Add to Chat chip.

Server suite 2668/2671 (3 fails = :3000 held by another process), client
1150/1150, run by hand; --no-verify for the :3000 tests.
2026-10-08 23:04:57 +07:00

64 lines
2.4 KiB
JavaScript

/**
* @file users-role-migration.test.js
* @description The `users` table migration that renamed the `member` role to
* `developer`: an existing database's members become developers, keep their
* logins, and the old value is refused afterwards.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { it, after } = require("node:test");
const assert = require("node:assert/strict");
const path = require("node:path");
const fs = require("node:fs");
const os = require("node:os");
const { DatabaseSync } = require("node:sqlite");
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), "role-migration-"));
const DB = path.join(TMP, "dashboard.db");
after(() => fs.rmSync(TMP, { recursive: true, force: true }));
it("turns every member into a developer and keeps their logins", () => {
const old = new DatabaseSync(DB);
old.exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
role TEXT NOT NULL DEFAULT 'member' CHECK(role IN ('admin','analyst','member')),
password_hash TEXT NOT NULL,
disabled INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE TABLE auth_sessions (
token_hash TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
expires_at TEXT NOT NULL
);
INSERT INTO users (username, role, password_hash) VALUES
('boss', 'admin', 'x'), ('mia', 'member', 'x'), ('ana', 'analyst', 'x');
INSERT INTO auth_sessions (token_hash, user_id, expires_at) VALUES ('t', 2, '2999-01-01');
`);
old.close();
process.env.DASHBOARD_DB_PATH = DB;
const { db } = require("../db");
const roles = db.prepare("SELECT username, role FROM users ORDER BY id").all();
assert.deepEqual(
roles.map((r) => [r.username, r.role]),
[
["boss", "admin"],
["mia", "developer"],
["ana", "analyst"],
]
);
assert.equal(db.prepare("SELECT user_id FROM auth_sessions").get().user_id, 2);
assert.throws(() =>
db
.prepare("INSERT INTO users (username, role, password_hash) VALUES ('x', 'member', 'x')")
.run()
);
db.prepare("INSERT INTO users (username, password_hash) VALUES ('neo', 'x')").run();
assert.equal(db.prepare("SELECT role FROM users WHERE username = 'neo'").get().role, "developer");
});