慣性聚合 高效追讀感興趣之博客、新聞、科技資訊
閱原文 以慣性聚合開啟

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
吾为何为桌面会计软件撰四百七十五测试
Rahul Gehlot · 2026-05-24 · via DEV Community

书有谬误,则应用不崩。惟欺尔而已。此乃试财货之软件异于他者处也.


钩子:默然之谬 凡谬多有声。应用崩矣。按纽不效。文字错位。尔

有异矣。财货之谬,无声。惟低语而已。

售货之窗隐,而顾客之银犹显五百。店主怏怏曰:“盖表计之谬也。”越月,账目不谐。审计者诘问,信义渐消。

此乃扰我夜寐之梦魇。是故Hisaab Pro——印度小商贾之桌案账簿软件——设四百七十五试。



屏幕截图一——计算器专业版仪表盘


应用——三行

层级 技术
运行时 Node.js + Express
数据库 SQLite(AES-256加密,每财政年一文件)
前端 纯JavaScript——无框架
部署 U盘。双击start.bat。无需安装,无需云端,无需网络。

拉梅什于斋普尔经营一家五金店。年五十有四。不知数据库为何物。其侄辈于U盘上设此,彼插之,点之,望其现金账中五千卢比之款,恰为其客所偿。

彼不应知 SQLite 交易之有。彼不应自审其账。彼当能删误售,而信余额更新之确。

测试者,乃 Ramesh 与其财之间无形之保证也。

若电尽,数据库须存续。若有人中途拽 USB,账册仍须平衡。无借口。无“吾辈将于下次发布中修正。”


那令我神经过敏之虫

删除后之鬼账

此乃旧时之状也:

店主为客设折扣——赊五千卢比。系统录之:减客账,增销账。复式簿记。平衡。无误。

越一周,客返其货。店主删其售。售遂隐于界面。

然客之余额犹显五千卢比。

此乃全删之函数:

// File: server/modules/sales/sales.service.js (original, line ~186)
function deleteSale(id, isDecoy) {
    var stmt = db.prepare('UPDATE sales SET is_deleted = 1, updated_at = datetime(\'now\', \'localtime\') WHERE id = ? AND is_decoy = ?');
    stmt.run(id, isDecoy ? 1 : 0);
    logger.info('Sales', 'Sale deleted: ID ' + id + (isDecoy ? ' [DECOY]' : ''));
    return true;
}

入全屏模式 出全屏模式

代码片段一——谬删(未修前)

一言耳。售货既"删"矣——然顾客之账目余额未尝回溯。相属之账簿条目犹存。现金之户犹充。

售货似已逝。钱脉犹在。幽数据也。

其解在此:

// File: server/modules/sales/sales.service.js (lines 441–487)
function deleteSale(id, isDecoy) {
    var sale = getSaleById(id, isDecoy);
    if (!sale) return false;

    var transaction = db.transaction(function() {
        var deletedInvoiceNo = sale.invoice_no + '-DEL-' + Date.now();
        db.prepare('UPDATE sales SET is_deleted = 1, invoice_no = ?, updated_at = datetime(\'now\', \'localtime\') WHERE id = ?')
            .run(deletedInvoiceNo, id);

        if (sale.customer_account_id) {
            db.prepare('UPDATE accounts SET current_balance = current_balance - ?, updated_at = datetime(\'now\', \'localtime\') WHERE id = ?')
                .run(sale.total, sale.customer_account_id);
        }

        db.prepare('UPDATE transactions SET is_deleted = 1 WHERE linked_sale_id = ?').run(id);

        var payments = db.prepare('SELECT * FROM payments WHERE sale_id = ? AND is_decoy = ? AND is_deleted = 0').all(id, isDecoy ? 1 : 0);
        payments.forEach(function(payment) {
            var assetAccount = payment.mode === 'cash'
                ? accountsService.getDefaultCashAccount(isDecoy)
                : accountsService.getDefaultBankAccount(isDecoy);
            if (assetAccount) {
                var assetRevert = payment.type === 'in' ? -payment.amount : payment.amount;
                db.prepare('UPDATE accounts SET current_balance = current_balance + ?, updated_at = datetime(\'now\', \'localtime\') WHERE id = ?')
                    .run(assetRevert, assetAccount.id);
            }

            if (payment.type === 'in' && sale.id) {
                db.prepare('UPDATE sales SET amount_paid = amount_paid - ?, updated_at = datetime(\'now\', \'localtime\') WHERE id = ?')
                    .run(payment.amount, sale.id);
            }

            db.prepare('UPDATE payments SET is_deleted = 1 WHERE id = ?').run(payment.id);
        });

        return true;
    });

    transaction();
    logger.info('Sales', 'Sale deleted: ID ' + id + (isDecoy ? ' [DECOY]' : ''));
    return true;
}

入全景模式 出全景模式

全删回滚码——修复后

旧时代码有弊,删售之后,查账犹见旧额——售事虽隐于界面,然钱迹犹存于库。今之善者,额款回溯无差,账簿亦无删售之痕。

此虫潜匿数周而不觉。其形无形,界面无恙。乃撰架构迁变之策,以追补腐坏之数:

UPDATE transactions SET is_deleted = 1
WHERE linked_sale_id IN (SELECT id FROM sales WHERE is_deleted = 1);

入全景模式 出全屏模式

WHERE条款逆生产数据库。冀已得尽鬼魅。


识破后人之试

鬼衡之变后,吾心渐生疑惧。乃作此试以验之。

// File: tests/double-entry.test.js (lines 830–887)
// Arrange — create a valid sale first to establish baseline
const saleResponse = await request(app)
    .post('/api/v1/sales')
    .set('Cookie', cookies)
    .send({ customer_account_id: testCustomerId, total: 1000, amount_paid: 1000, date: '2026-04-29' });
expect(saleResponse.status).toBe(201);

// Count transactions before the bad operation
const db = new Database(TEST_DB_PATH);
db.pragma(`key = '${TEST_DB_KEY}'`);
const countBefore = db.prepare(
    'SELECT COUNT(*) as count FROM transactions WHERE is_deleted = 0'
).get().count;
db.close();

// Act — try to create a sale with a NEGATIVE amount
// The API should reject this, but will it leave ghost entries?
const invalidSaleData = {
    customer_account_id: testCustomerId,
    total: -500,
    amount_paid: -500,
    date: '2026-04-29'
};

const failResponse = await request(app)
    .post('/api/v1/sales')
    .set('Cookie', cookies)
    .send(invalidSaleData);

expect(failResponse.status).toBe(400);

// Assert — verify the transaction count is EXACTLY the same
// If even ONE extra row exists, the books are now corrupted
const db2 = new Database(TEST_DB_PATH);
db2.pragma(`key = '${TEST_DB_KEY}'`);
const countAfter = db2.prepare(
    'SELECT COUNT(*) as count FROM transactions WHERE is_deleted = 0'
).get().count;
db2.close();

// THIS is the assertion that matters
expect(countAfter).toBe(countBefore);

入全屏模式 出全屏模式

代码片段三——部分提交测试(核心)

察此试之异处。非惟验HTTP之应(状400),亦启一分数据库之连接 查询原始交易计数。

何也?因API虽返回错误,犹有部分数据已提交。而此即所发生之事。

测试于首次运行即捕获之:countAftercountBefore + 1sales 表插入被回滚,然transactions已执行插入。诸书默然移位,缺一行矣。

试运行者将显expected 5, received 6以赤色着expect(countAfter).toBe(countBefore)— 遂得之顷。六言显污数据库,API视之谓"成功"。

六字之别,辨“一切安好”与“汝书有误”:

expect(countAfter).toBe(countBefore);

入全景模式 退出全屏模式


你无法手动复现的漏洞

写作时崩溃(SIGKILL漏洞)

交易中途停电。用户猛扯USB。taskkill /F 错误进程。

无WAL(预写式日志)模式+显式事务,首笔数据库写入存续,次笔则否。数据残缺。书籍损毁。

此不可手复也——须于两次INSERT之间之毫秒间,以SIGKILL之。是测试乃定然为之:

// File: tests/crash-simulation.test.js (lines 228–258)
test('partial writes should not persist after crash', () => {
    let writeCount = 0;
    const mockStmt = {
        run: jest.fn().mockImplementation(() => {
            writeCount++;
            if (writeCount === 2) {
                throw new Error('SIGKILL: Process terminated during second write');
            }
            return { changes: 1, lastInsertRowid: writeCount };
        })
    };
    mockDb.prepare.mockReturnValue(mockStmt);

    const transactionFn = mockDb.transaction(function() {
        mockDb.prepare('INSERT INTO sales (total) VALUES (?)').run(1000);
        mockDb.prepare('INSERT INTO transactions (amount) VALUES (?)').run(1000);
    });

    expect(() => { transactionFn(); }).toThrow('SIGKILL: Process terminated during second write');
    expect(writeCount).toBe(2);
});

入全屏模式 出全屏模式

代码片段四——崩坏模拟测试

今每连接皆严守PRAGMA journal_mode = WAL。每多步操作皆裹以db.transaction()。若进程于写入中途殒灭,则数据库未遭更改。

此乃仅存于理者之谬,直至雷雨之夜,凌晨二时,偶遇真用者方显。非徒点击可试,唯以代码拟不可能之事,方可验之。

更十二事 — 他物测试所察

谬误 其弊何在
发票号码复用 删除INV-05释放其号;后续销售复用之。审计记录有缺环——税审员之警示也。 اصلاح:将已删除发票更名至INV-05-DEL-{timestamp}
静默500 半数API接口于错误时返回空{},用户睹白屏。部分接口甚至于错误信息中泄SQL语法与列名。
硬编码加密密钥 config.json有所失焉database_key,此应用默然使用之'hisaab-pro-default-key-2026'— 公源码中一字符串。任何人皆可解密任一数据库。
复薪 呼召generatePayroll()同月同员,复支两俸。现银扣二。无讹。
账户余额之偏移 僅更新售價,僅變更sales之表。客戶之current_balance未再同步。餘額隨時間而偏移。

此諸事無一顯於用戶界面。此諸事皆將毀真財務數據。此諸事皆於用戶睹之前,為測試所察。


四百七十五測試之組織如何

宗旨:所获之报。可扫描之证也,显其要目。


屏景二——终端示之:“测试:二者略过,四百七十三者通过,四百七十五者总数。”色呈青绿。

吾之测试,分层而非分门:

测试 所验之事
会计之全 约百三十 借贷相抵(借必等于贷),账簿余额,试算平衡等于零
数据安全 ~110 写入前日志模式,原子回滚,崩溃恢复,备份/还原
业务约束 ~55 无重复薪资发放,外键约束,无负数金额
输入校验 ~55 Zod模式——不良日期、负值、XSS尝试
USB可靠性 ~85 跨PC兼容性、驱动器字母变更、不安全移除
错误处理 ~45 无静默失败,无空错误,净化消息

每测试文件皆以相同区块起始:

// File: tests/double-entry.test.js (lines 1–22)
/**
 * Double-Entry Enforcement Tests — Hisaab Pro
 *
 * Acceptance Criteria:
 * 1. Every transaction MUST create balanced journal entries (debit = credit)
 * 2. Failed transactions must not create partial ledger entries
 * 3. Updated transactions must maintain balance (old deleted, new balanced)
 * 4. Multiple transactions each maintain independent double-entry
 *
 * Following AAA Pattern: Arrange → Act → Assert
 */

入全景模式 出全屏模式

代码片段五——测试文件中接受标准标题

此标题迫我言明何哉此测试先于吾书单行代码而存。非为覆盖率之比。乃为担保也。


屏幕截图三——VS Code测试探索器显示所有二十个测试文件及其describe模块


令我惊异者

一.测试教我系统之运作——非相反

我原以为先写代码,后写测试以验之。然,我乃为所思之场景写测试已处置,观其败,而得见我所未知之行。偏提交之弊,由此而发——吾作此试,期API能净拒负值。试示吾API拒之,然数据库已朽。其试知系统之深,胜于吾。

每当我触碰财理,必先撰试。非为TDD之纯,盖因试显我未觉之设也。

二、至危之码,莫过于至简之码

唯一线UPDATE sales SET is_deleted = 1何故致此幽衡之谬?其状似是。未也正 — 于一事,所言皆非。其弊,在于万般皆非。未尝言。未及客户余额。未及关联交易。未及支付。代码简者,以其不备也。

吾今读简易之函数,已怀疑虑。简易者,常指"未顾及须行之五事。"

三、自信非审阅所能致

试套未行,吾已审之。每变,辄手核数条。"似善。"然手核者,仅取其万一——察五条而谓千条皆善。此幽灵余额之谬,所及者乃数周所删之每笔交易。手核或于第三察而见之,或于第三十察而见之,或永不得见。

此试不取样本。此试每时每刻,皆验诸交易,瞬息之间,速如0.3秒耳。

4. 常态测试之假设,于此不适用

寻常之网应用测试,所问者何?"按纽可呈否?""API可返200否?"此皆关于功能之问也。

理财之软件,需别设诸问:

  • "若数据库书之半途而中,半途而废,其数据之状何如?"
  • "若用户删其不当删之记录,复有他事崩坏否?"
  • "若进程于适然之刻而绝,其复苏之道可行乎?"
  • "若诸事顺遂而输出悄然有误,可有察觉者乎?"

首者关乎功能,次者关乎信义。会计之应用,可失其特,不可失其信.


数字之数

试文 廿
总试数test()it() 互阻, 475
测试码行 ~13,800
应用码行 ~15,000
测试与码之比 ~0.9:1
生产前实得数据损之虫 4
自测试以来生产数据损坏事件 0

收束:测试乃产品之特色

吾非因必而作测试,吾作之,盖无测试之会计应用,实乃以雅致之方,败坏财务数据耳。

每思"此易破也",辄忆Ramesh。彼不知事务回滚为何物,亦不知WAL模式之用。乃插USB之盘,以性命相托。

唯一句UPDATE本当令其败。SIGKILL之虫当令其败。部分提交当令其败。重复薪资当令其败。

475 之试。自

,未尝有数据腐败之事。彼不知试之有也。此其旨也。彼无须知之。


Hisaab Pro 乃开源之器,存于 github.com/spelldrake/hisaab-pro。试之套存于 tests/。475 之试。生产无谬。此其旨也。