import assert from "node:assert/strict"; import { createHash } from "node:crypto"; import test from "node:test"; import { Hono } from "hono"; import app from "./index.js"; import { registerElectricMonthlyRoute, registerElectricOverviewRoute, type ElectricDependencies, } from "./electric.js"; import { registerEtcOverviewRoute, type EtcOverviewDependencies, } from "./etc.js"; import { registerHydrogenDailyRoute, type HydrogenDailyDependencies, } from "./hydrogen-daily.js"; import { registerHydrogenOverviewRoute, type HydrogenOverviewDependencies, } from "./hydrogen-overview.js"; import { registerHydrogenSettlementRoute, type HydrogenSettlementDependencies, } from "./hydrogen-settlement.js"; import { registerHydrogenStationBoardRoute, type HydrogenStationBoardDependencies, } from "./hydrogen-station-board.js"; import { registerHydrogenBiV2Routes, type HydrogenBiV2Dependencies, } from "./hydrogen-bi-v2.js"; interface QueryCall { sql: string; params: unknown; } interface CacheCall { key: string; force: boolean | undefined; } function createQueryMock(resultSets: unknown[][], calls: QueryCall[]) { return async (sql: string, params?: unknown) => { calls.push({ sql, params }); return [resultSets.shift() ?? [], []]; }; } function createCachedMock(calls: CacheCall[]) { return async ( key: string, loader: () => Promise, opts: { force?: boolean } = {}, ): Promise => { calls.push({ key, force: opts.force }); return loader(); }; } function sqlHash(sql: string): string { return createHash("sha256").update(sql).digest("hex").slice(0, 12); } test("能源路由按既有顺序注册并增加只读单站看板接口", () => { assert.deepEqual( app.routes .filter((route) => route.method === "GET") .map((route) => route.path), [ "/hydrogen/overview", "/hydrogen/overview-detail", "/hydrogen/daily", "/hydrogen/daily-detail", "/hydrogen/settlement", "/hydrogen/station-board", "/h2/v2/meta", "/h2/v2/overview", "/h2/v2/daily", "/h2/v2/daily-tree", "/h2/v2/drill", "/electric/overview", "/electric/monthly", "/etc/overview", ], ); }); test("氢能 BI v2 使用独立真实账本合同,并且不让前端拼接旧接口", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [ [{ value: 2026, startDate: "2026-01-01", endDate: "2026-08-18" }], [{ id: 7, name: "测试站", province: "广东省", city: "佛山市" }], [{ ledgerAt: "2026-08-18 10:00:00" }], ], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request("/h2/v2/meta"); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { years: [{ value: 2026, startDate: "2026-01-01", endDate: "2026-08-18" }], stations: [{ id: "7", name: "测试站", province: "广东省", city: "佛山市" }], watermark: { ledgerAt: "2026-08-18 10:00:00", paymentAt: null }, }); assert.equal(calls.length, 3); assert.match(calls[0].sql, /FROM hydrogen_fuel_ledger/); assert.match(calls[1].sql, /FROM new_hydrogen_site/); assert.match(calls[1].sql, /EXISTS\s*\([\s\S]*amount_kg[\s\S]*> 0/); }); test("氢能 BI v2 下钻在日期、站点、客户、车辆及区域上下文中复用同一过滤口径", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [ [{ id: "123" }], [{ recordCount: 2, kg: 30, cost: 900, revenue: 1000 }], [ { id: "2026-08-18", name: "2026-08-18", province: null, city: null, recordCount: 2, stationCount: 1, customerCount: 1, kg: 30, cost: 900, revenue: 1000, lingniuKg: 0, externalKg: 30, }, ], ], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request( "/h2/v2/drill?date=2026-08-18&stationId=0&customerId=0&customerName=%E6%9C%AA%E5%85%B3%E8%81%94%E5%AE%A2%E6%88%B7&plateNo=%E6%97%A0%E8%BD%A6%E7%89%8C®ion=%E5%98%89%E5%85%B4%E5%B8%82®ionGranularity=city&vehicleScope=external&verifyScope=verified&groupBy=date&pageSize=20", ); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.groupBy, "date"); assert.equal(payload.filters.date, "2026-08-18"); assert.equal(payload.filters.stationId, "0"); assert.equal(payload.filters.customerId, 0); assert.equal(payload.filters.customerName, "未关联客户"); assert.equal(payload.filters.plateNo, "无车牌"); assert.equal(payload.filters.region, "嘉兴市"); assert.equal(payload.groups[0].id, "2026-08-18"); assert.equal(payload.summary.kg, 30); assert.equal(calls.length, 3); assert.deepEqual(calls[0].params, ["嘉兴市"]); assert.match(calls[0].sql, /FROM new_hydrogen_site rs/); const commonParams = [ "2026-08-18", "2026-08-18", "0", 0, "未关联客户", "无车牌", "123", ]; assert.deepEqual(calls[1].params, commonParams); assert.deepEqual((calls[2].params as unknown[]).slice(0, -2), commonParams); for (const call of calls.slice(1)) { assert.match(call.sql, /COALESCE\(b\.station_id, 0\) = \?/); assert.match( call.sql, /COALESCE\(b\.system_customer_id, b\.customer_id, 0\) = \?/, ); assert.match(call.sql, /COALESCE\(NULLIF\(b\.license_plate/); assert.match(call.sql, /b\.station_id IN \(\?\)/); assert.match(call.sql, /b\.vehicle_id IS NULL/); assert.match(call.sql, /verify_status/); } assert.match( calls[2].sql, /GROUP BY DATE_FORMAT\(b\.refuel_time, '%Y-%m-%d'\)/, ); assert.match(calls[2].sql, /ORDER BY id DESC/); }); test("氢能 BI v2 利润下钻只保留客户承担订单,并与对客总价使用同一订单集合", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [ [{ recordCount: 1, kg: 10, cost: 300, revenue: 360 }], [ { id: "7", name: "客户承担站", province: "浙江省", city: "嘉兴市", recordCount: 1, stationCount: 1, customerCount: 1, kg: 10, cost: 300, revenue: 360, lingniuKg: 10, externalKg: 0, }, ], [ { id: 1, time: "2026-08-18 09:00:00", orderNo: "H2-1", stationId: 7, stationName: "客户承担站", customerId: 8, customerName: "客户A", plateNo: "浙A1", source: "api", verifyStatus: "VERIFIED", vehicleId: 1, kg: 10, unitPrice: 30, cost: 300, revenue: 360, }, ], ], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request( "/h2/v2/drill?year=2026&vehicleScope=all&verifyScope=all&groupBy=station&amountScope=customer", ); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.amountScope, "customer"); assert.equal(payload.summary.revenue - payload.summary.cost, 60); assert.equal(calls.length, 2); for (const call of calls) { assert.match( call.sql, /b\.settlement_type = 1/, ); } }); test("氢能 BI v2 保留原型允许选择的 2023 历史年份,不回退到当前年", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [ [ { ledgerAt: null, totalKg: 0, totalCost: 0, totalRevenue: 0, customerCost: 0, monthKg: 0, monthCost: 0, todayKg: 0, todayCost: 0, recordCount: 0, stationCount: 0, }, ], [], [], [], ], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request( "/h2/v2/overview?year=2023&vehicleScope=all&verifyScope=all", ); assert.equal(response.status, 200); const payload = await response.json(); assert.deepEqual(payload.range, { startDate: "2023-01-01", endDate: "2023-12-31", }); assert.equal(payload.filters.startDate, "2023-01-01"); assert.equal(payload.filters.endDate, "2023-12-31"); assert.equal(payload.kpis.totalKg, 0); assert.equal(calls.length, 4); }); test("氢能 BI v2 客户账单按 settlement_type 映射客户、我司和其他承担", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [ [ { ledgerAt: "2026-08-19 10:00:00", totalKg: 60, totalCost: 1800, totalRevenue: 720, customerBearingKg: 10, companyBearingKg: 30, otherBearingKg: 20, customerRevenue: 360, customerCost: 600, companyCost: 900, otherCost: 300, monthKg: 60, monthCost: 1800, todayKg: 0, todayCost: 0, recordCount: 3, stationCount: 1, }, ], [ { month: "2026-01", totalKg: 60, lingniuKg: 30, externalKg: 30, cost: 1800, revenue: 720, customerRevenue: 360, customerCost: 600, }, ], [], [ { id: 1, name: "混合承担客户", kg: 30, customerBearingKg: 10, companyBearingKg: 20, otherBearingKg: 0, cost: 900, revenue: 300, customerRevenue: 360, customerCost: 600, companyCost: 300, otherCost: 0, recordCount: 2, }, { id: 2, name: "仅我司承担客户", kg: 30, customerBearingKg: 0, companyBearingKg: 10, otherBearingKg: 20, cost: 900, revenue: 0, customerRevenue: 0, customerCost: 0, companyCost: 300, otherCost: 600, recordCount: 1, }, ], ], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request( "/h2/v2/overview?year=2026&vehicleScope=all&verifyScope=all", ); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.kpis.customerBearingKg, 10); assert.equal(payload.kpis.companyBearingKg, 30); assert.equal(payload.kpis.otherBearingKg, 20); assert.equal( payload.kpis.totalKg, payload.kpis.customerBearingKg + payload.kpis.companyBearingKg + payload.kpis.otherBearingKg, ); assert.equal(payload.kpis.customerGrossProfit, -240); assert.equal(payload.monthly[0].customerRevenue, 360); assert.equal(payload.monthly[0].customerCost, 600); assert.equal(payload.monthly[0].customerGrossProfit, -240); assert.deepEqual(payload.customers.map((row: { name: string; bearer: string }) => ({ name: row.name, bearer: row.bearer, })), [ { name: "混合承担客户", bearer: "both" }, { name: "仅我司承担客户", bearer: "other" }, ]); assert.match(calls[3].sql, /AS customerBearingKg/); assert.match(calls[3].sql, /AS companyBearingKg/); assert.match(calls[3].sql, /AS otherBearingKg/); assert.match(calls[2].sql, /AS customerRevenue/); assert.match(calls[2].sql, /AS customerCost/); assert.match(calls[2].sql, /AS companyCost/); assert.match(calls[2].sql, /AS otherCost/); assert.match(calls[3].sql, /AS customerRevenue/); assert.match(calls[3].sql, /AS customerCost/); assert.match(calls[3].sql, /AS companyCost/); assert.match(calls[3].sql, /AS otherCost/); assert.equal(payload.customers[0].customerRevenue, 360); assert.equal(payload.customers[0].customerCost, 600); assert.match(calls[3].sql, /b\.settlement_type = 1/); assert.match(calls[3].sql, /b\.settlement_type = 2/); assert.match(calls[3].sql, /b\.settlement_type NOT IN \(1, 2\)/); }); test("氢能 BI v2 月度上下文换算为完整自然月并沿用至明细", async () => { const calls: QueryCall[] = []; const v2App = new Hono(); registerHydrogenBiV2Routes(v2App, { hydrogenPool: { query: createQueryMock( [[{ recordCount: 0, kg: 0, cost: 0, revenue: 0 }], []], calls, ), }, } as unknown as HydrogenBiV2Dependencies); const response = await v2App.request( "/h2/v2/drill?month=2026-02&vehicleScope=all&verifyScope=all&groupBy=record", ); assert.equal(response.status, 200); const payload = await response.json(); assert.deepEqual(payload.filters.startDate, "2026-02-01"); assert.deepEqual(payload.filters.endDate, "2026-02-28"); assert.equal(payload.filters.month, "2026-02"); assert.equal(calls.length, 2); assert.deepEqual(calls[0].params, ["2026-02-01", "2026-02-28"]); assert.deepEqual((calls[1].params as unknown[]).slice(0, -2), [ "2026-02-01", "2026-02-28", ]); }); test("单站看板仅保留有加氢记录的站点并合并区间现结流水", async () => { const calls: QueryCall[] = []; const cacheCalls: CacheCall[] = []; const stationBoardApp = new Hono(); registerHydrogenStationBoardRoute(stationBoardApp, { hydrogenPool: { query: createQueryMock( [ [ { id: 7, name: "测试加氢站", province: "广东省", city: "佛山市", kg: 100, fee: 3500, recordCount: 2, latestLedgerTime: "2026-08-17 10:00:00", }, { id: 8, name: "零业务站", province: "广东省", city: "广州市", kg: 0, fee: 0, recordCount: 0, latestLedgerTime: null, }, ], [ { stationId: 7, amount: 5000, paymentCount: 1, latestPaymentDate: "2026-08-17", }, ], [ { date: "2026-08-16", kg: 40, fee: 1400, recordCount: 1 }, { date: "2026-08-17", kg: 60, fee: 2100, recordCount: 1 }, ], [ { stationId: 7, date: "2026-08-16", kg: 40 }, { stationId: 7, date: "2026-08-17", kg: 60 }, ], [{ date: "2026-08-17", amount: 5000, paymentCount: 1 }], ], calls, ), }, cached: createCachedMock(cacheCalls), } as unknown as HydrogenStationBoardDependencies); const response = await stationBoardApp.request( "/hydrogen/station-board?startDate=2026-08-16&endDate=2026-08-17&force=1", ); assert.equal(response.status, 200); const payload = await response.json(); assert.deepEqual(payload.summary, { stationCount: 1, activeStationCount: 1, totalKg: 100, totalFee: 3500, recordCount: 2, paymentAmount: 5000, paymentCount: 1, latestLedgerTime: "2026-08-17 10:00:00", daily: [ { date: "2026-08-16", kg: 40, fee: 1400, recordCount: 1, paymentAmount: 0, paymentCount: 0, }, { date: "2026-08-17", kg: 60, fee: 2100, recordCount: 1, paymentAmount: 5000, paymentCount: 1, }, ], }); assert.equal(payload.stations.length, 1); assert.equal(payload.stations[0].name, "测试加氢站"); assert.deepEqual(payload.stations[0].dailyKg, [ { date: "2026-08-16", kg: 40 }, { date: "2026-08-17", kg: 60 }, ]); assert.deepEqual(cacheCalls, [ { key: "hydrogen/station-board?start=2026-08-16&end=2026-08-17", force: true, }, ]); assert.equal(calls.length, 5); assert.match(calls[0].sql, /FROM hydrogen_station s/); assert.match(calls[1].sql, /FROM hydrogen_station_payment/); assert.match(calls[2].sql, /SUM\(b\.amount_kg\)/); assert.match(calls[3].sql, /GROUP BY b\.station_id/); assert.match(calls[4].sql, /GROUP BY DATE_FORMAT\(payment_date/); }); test("站日现结台账仅读取付款流水,并按最近有数据的 15 天聚合", async () => { const calls: QueryCall[] = []; const cacheCalls: CacheCall[] = []; const app = new Hono(); registerHydrogenSettlementRoute(app, { hydrogenPool: { query: createQueryMock( [ [{ latestPaymentDate: "2026-05-26" }], [ { date: "2026-05-26", stationId: 44, stationName: "联新加氢站", amount: 30000, paymentCount: 1, matchMode: "exact", }, { date: "2026-05-25", stationId: null, stationName: "佛山中石化", amount: 20000, paymentCount: 2, matchMode: "group", }, ], ], calls, ), }, cached: createCachedMock(cacheCalls), } as unknown as HydrogenSettlementDependencies); const response = await app.request( "/hydrogen/settlement?range=latest&force=1", ); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { range: { start: "2026-05-12", end: "2026-05-26", mode: "latest" }, summary: { amount: 50000, paymentCount: 3, stationDayCount: 2, stationCount: 2, latestPaymentDate: "2026-05-26", }, rows: [ { date: "2026-05-26", stationId: 44, stationName: "联新加氢站", amount: 30000, paymentCount: 1, matchMode: "exact", }, { date: "2026-05-25", stationId: null, stationName: "佛山中石化", amount: 20000, paymentCount: 2, matchMode: "group", }, ], }); assert.deepEqual(cacheCalls, [ { key: "hydrogen/settlement?start=2026-05-12&end=2026-05-26", force: true }, ]); assert.equal(calls.length, 2); assert.match(calls[0].sql, /MAX\(payment_date\)/); assert.match(calls[1].sql, /FROM hydrogen_station_payment p/); assert.match(calls[1].sql, /LIMIT 500/); assert.deepEqual(calls[1].params, ["2026-05-12", "2026-05-26"]); }); test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", async () => { const hydrogenCalls: QueryCall[] = []; const electricCalls: QueryCall[] = []; const cacheCalls: CacheCall[] = []; const cached = createCachedMock(cacheCalls); const hydrogenQuery = createQueryMock( [ [{ y: 2025, latestLedgerTime: "2025-01-02 10:30:00" }], [{}], [], [], [], [], [ { name: "混合承担客户", payer: "mixed", kg: 10, cost: 350, revenue: 360, }, ], [ { d: "2025-01-02", stationId: 7, stationName: "测试站", kg: 10, fee: 350, pricePerKg: 35, }, ], ], hydrogenCalls, ); const electricQuery = createQueryMock( [ [ { totalKwh: 20, totalFee: 30, monthKwh: 15, monthFee: 25, todayKwh: 10, todayFee: 12, }, ], [], [{ date: "2025-01-02", kwh: 10, fee: 12 }], [{ kwh: 5 }], [{ date: "2025-01-02", kwh: 10, fee: 12 }], ], electricCalls, ); const hydrogenOverviewApp = new Hono(); registerHydrogenOverviewRoute(hydrogenOverviewApp, { hydrogenPool: { query: hydrogenQuery }, cached, } as unknown as HydrogenOverviewDependencies); const overviewResponse = await hydrogenOverviewApp.request( "/hydrogen/overview?year=2025&force=1", ); assert.equal(overviewResponse.status, 200); const overviewPayload = (await overviewResponse.json()) as { customers: { payer: string; name: string }[]; }; assert.deepEqual(overviewPayload.customers, [ { name: "混合承担客户", payer: "mixed", kg: 10, cost: 350, revenue: 360, }, ]); const hydrogenDailyApp = new Hono(); registerHydrogenDailyRoute(hydrogenDailyApp, { hydrogenPool: { query: hydrogenQuery }, cached, } as unknown as HydrogenDailyDependencies); const dailyResponse = await hydrogenDailyApp.request( "/hydrogen/daily?startDate=2025-01-01&endDate=2025-01-02&customer=external&force=1", ); assert.equal(dailyResponse.status, 200); assert.deepEqual(await dailyResponse.json(), [ { date: "2025-01-02", totalKg: 10, totalFee: 350, chainPct: 0, customerType: "external", stations: [ { id: 7, name: "测试站", pricePerKg: 35, kg: 10, fee: 350, chainPct: 0, }, ], }, { date: "2025-01-01", totalKg: 0, totalFee: 0, chainPct: 0, customerType: "external", stations: [], }, ]); const electricApp = new Hono(); const electricDependencies = { pool: { query: electricQuery }, cached, } as unknown as ElectricDependencies; registerElectricOverviewRoute(electricApp, electricDependencies); registerElectricMonthlyRoute(electricApp, electricDependencies); const electricOverviewResponse = await electricApp.request( "/electric/overview?force=1", ); assert.equal(electricOverviewResponse.status, 200); const electricMonthlyResponse = await electricApp.request( "/electric/monthly?startDate=2025-01-01&endDate=2025-01-02&customer=external&force=1", ); assert.equal(electricMonthlyResponse.status, 200); assert.deepEqual(cacheCalls, [ { key: "hydrogen/overview?year=2025", force: true }, { key: "hydrogen/daily?start=2025-01-01&end=2025-01-02&customer=external", force: true, }, { key: "electric/overview", force: true }, { key: "electric/monthly?customer=external&start=2025-01-01&end=2025-01-02", force: true, }, ]); // 指纹来自拆分前的 13 条 SQL,连空白符变化也会使测试失败。 assert.deepEqual( [...hydrogenCalls, ...electricCalls].map((call) => sqlHash(call.sql)), [ "515acf4dbb2a", "de16af670e96", "1367eddb3b7f", "f125d4dde376", "c547b8e1318e", "0e3dfd8f4b81", "f1f69f275993", "688cfdb7a2bf", "03fd669f8077", "6e4031b10926", "4fc77463294c", "1886d54939d8", "09fc714ec4b2", ], ); assert.deepEqual( hydrogenCalls.map((call) => call.params), [ ["2024-01-01"], [ 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 0, 0, 0, 0, 0, 0, 0, 0, "2024-01-01", ], ["2024-01-01", 2025], ["2024-01-01", 2025], ["2024-01-01", 2025], ["2024-01-01", 2025], ["2024-01-01", 2025], ["2025-01-01", "2025-01-02"], ], ); assert.match(hydrogenCalls[7].sql, /b\.vehicle_id IS NULL/); assert.deepEqual( electricCalls.map((call) => call.params), [undefined, undefined, undefined, undefined, ["2025-01-01", "2025-01-02"]], ); }); test("氢能总览按车辆归属和单站筛选,并隔离缓存结果", async () => { const calls: QueryCall[] = []; const cacheCalls: CacheCall[] = []; const app = new Hono(); registerHydrogenOverviewRoute(app, { hydrogenPool: { query: createQueryMock( [ [{ y: 2026, latestLedgerTime: "2026-08-17 21:50:09" }], [{ yearKg: 12, latestLedgerTime: "2026-08-17 21:50:09" }], [], [], [], [], [], ], calls, ), }, cached: createCachedMock(cacheCalls), } as unknown as HydrogenOverviewDependencies); const response = await app.request( "/hydrogen/overview?year=2026&stationId=7&vehicleScope=lingniu", ); const payload = await response.json(); assert.equal(response.status, 200); assert.equal(payload.latestLedgerTime, "2026-08-17 21:50:09"); assert.deepEqual(cacheCalls, [ { key: "hydrogen/overview?year=2026&station=7&vehicle=lingniu", force: false, }, ]); assert.deepEqual(payload.filter, { stationId: 7, vehicleScope: "lingniu", verifyScope: "all", }); // 年份候选项是全局元数据;其余查询都必须带上单站筛选参数。 assert.ok( calls .slice(1) .every( (call) => Array.isArray(call.params) && (call.params as unknown[]).includes(7), ), ); assert.match(calls[1].sql, /station_id = \? AND vehicle_id IS NOT NULL/); assert.match( calls[2].sql, /b\.station_id = \? AND b\.vehicle_id IS NOT NULL/, ); }); test("ETC 总览对空台账返回明确空状态", async () => { const calls: QueryCall[] = []; const cacheCalls: CacheCall[] = []; const app = new Hono(); registerEtcOverviewRoute(app, { pool: { query: createQueryMock( [ [ { tollRecordCount: 0, vehicleCount: 0, totalAmount: null, latestTollTime: null, billCount: 0, receivableAmount: null, paidAmount: null, }, ], ], calls, ), }, cached: createCachedMock(cacheCalls), } as unknown as EtcOverviewDependencies); const response = await app.request("/etc/overview?force=1"); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { tollRecordCount: 0, vehicleCount: 0, totalAmount: 0, latestTollTime: null, billCount: 0, receivableAmount: 0, paidAmount: 0, hasData: false, }); assert.deepEqual(cacheCalls, [{ key: "etc/overview", force: true }]); assert.match(calls[0].sql, /FROM etc_toll_record/); assert.match(calls[0].sql, /FROM energy_etc_bill/); });