125 lines
6.0 KiB
JavaScript
125 lines
6.0 KiB
JavaScript
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const Sqlite = require('better-sqlite3');
|
|
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'raw-account-line-'));
|
|
const dbPath = path.join(tempDir, 'accounts.db');
|
|
|
|
// 模拟升级前的 accounts 表,验证 initDB 会为旧库补列。
|
|
const legacyDb = new Sqlite(dbPath);
|
|
legacyDb.exec(`
|
|
CREATE TABLE accounts (
|
|
email TEXT PRIMARY KEY,
|
|
password TEXT,
|
|
recovery_email TEXT,
|
|
secret_key TEXT,
|
|
browser_id TEXT,
|
|
country TEXT,
|
|
sheerid_link TEXT,
|
|
status TEXT DEFAULT '未登录',
|
|
message TEXT,
|
|
status_reason TEXT,
|
|
proxy_ip TEXT,
|
|
is_exported INTEGER DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
legacyDb.close();
|
|
|
|
const Config = require('../src/core/config');
|
|
Config.DATA_DIR = tempDir;
|
|
Config.DB_PATH = dbPath;
|
|
const database = require('../src/core/database');
|
|
|
|
try {
|
|
database.initDB();
|
|
const columns = database.getDb().pragma('table_info(accounts)').map(row => row.name);
|
|
assert(columns.includes('raw_account_line'), '旧数据库应自动增加 raw_account_line');
|
|
assert(columns.includes('password_changed_at'), '旧数据库应自动增加改密时间字段');
|
|
|
|
const firstRaw = ' test@example.com----OldPass#1----helper@example.com----ABCDEF234567 ';
|
|
const result = database.importAccountsFromText(`# 注释\r\n${firstRaw}\r\n\r\n`);
|
|
assert.strictEqual(result.successCount, 1);
|
|
assert.strictEqual(result.errorCount, 0);
|
|
|
|
let account = database.getAccount('test@example.com');
|
|
assert.strictEqual(account.raw_account_line, firstRaw, '应原样保留有效账号整行');
|
|
assert.strictEqual(account.secret_key, 'abcdef234567', '字段解析仍可执行既有的 2FA 小写转换');
|
|
|
|
database.updateAccountPassword(account.email, 'ChangedPassword');
|
|
database.updateAccount2faKey(account.email, 'changed2fa');
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.raw_account_line, firstRaw, '任务更新密码/2FA 不应改写导入原文');
|
|
assert.strictEqual(account.password_changed, 1, '成功保存新密码时应同步标记已改密');
|
|
assert.strictEqual(account.secret_key, 'changed2fa', '成功保存新2FA时应回填新密钥');
|
|
assert.strictEqual(account.fa_changed, 1, '新2FA密钥和已改2FA标记必须同时写入');
|
|
assert(account.password_changed_at, '成功保存新密码时应记录改密时间');
|
|
const securityDates = JSON.parse(account.security_dates);
|
|
assert(securityDates.passwordMonth && securityDates.passwordDay, '成功改密后应立即写入今天的密码日期');
|
|
|
|
database.updateAccountStatusReason(account.email, ' totp_code_wrong ');
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.status_reason, 'TOTP_CODE_WRONG', '失败原因码应标准化后持久化');
|
|
database.updateAccountStatusReason(account.email, null);
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.status_reason, null, '登录成功后应能清除旧失败原因');
|
|
assert.strictEqual(account.raw_account_line, firstRaw, '原因码更新不应改写导入原文');
|
|
|
|
const latestRaw = 'test@example.com----LatestPass----LATEST234567';
|
|
database.importAccountsFromText(latestRaw);
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.raw_account_line, latestRaw, '重复导入应保存最新一次原文');
|
|
|
|
const beforeSkippedImport = { ...account };
|
|
const skipped = database.importAccountsFromText(
|
|
'TEST@example.com----MustNotReplace----SKIPPED234567',
|
|
'----',
|
|
'待检查',
|
|
null,
|
|
'新客户',
|
|
true,
|
|
);
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(skipped.successCount, 0, '勾选跳过重复后不应把重复邮箱计为导入成功');
|
|
assert.strictEqual(skipped.skippedCount, 1, '邮箱大小写不同时也应识别为重复');
|
|
assert.deepStrictEqual(account, beforeSkippedImport, '跳过重复邮箱后账号全部字段都应保持不变');
|
|
assert.deepStrictEqual(skipped.importedEmails, [], '跳过的邮箱不应进入导入后的自动初始化名单');
|
|
|
|
const sameBatch = database.importAccountsFromText(
|
|
'new@example.com----FirstPassword\nNEW@example.com----SecondPassword',
|
|
'----',
|
|
'未登录',
|
|
null,
|
|
null,
|
|
true,
|
|
);
|
|
assert.strictEqual(sameBatch.successCount, 1, '同一批次首次出现的邮箱应正常导入');
|
|
assert.strictEqual(sameBatch.skippedCount, 1, '同一批次后续重复邮箱应跳过');
|
|
assert.strictEqual(database.getAccount('new@example.com').password, 'FirstPassword', '同批重复行不应覆盖首次导入内容');
|
|
|
|
database.updateAccountTag(account.email, '家庭组母号');
|
|
database.updateAccount2faKey(account.email, 'familychanged2fa');
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.secret_key, 'familychanged2fa', '家庭组母号的任务2FA回写也不得被导入保护跳过');
|
|
assert.strictEqual(account.fa_changed, 1, '家庭组母号应正常保留已改2FA标记');
|
|
const protectedRaw = 'test@example.com----MustNotReplace----PROTECTED234';
|
|
database.importAccountsFromText(protectedRaw);
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.raw_account_line, latestRaw, '家庭组母号重复导入时仍应整体跳过');
|
|
|
|
database.updateAccountStatus(account.email, '登录成功');
|
|
database.updateAccountMessage(account.email, '✅ 登录成功');
|
|
account = database.getAccount(account.email);
|
|
assert.strictEqual(account.status, '登录成功', '家庭组母号的任务状态不应被导入保护拦截');
|
|
assert.strictEqual(account.message, '✅ 登录成功', '家庭组母号的任务消息不应保留旧错误');
|
|
assert.strictEqual(account.raw_account_line, latestRaw, '任务状态/消息更新不应改写导入原文');
|
|
|
|
console.log('database_raw_account_line.test.js: PASS');
|
|
} finally {
|
|
try { database.getDb().close(); } catch (_) {}
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
}
|