@@ -13,6 +13,10 @@ import {
1313 assertRejects ,
1414 assertThrows ,
1515} from "jsr:@std/assert@1.0.10" ;
16+ import {
17+ MockError ,
18+ stub ,
19+ } from "jsr:@std/testing/mock" ;
1620import { getMainConfiguration } from "./config.ts" ;
1721import type { PoolClient , QueryClient } from "../client.ts" ;
1822import type { ClientOptions } from "../connection/connection_params.ts" ;
@@ -694,9 +698,13 @@ Deno.test(
694698) ;
695699
696700// This test depends on the assumption that all clients will default to
697- // one reconneciton by default
701+ // one reconnection by default
702+ //
703+ // It also specifically tests the case where the database system sends a
704+ // graceful disconnect, not the case where the database connection is just
705+ // closed due to the database process crashing or being killed.
698706Deno . test (
699- "Default reconnection" ,
707+ "Default reconnection after graceful database connection close " ,
700708 withClient ( async ( client ) => {
701709 await assertRejects (
702710 ( ) =>
@@ -714,6 +722,66 @@ Deno.test(
714722 } ) ,
715723) ;
716724
725+ // This test depends on the assumption that all clients will default to
726+ // one reconnection by default
727+ Deno . test (
728+ "Default reconnection after connection drop" ,
729+ withClient ( async ( client ) => {
730+ // Test that connection is OK
731+ const { rows : result } = await client . queryObject < { res : number } > ( {
732+ text : `SELECT 1` ,
733+ fields : [ "res" ] ,
734+ } ) ;
735+ assertEquals ( result [ 0 ] . res , 1 ) ;
736+
737+ // Use mock simulate irrecoverably broken pipe
738+ const connWrite = stub (
739+ WritableStreamDefaultWriter . prototype ,
740+ "write" ,
741+ ( _buffer ) => {
742+ throw new Deno . errors . BrokenPipe ( ) ;
743+ }
744+ ) ;
745+ try {
746+ await assertRejects (
747+ ( ) =>
748+ client . queryArray `SELECT 1` ,
749+ Deno . errors . BrokenPipe ,
750+ ) ;
751+ } finally {
752+ connWrite . restore ( ) ;
753+ }
754+
755+ // Use mock simulate broken pipe, that can be healed by reconnecting
756+ const connWrite2 = stub (
757+ WritableStreamDefaultWriter . prototype ,
758+ "write" ,
759+ ( _buffer ) => {
760+ connWrite2 . restore ( ) ;
761+ throw new Deno . errors . BrokenPipe ( ) ;
762+ }
763+ ) ;
764+ try {
765+ const { rows : result2 } = await client . queryObject < { res : number } > ( {
766+ text : `SELECT 1` ,
767+ fields : [ "res" ] ,
768+ } ) ;
769+ assertEquals ( result2 [ 0 ] . res , 1 ) ;
770+ } finally {
771+ // Restoring must fail here because it already happened
772+ //
773+ // If this fails, it means the Mock was never invoked and the test must be updated!
774+ assertThrows (
775+ ( ) =>
776+ connWrite2 . restore ( ) ,
777+ MockError ,
778+ ) ;
779+ }
780+
781+ assertEquals ( client . connected , true ) ;
782+ } ) ,
783+ ) ;
784+
717785Deno . test (
718786 "Handling of debug notices" ,
719787 withClient ( async ( client ) => {
@@ -1498,6 +1566,41 @@ Deno.test(
14981566 } ) ,
14991567) ;
15001568
1569+ Deno . test (
1570+ "Transaction fails after connection drop" ,
1571+ withClient ( async ( client ) => {
1572+ const name = "transactionFailsOnConnectionDrop" ;
1573+ const transaction = client . createTransaction ( name ) ;
1574+
1575+ await transaction . begin ( ) ;
1576+
1577+ // Use mock simulate broken pipe
1578+ const connWrite = stub (
1579+ WritableStreamDefaultWriter . prototype ,
1580+ "write" ,
1581+ ( _buffer ) => {
1582+ throw new Deno . errors . BrokenPipe ( ) ;
1583+ }
1584+ ) ;
1585+ try {
1586+ await assertRejects (
1587+ ( ) =>
1588+ transaction . queryArray `SELECT 1` ,
1589+ TransactionError ,
1590+ ) ;
1591+ } finally {
1592+ connWrite . restore ( ) ;
1593+ }
1594+
1595+ // Test that connection is OK afterward
1596+ const { rows : result } = await client . queryObject < { res : number } > ( {
1597+ text : `SELECT 1` ,
1598+ fields : [ "res" ] ,
1599+ } ) ;
1600+ assertEquals ( result [ 0 ] . res , 1 ) ;
1601+ } ) ,
1602+ ) ;
1603+
15011604Deno . test (
15021605 "Transaction savepoints" ,
15031606 withClient ( async ( client ) => {
0 commit comments