diff --git a/public/og/compare/datagrip.png b/public/og/compare/datagrip.png index c153da7..31fdbea 100644 Binary files a/public/og/compare/datagrip.png and b/public/og/compare/datagrip.png differ diff --git a/public/og/compare/heidisql.png b/public/og/compare/heidisql.png index 70979b8..80ca14c 100644 Binary files a/public/og/compare/heidisql.png and b/public/og/compare/heidisql.png differ diff --git a/public/og/compare/navicat.png b/public/og/compare/navicat.png index 160b2cb..94c65f3 100644 Binary files a/public/og/compare/navicat.png and b/public/og/compare/navicat.png differ diff --git a/public/og/compare/postico.png b/public/og/compare/postico.png index 5faf7f7..a02739d 100644 Binary files a/public/og/compare/postico.png and b/public/og/compare/postico.png differ diff --git a/resources/data/comparisons.json b/resources/data/comparisons.json index e974a6f..afe9c26 100644 --- a/resources/data/comparisons.json +++ b/resources/data/comparisons.json @@ -300,12 +300,12 @@ "name": "DataGrip", "tagline": "Free, Native DataGrip Alternative for Mac.", "description": "Native DataGrip alternative for Mac. Free, open source, fast. JetBrains-quality editor without the IntelliJ weight.", - "longDescription": "DataGrip is JetBrains' database IDE, built on the IntelliJ platform in Java. It costs $99 the first year, $79 the second, $59 from year three; or $9.90 per month. TablePro is free, native macOS, and starts in 1 second. Same SQL editor power for daily work, without the IDE weight.", + "longDescription": "DataGrip is JetBrains' database IDE, built on the IntelliJ platform in Java. It costs $99 the first year, $79 the second, $59 from year three; or $10.90 per month. TablePro is free, native macOS, and starts in 1 second. Same SQL editor power for daily work, without the IDE weight.", "rows": [ { "label": "Price", "tablePro": "Free", - "competitor": "$99/yr individual (first year), $9.90/mo" + "competitor": "$99/yr individual (first year), $10.90/mo" }, { "label": "Open Source", @@ -364,7 +364,7 @@ } ], "prosTablePro": [ - "Free vs $99/yr (DataGrip Personal) or $9.90/mo", + "Free vs $99/yr (DataGrip Personal) or $10.90/mo", "Native macOS, not Java", "1 second start vs ~10 seconds", "80 MB RAM vs 1-2 GB", @@ -387,7 +387,7 @@ "datagrip alternative mac" ], "ogTableProMetaHtml": "$0 Forever
SwiftUI · 80 MB RAM
One-click connect", - "ogCompetitorMetaHtml": "$100/yr individual
IntelliJ . 1-2 GB RAM
1.1 GB download", + "ogCompetitorMetaHtml": "$99/yr individual
IntelliJ . 1-2 GB RAM
1.1 GB download", "ogPunchline": "Free. Native. No JetBrains tax.", "ogStatValue": "10x", "ogStatLabel": "LESS RAM", @@ -445,12 +445,12 @@ "name": "Navicat", "tagline": "Free, Native Navicat Alternative for Mac.", "description": "Native Navicat alternative for Mac. Free, open source, fast. 25 databases, AI assistant, iOS app.", - "longDescription": "Navicat Premium covers 7+ databases in one app. It costs $399 per year subscription or $1,299 for a perpetual license per user. TablePro covers 25 databases and is free, open source under AGPLv3. Native macOS, no per-database editions.", + "longDescription": "Navicat Premium covers 7+ databases in one app. It costs $399 per year subscription or $1,599 for a perpetual license per user. TablePro covers 25 databases and is free, open source under AGPLv3. Native macOS, no per-database editions.", "rows": [ { "label": "Price", "tablePro": "Free", - "competitor": "$399/yr or $1,299 perpetual" + "competitor": "$399/yr or $1,599 perpetual" }, { "label": "Open Source", @@ -514,7 +514,7 @@ } ], "prosTablePro": [ - "Free vs $399/yr or $1,299 perpetual", + "Free vs $399/yr or $1,599 perpetual", "25 databases in one app", "Native macOS", "AI SQL assistant in the free tier", @@ -538,7 +538,7 @@ "navicat too expensive" ], "ogTableProMetaHtml": "$0 Lifetime
SwiftUI · open source
One app, every DB", - "ogCompetitorMetaHtml": "$1,299+ bundle
Per-DB licenses
Closed source", + "ogCompetitorMetaHtml": "$1,599+ perpetual
Per-DB licenses
Closed source", "ogPunchline": "Free vs $30/month. One app for every database.", "ogStatValue": "100%", "ogStatLabel": "CHEAPER", @@ -906,7 +906,7 @@ "slug": "postico", "name": "Postico", "tagline": "Multi-Database Postico Alternative for Mac.", - "description": "Native, free, open-source Postico alternative for Mac. Postgres plus MySQL, MongoDB, Redis, SQLite, and 13 more.", + "description": "Native, free, open-source Postico alternative for Mac. Postgres plus MySQL, MongoDB, Redis, SQLite, and 20 more.", "longDescription": "Postico 2 is a polished native Mac client for PostgreSQL, Redshift, CockroachDB, and Greenplum. It costs $69 for a personal license (3 devices) or $99 per device for commercial use. TablePro is free and open source under AGPLv3, and covers 25 databases beyond Postgres.", "rows": [ { @@ -1001,7 +1001,7 @@ ], "chooseTablePro": "you work with multiple databases or want AI and iOS support in the free tier.", "chooseCompetitor": "Postgres, Redshift, CockroachDB, or Greenplum is your only database and you prefer paying once for a focused tool.", - "verdict": "Postico is excellent for Postgres alone. TablePro covers the same ground for Postgres plus 17 other databases, free.", + "verdict": "Postico is excellent for Postgres alone. TablePro covers the same ground for Postgres plus 24 other databases, free.", "keywords": [ "postico alternative", "tablepro vs postico", @@ -1010,8 +1010,8 @@ "multi-database mac client" ], "ogTableProMetaHtml": "FREE · 25 databases", - "ogCompetitorMetaHtml": "$40 · Postgres only", - "ogPunchline": "Postgres plus 17 more. Free. Native.", + "ogCompetitorMetaHtml": "$69 · Postgres only", + "ogPunchline": "Postgres plus 24 more. Free. Native.", "ogStatValue": "25", "ogStatLabel": "DATABASES", "benchmarks": { @@ -1229,7 +1229,7 @@ "name": "HeidiSQL", "tagline": "Native HeidiSQL Alternative for Mac.", "description": "Native HeidiSQL alternative for Mac. No Wine, no VM. Free and open source. 25 databases, AI assistant, iCloud sync.", - "longDescription": "HeidiSQL is a free GPL-2.0 database client built with Delphi for Windows. As of 2026 there is a separate Lazarus-based macOS preview build, but it remains a Windows-first app. TablePro is built natively for macOS in Swift and AppKit and covers MySQL, MariaDB, PostgreSQL, SQL Server, SQLite (the same databases HeidiSQL handles) plus 13 more.", + "longDescription": "HeidiSQL is a free GPL-2.0 database client built with Delphi for Windows. As of 2026 there is a separate Lazarus-based macOS preview build, but it remains a Windows-first app. TablePro is built natively for macOS in Swift and AppKit and covers MySQL, MariaDB, PostgreSQL, SQL Server, SQLite (the same databases HeidiSQL handles) plus 20 more.", "rows": [ { "label": "Price", diff --git a/tests/Feature/Seo/StaleClaimsTest.php b/tests/Feature/Seo/StaleClaimsTest.php index 498bfc4..20cc8fd 100644 --- a/tests/Feature/Seo/StaleClaimsTest.php +++ b/tests/Feature/Seo/StaleClaimsTest.php @@ -219,3 +219,89 @@ expect($routed)->toBe($authored); }); + +it('never implies a database count other than 25', function (): void { + /* + * Four entries carried counts from the era when TablePro shipped 18 — + * Postico promised "Postgres plus 17 other databases" and named five + * engines "and 13 more", HeidiSQL the same. Both read as 18, on pages whose + * own table said 25 two rows below. + * + * Relative phrasing is what rots: "plus N more" has to be re-derived every + * time the driver count moves, and nobody remembers to. This catches the + * ones that no longer add up. + */ + $entries = json_decode(file_get_contents(base_path('resources/data/comparisons.json')), true); + + $offenders = []; + + foreach ($entries as $entry) { + array_walk_recursive($entry, function ($value) use (&$offenders, $entry): void { + if (! is_string($value)) { + return; + } + + // "Postgres plus 24 other databases" / "... and 20 more" + if (! preg_match('/\b(?:plus|and)\s+(\d{1,3})\s+(?:other|more)\b/i', $value, $m)) { + return; + } + + /* + * Only the engines named in the same sentence count. Postico's + * verdict reads "excellent for Postgres alone. TablePro covers the + * same ground for Postgres plus 24 other databases" — counting the + * whole string sees Postgres twice and makes 25 look like 26. + */ + $before = substr($value, 0, strpos($value, $m[0])); + $sentence = preg_split('/(?<=[.!?])\s+/', $before); + + $named = preg_match_all( + '/\b(PostgreSQL|Postgres|MySQL|MariaDB|MongoDB|Redis|SQLite|SQL Server|Redshift|CockroachDB|Greenplum|Snowflake|Cassandra|DuckDB|Oracle|ClickHouse)\b/i', + (string) end($sentence), + ); + + if ($named + (int) $m[1] !== 25) { + $offenders[] = "{$entry['slug']}: \"{$m[0]}\" after {$named} named engines = " . ($named + (int) $m[1]); + } + }); + } + + expect($offenders)->toBe([], "Database counts that do not total 25:\n " . implode("\n ", $offenders)); +}); + +it('quotes one price per competitor claim, consistently within an entry', function (): void { + /* + * The OG card is generated from its own fields, so it drifts silently from + * the page it advertises: Postico's said $40 against $69/$99 in the entry, + * DataGrip's said $100 against $99. Nobody sees an OG image until it is + * already being shared. + */ + $entries = json_decode(file_get_contents(base_path('resources/data/comparisons.json')), true); + + foreach ($entries as $entry) { + if (! isset($entry['ogCompetitorMetaHtml'])) { + continue; + } + + preg_match_all('/\$[\d,]+(?:\.\d{2})?/', $entry['ogCompetitorMetaHtml'], $onCard); + + if ($onCard[0] === []) { + continue; + } + + $inEntry = []; + array_walk_recursive($entry, function ($value) use (&$inEntry): void { + if (is_string($value)) { + preg_match_all('/\$[\d,]+(?:\.\d{2})?/', $value, $found); + $inEntry = array_merge($inEntry, $found[0]); + } + }); + + foreach ($onCard[0] as $price) { + expect(array_count_values($inEntry)[$price] ?? 0)->toBeGreaterThan( + 1, + "{$entry['slug']}: the OG card quotes {$price}, which appears nowhere else in the entry", + ); + } + } +});