From 2c39f513e236ca074cffcb530f7ef77588e51e2a Mon Sep 17 00:00:00 2001 From: voidofrgestudio Date: Mon, 7 Sep 2026 20:14:06 +0700 Subject: [PATCH 1/2] fix(mysql,postgres): omit unmeasured database size instead of publishing 0 bytes Route both providers' overview size reads through measuredNullableAggregate() so a missing result row or a row without the size column publishes neither databaseSizeBytes nor a formatted size: databaseSize stays "N/A", exactly as mssql.ts and oracle.ts already do (#585/#601). A returned SQL NULL (an empty database) remains a measured zero and is still published as 0 / "0 B". Tests, one per provider suite in the #601 shape: row without the column, no result row, and a non-finite value each assert databaseSizeBytes is absent and databaseSize is "N/A"; the anti-vacuity twin pins a returned NULL at 0. Closes #621 --- docs/providers/mysql.md | 6 ++ docs/providers/postgres.md | 6 ++ src/lib/db/providers/sql/mysql.ts | 8 +- src/lib/db/providers/sql/postgres.ts | 7 +- tests/integration/db/mysql-provider.test.ts | 72 +++++++++++++++++ .../integration/db/postgres-provider.test.ts | 80 +++++++++++++++++++ 6 files changed, 173 insertions(+), 6 deletions(-) diff --git a/docs/providers/mysql.md b/docs/providers/mysql.md index 32e99cff..f1af6eab 100644 --- a/docs/providers/mysql.md +++ b/docs/providers/mysql.md @@ -598,6 +598,12 @@ outright on Doris for a filter the statement does not need ([#573](https://githu and the narrowest fix changes only what a grammar refuses. `getOverview()` also costs one round trip fewer than before, reading uptime and connections out of the same result set. +**Database size is absent, never zeroed, when it is not measured.** `getOverview()` sizes the +database with `SUM(DATA_LENGTH + INDEX_LENGTH)` over `information_schema.tables`. A missing result +row, or a row without the `size_bytes` column, is no measurement at all: `databaseSizeBytes` is +omitted and `databaseSize` stays `"N/A"`. Only a returned SQL `NULL` — an empty database — is a +measured zero, and that reading is published as `0`/`"0 B"`. + **Graceful degradation — note the *different* failure modes:** - `getHealth()` slow-queries: the digest rows, or **an empty list** — never a placeholder row, and on this path **the reason is dropped**. It used to answer a single fabricated row diff --git a/docs/providers/postgres.md b/docs/providers/postgres.md index 5afe0956..2ccbb7ba 100644 --- a/docs/providers/postgres.md +++ b/docs/providers/postgres.md @@ -713,6 +713,12 @@ base) fans these out in parallel. `getTableStats()` / `getIndexStats()` accept an optional `{ schema }` filter; with none they cover all user schemas. +**Database size is absent, never zeroed, when it is not measured.** `getOverview()` sizes the +database with `pg_database_size`; a missing result row, or a row without `database_size_bytes`, is +no measurement at all, so `databaseSizeBytes` is omitted and `databaseSize` stays `"N/A"`. Only a +returned SQL `NULL` — an empty database — is a measured zero, and that reading is published as +`0`/`"0 B"`. + ### 7.1 When the cache hit ratio is not measurable The ratio comes from `pg_statio_user_tables`, and there are two ordinary states in which that view diff --git a/src/lib/db/providers/sql/mysql.ts b/src/lib/db/providers/sql/mysql.ts index 743449be..758824ce 100644 --- a/src/lib/db/providers/sql/mysql.ts +++ b/src/lib/db/providers/sql/mysql.ts @@ -29,6 +29,7 @@ import { } from "../../types"; import { DatabaseConfigError, ConnectionError, QueryError, mapDatabaseError } from "../../errors"; import { formatBytes } from "../../utils/pool-manager"; +import { measuredNullableAggregate } from "../../utils/measured-aggregate"; import { CACHE_HIT_RATIO_UNAVAILABLE, formatCacheHitRatio, measuredNumber } from "@/lib/monitoring-cache-ratio"; /** @@ -1292,7 +1293,8 @@ export class MySQLProvider extends SQLBaseProvider { // Get database size const [sizeRows] = await runStatement(conn, OVERVIEW_DATABASE_SIZE_SQL, [this.config.database]); - const databaseSizeBytes = parseInt(sizeRows[0]?.size_bytes || "0"); + const databaseSizeBytes = measuredNullableAggregate(sizeRows[0], "size_bytes"); + const databaseSize = databaseSizeBytes === undefined ? "N/A" : formatBytes(databaseSizeBytes); // Get table and index count const [countRows] = await runStatement(conn, OVERVIEW_OBJECT_COUNTS_SQL, [this.config.database]); @@ -1308,8 +1310,8 @@ export class MySQLProvider extends SQLBaseProvider { ...(uptimeSeconds === undefined ? {} : { startTime: new Date(Date.now() - uptimeSeconds * 1000) }), ...(activeConnections === undefined ? {} : { activeConnections }), maxConnections, - databaseSize: formatBytes(databaseSizeBytes), - databaseSizeBytes, + databaseSize, + ...(databaseSizeBytes === undefined ? {} : { databaseSizeBytes }), tableCount: parseInt(tableCountRows[0]?.cnt || "0"), indexCount: parseInt(countRows[0]?.index_count || "0"), }; diff --git a/src/lib/db/providers/sql/postgres.ts b/src/lib/db/providers/sql/postgres.ts index 50b39ff1..794d42f7 100644 --- a/src/lib/db/providers/sql/postgres.ts +++ b/src/lib/db/providers/sql/postgres.ts @@ -39,6 +39,7 @@ import { import { assertReadOnlyBudget, measureResultBytes } from "./read-only-budget"; import { postgresColumnTypes } from "./column-types"; import { formatBytes } from "../../utils/pool-manager"; +import { measuredNullableAggregate } from "../../utils/measured-aggregate"; import { CACHE_HIT_RATIO_UNAVAILABLE, formatCacheHitRatio, measuredNumber } from "@/lib/monitoring-cache-ratio"; // ============================================================================ @@ -1813,8 +1814,8 @@ export class PostgresProvider extends SQLBaseProvider { let databaseSizeBytes: number | undefined; try { const sizeRes = await client.query(OVERVIEW_SIZE_SQL, [this.config.database]); - databaseSize = sizeRes.rows[0].database_size || "0 bytes"; - databaseSizeBytes = parseInt(sizeRes.rows[0].database_size_bytes || "0"); + databaseSizeBytes = measuredNullableAggregate(sizeRes.rows[0], "database_size_bytes"); + if (databaseSizeBytes !== undefined) databaseSize = formatBytes(databaseSizeBytes); } catch { databaseSize = "N/A"; databaseSizeBytes = undefined; @@ -1842,7 +1843,7 @@ export class PostgresProvider extends SQLBaseProvider { activeConnections, maxConnections, databaseSize, - databaseSizeBytes, + ...(databaseSizeBytes === undefined ? {} : { databaseSizeBytes }), tableCount, indexCount, }; diff --git a/tests/integration/db/mysql-provider.test.ts b/tests/integration/db/mysql-provider.test.ts index 46246eab..5994f9eb 100644 --- a/tests/integration/db/mysql-provider.test.ts +++ b/tests/integration/db/mysql-provider.test.ts @@ -1560,6 +1560,78 @@ describe("MySQLProvider", () => { expect(overview.startTime).toBeInstanceOf(Date); }); + test("a size result without the expected column leaves overview size absent", async () => { + mockExecuteFn = (sql: string) => { + const lower = sql.toLowerCase(); + if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + return Promise.resolve([[{ size_mb: "12.50", name: "testdb" }], []]); + } + return defaultMockExecute(sql); + }; + + provider = new MySQLProvider(makeMySQLConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a size read with no result row leaves overview size absent", async () => { + mockExecuteFn = (sql: string) => { + const lower = sql.toLowerCase(); + if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + return Promise.resolve([[], []]); + } + return defaultMockExecute(sql); + }; + + provider = new MySQLProvider(makeMySQLConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a non-finite size leaves overview size absent", async () => { + mockExecuteFn = (sql: string) => { + const lower = sql.toLowerCase(); + if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + return Promise.resolve([[{ size_mb: "12.50", size_bytes: Number.POSITIVE_INFINITY, name: "testdb" }], []]); + } + return defaultMockExecute(sql); + }; + + provider = new MySQLProvider(makeMySQLConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a database that measures zero bytes keeps its measured zero size", async () => { + // The anti-vacuity twin of the tests above: `SUM(DATA_LENGTH + INDEX_LENGTH)` + // returns NULL over an empty schema, and that returned null aggregate is a + // measured zero the provider must keep publishing - never an absence. + mockExecuteFn = (sql: string) => { + const lower = sql.toLowerCase(); + if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + return Promise.resolve([[{ size_mb: "0.00", size_bytes: null, name: "testdb" }], []]); + } + return defaultMockExecute(sql); + }; + + provider = new MySQLProvider(makeMySQLConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(true); + expect(overview.databaseSizeBytes).toBe(0); + expect(overview.databaseSize).toBe("0 B"); + }); + test("does not call a MariaDB server MySQL", async () => { mockExecuteFn = mariaDBMockExecute; diff --git a/tests/integration/db/postgres-provider.test.ts b/tests/integration/db/postgres-provider.test.ts index d54b5bef..bc22bf7b 100644 --- a/tests/integration/db/postgres-provider.test.ts +++ b/tests/integration/db/postgres-provider.test.ts @@ -2349,6 +2349,86 @@ describe("PostgresProvider", () => { // 90061 seconds = 1d 1h 1m expect(overview.uptime).toBe("1d 1h 1m"); }); + + test("a size result without the expected column leaves overview size absent", async () => { + mockQueryFn = async (sql: string, params?: unknown[]) => { + const normalized = sql.trim().toLowerCase(); + if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { + return Promise.resolve({ rows: [{ database_size: "512 MB" }], fields: [], rowCount: 1 }); + } + return defaultMockQuery(sql, params); + }; + + provider = new PostgresProvider(makePgConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a size read with no result row leaves overview size absent", async () => { + mockQueryFn = async (sql: string, params?: unknown[]) => { + const normalized = sql.trim().toLowerCase(); + if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { + return Promise.resolve({ rows: [], fields: [], rowCount: 0 }); + } + return defaultMockQuery(sql, params); + }; + + provider = new PostgresProvider(makePgConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a non-finite size leaves overview size absent", async () => { + mockQueryFn = async (sql: string, params?: unknown[]) => { + const normalized = sql.trim().toLowerCase(); + if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { + return Promise.resolve({ + rows: [{ database_size: "512 MB", database_size_bytes: Number.POSITIVE_INFINITY }], + fields: [], + rowCount: 1, + }); + } + return defaultMockQuery(sql, params); + }; + + provider = new PostgresProvider(makePgConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(false); + expect(overview.databaseSize).toBe("N/A"); + }); + + test("a database that measures zero bytes keeps its measured zero size", async () => { + // The anti-vacuity twin of the tests above: `pg_database_size($1)` answers NULL + // when the aggregate has nothing to measure, and that returned null aggregate is + // a measured zero the provider must keep publishing - never an absence. + mockQueryFn = async (sql: string, params?: unknown[]) => { + const normalized = sql.trim().toLowerCase(); + if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { + return Promise.resolve({ + rows: [{ database_size: null, database_size_bytes: null }], + fields: [], + rowCount: 1, + }); + } + return defaultMockQuery(sql, params); + }; + + provider = new PostgresProvider(makePgConfig()); + await provider.connect(); + const overview = await provider.getOverview(); + + expect("databaseSizeBytes" in overview).toBe(true); + expect(overview.databaseSizeBytes).toBe(0); + expect(overview.databaseSize).toBe("0 B"); + }); }); // -------------------------------------------------------------------------- From c0c522285e7a3c799d0f15df9cf2665b32932132 Mon Sep 17 00:00:00 2001 From: cevheri Date: Tue, 8 Sep 2026 17:05:51 +0300 Subject: [PATCH 2/2] fix(postgres): correct the overview size read and its prose Takes over the review items on #627 so the branch can land. - `defaultMockQuery` takes one argument and the four new size tests passed two. That TS2554 is what halted `Lint, Typecheck and Build` at step nine, leaving lint, knip, build, build:lib and attw unmeasured rather than green. - `OVERVIEW_SIZE_SQL` drops `pg_size_pretty()`. `databaseSize` is `formatBytes()` over the byte figure now, the shape `mssql.ts` uses, so the pretty column was one nothing read and one whose value would disagree with the published string on every rounding boundary. Pinned by a test that fails without the change. - The prose said `pg_database_size()` answers NULL for an empty database. It is a function, not an aggregate, and measured on PostgreSQL 18 a freshly created database answers 7774735 bytes, never NULL and never zero. The returned-NULL test stays: it pins the shared helper's contract, and MySQL's `SUM()` over an empty schema is where that null row is real. - Fixtures no longer carry a `database_size` column the statement stopped selecting, and the formatter wrapped the three-condition `if` in the MySQL suite. Full suite against origin/main under identical conditions: 183 fail on both, the failing sets identical by name, 10702 pass against 10693. format, lint, typecheck, knip, the four drift guards, build, build:lib and attw all pass. --- docs/providers/postgres.md | 12 ++-- src/lib/db/providers/sql/postgres.ts | 1 - tests/integration/db/mysql-provider.test.ts | 24 ++++++-- .../integration/db/postgres-provider.test.ts | 55 +++++++++++++------ 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/docs/providers/postgres.md b/docs/providers/postgres.md index 701971e7..6629f5d9 100644 --- a/docs/providers/postgres.md +++ b/docs/providers/postgres.md @@ -723,10 +723,14 @@ base) fans these out in parallel. all user schemas. **Database size is absent, never zeroed, when it is not measured.** `getOverview()` sizes the -database with `pg_database_size`; a missing result row, or a row without `database_size_bytes`, is -no measurement at all, so `databaseSizeBytes` is omitted and `databaseSize` stays `"N/A"`. Only a -returned SQL `NULL` — an empty database — is a measured zero, and that reading is published as -`0`/`"0 B"`. +database with `pg_database_size($1)` and reads the byte figure only, the shape `mssql.ts` uses: +`databaseSize` is `formatBytes()` over that number, so no `pg_size_pretty()` column is selected. A +missing result row, or a row without `database_size_bytes`, is no measurement at all, so +`databaseSizeBytes` is omitted and `databaseSize` stays `"N/A"`. A returned SQL `NULL` is a measured +zero and is still published as `0`/`"0 B"`, which is the shared helper's contract rather than a +state this engine produces: `pg_database_size()` is a function, not an aggregate, and measured on +PostgreSQL 18 a freshly created database answers 7774735 bytes, never `NULL` and never zero. MySQL's +`SUM()` over an empty schema is where that null row is real. ### 7.1 When the cache hit ratio is not measurable diff --git a/src/lib/db/providers/sql/postgres.ts b/src/lib/db/providers/sql/postgres.ts index 794d42f7..8eea8314 100644 --- a/src/lib/db/providers/sql/postgres.ts +++ b/src/lib/db/providers/sql/postgres.ts @@ -575,7 +575,6 @@ const OVERVIEW_CONNECTIONS_SQL = ` // getOverview: database size, pretty-printed and raw bytes ($1 = database). const OVERVIEW_SIZE_SQL = ` SELECT - pg_size_pretty(pg_database_size($1)) as database_size, pg_database_size($1) as database_size_bytes `; diff --git a/tests/integration/db/mysql-provider.test.ts b/tests/integration/db/mysql-provider.test.ts index 5994f9eb..2787f877 100644 --- a/tests/integration/db/mysql-provider.test.ts +++ b/tests/integration/db/mysql-provider.test.ts @@ -1563,7 +1563,11 @@ describe("MySQLProvider", () => { test("a size result without the expected column leaves overview size absent", async () => { mockExecuteFn = (sql: string) => { const lower = sql.toLowerCase(); - if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + if ( + lower.includes("information_schema.tables") && + lower.includes("sum(data_length") && + !lower.includes("table_name") + ) { return Promise.resolve([[{ size_mb: "12.50", name: "testdb" }], []]); } return defaultMockExecute(sql); @@ -1580,7 +1584,11 @@ describe("MySQLProvider", () => { test("a size read with no result row leaves overview size absent", async () => { mockExecuteFn = (sql: string) => { const lower = sql.toLowerCase(); - if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + if ( + lower.includes("information_schema.tables") && + lower.includes("sum(data_length") && + !lower.includes("table_name") + ) { return Promise.resolve([[], []]); } return defaultMockExecute(sql); @@ -1597,7 +1605,11 @@ describe("MySQLProvider", () => { test("a non-finite size leaves overview size absent", async () => { mockExecuteFn = (sql: string) => { const lower = sql.toLowerCase(); - if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + if ( + lower.includes("information_schema.tables") && + lower.includes("sum(data_length") && + !lower.includes("table_name") + ) { return Promise.resolve([[{ size_mb: "12.50", size_bytes: Number.POSITIVE_INFINITY, name: "testdb" }], []]); } return defaultMockExecute(sql); @@ -1617,7 +1629,11 @@ describe("MySQLProvider", () => { // measured zero the provider must keep publishing - never an absence. mockExecuteFn = (sql: string) => { const lower = sql.toLowerCase(); - if (lower.includes("information_schema.tables") && lower.includes("sum(data_length") && !lower.includes("table_name")) { + if ( + lower.includes("information_schema.tables") && + lower.includes("sum(data_length") && + !lower.includes("table_name") + ) { return Promise.resolve([[{ size_mb: "0.00", size_bytes: null, name: "testdb" }], []]); } return defaultMockExecute(sql); diff --git a/tests/integration/db/postgres-provider.test.ts b/tests/integration/db/postgres-provider.test.ts index bc22bf7b..49bf8d3f 100644 --- a/tests/integration/db/postgres-provider.test.ts +++ b/tests/integration/db/postgres-provider.test.ts @@ -432,10 +432,10 @@ function defaultMockQuery(sql: string): Promise<{ rows: unknown[]; fields?: { na }); } - // getOverview: database size (pg_database_size with pretty + bytes) + // getOverview: database size, the byte figure only - `databaseSize` is formatBytes() over it. if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { return Promise.resolve({ - rows: [{ database_size: "512 MB", database_size_bytes: "536870912" }], + rows: [{ database_size_bytes: "536870912" }], fields: [], rowCount: 1, }); @@ -2351,12 +2351,12 @@ describe("PostgresProvider", () => { }); test("a size result without the expected column leaves overview size absent", async () => { - mockQueryFn = async (sql: string, params?: unknown[]) => { + mockQueryFn = async (sql: string) => { const normalized = sql.trim().toLowerCase(); if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { - return Promise.resolve({ rows: [{ database_size: "512 MB" }], fields: [], rowCount: 1 }); + return Promise.resolve({ rows: [{ unexpected_column: "512 MB" }], fields: [], rowCount: 1 }); } - return defaultMockQuery(sql, params); + return defaultMockQuery(sql); }; provider = new PostgresProvider(makePgConfig()); @@ -2368,12 +2368,12 @@ describe("PostgresProvider", () => { }); test("a size read with no result row leaves overview size absent", async () => { - mockQueryFn = async (sql: string, params?: unknown[]) => { + mockQueryFn = async (sql: string) => { const normalized = sql.trim().toLowerCase(); if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { return Promise.resolve({ rows: [], fields: [], rowCount: 0 }); } - return defaultMockQuery(sql, params); + return defaultMockQuery(sql); }; provider = new PostgresProvider(makePgConfig()); @@ -2385,16 +2385,16 @@ describe("PostgresProvider", () => { }); test("a non-finite size leaves overview size absent", async () => { - mockQueryFn = async (sql: string, params?: unknown[]) => { + mockQueryFn = async (sql: string) => { const normalized = sql.trim().toLowerCase(); if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { return Promise.resolve({ - rows: [{ database_size: "512 MB", database_size_bytes: Number.POSITIVE_INFINITY }], + rows: [{ database_size_bytes: Number.POSITIVE_INFINITY }], fields: [], rowCount: 1, }); } - return defaultMockQuery(sql, params); + return defaultMockQuery(sql); }; provider = new PostgresProvider(makePgConfig()); @@ -2406,19 +2406,22 @@ describe("PostgresProvider", () => { }); test("a database that measures zero bytes keeps its measured zero size", async () => { - // The anti-vacuity twin of the tests above: `pg_database_size($1)` answers NULL - // when the aggregate has nothing to measure, and that returned null aggregate is - // a measured zero the provider must keep publishing - never an absence. - mockQueryFn = async (sql: string, params?: unknown[]) => { + // The anti-vacuity twin of the tests above: a null aggregate is a MEASURED zero the + // provider must keep publishing, never an absence. It pins the shared helper's + // contract rather than a state this engine produces - `pg_database_size()` is a + // function, not an aggregate, and measured on PostgreSQL 18 a freshly created + // database answers 7774735 bytes, never NULL and never zero. MySQL's `SUM()` over an + // empty schema is where the null row is real. + mockQueryFn = async (sql: string) => { const normalized = sql.trim().toLowerCase(); if (normalized.includes("pg_database_size") && normalized.includes("database_size_bytes")) { return Promise.resolve({ - rows: [{ database_size: null, database_size_bytes: null }], + rows: [{ database_size_bytes: null }], fields: [], rowCount: 1, }); } - return defaultMockQuery(sql, params); + return defaultMockQuery(sql); }; provider = new PostgresProvider(makePgConfig()); @@ -2429,6 +2432,26 @@ describe("PostgresProvider", () => { expect(overview.databaseSizeBytes).toBe(0); expect(overview.databaseSize).toBe("0 B"); }); + + test("the overview size read asks for the byte figure only", async () => { + const seen: string[] = []; + mockQueryFn = async (sql: string) => { + seen.push(sql); + return defaultMockQuery(sql); + }; + + provider = new PostgresProvider(makePgConfig()); + await provider.connect(); + await provider.getOverview(); + + const sizeRead = seen.find((sql) => sql.includes("pg_database_size")); + expect(sizeRead).toBeDefined(); + // `databaseSize` is `formatBytes(databaseSizeBytes)` now, the shape `mssql.ts` + // uses, so a selected `pg_size_pretty()` would be a column nothing reads - and + // one whose value disagreed with the published string on every rounding boundary. + expect(sizeRead).not.toContain("pg_size_pretty"); + expect(sizeRead).toContain("database_size_bytes"); + }); }); // --------------------------------------------------------------------------