//! database: mockeddatabase, user: admin, password: password //! CREATE TABLE playground ( //! id serial PRIMARY KEY, //! data1 varchar (50) NOT NULL, //! data2 varchar (50) NOT NULL //! ); //! Note: In modern `PostgreSQL`, the default authentication method is scram-sha-256. //! This hash method is secured by a nonce, so this mocked server uses md5 instead. use std::time::Duration; use postgres::{Client, NoTls}; use socket_server_mocker::Instruction::{ReceiveMessage, SendMessage, StopExchange}; use socket_server_mocker::ServerMocker; #[test] fn postgres_insert_mock() { // Mock PostgreSQL server on a random port (default PostgresSQL port is 5432) let server = ServerMocker::tcp().unwrap(); // Add mock binary messages corresponding to client connection and authentication server .add_mock_instructions(vec![ ReceiveMessage, SendMessage(b"R\x00\x00\x00\x0c\x00\x00\x00\x05\x1cS\xa5\xf3".into()), ReceiveMessage, SendMessage( b"R\x00\x00\x00\x08\x00\x00\x00\x00S\x00\x00\x00\ \x16application_name\x00\x00S\x00\x00\x00\x19client_encoding\x00UTF8\x00S\x00\x00\x00\x17DateStyle\x00ISO, DMY\x00S\x00\x00\ \x00&default_transaction_read_only\x00off\x00S\x00\x00\x00\x17in_hot_standby\x00off\x00S\x00\x00\x00\x19integer_datetimes\x00on\x00S\ \x00\x00\x00\x1bIntervalStyle\x00postgres\x00S\x00\x00\x00\x14is_superuser\x00on\x00S\x00\x00\x00\x19server_encoding\x00UTF8\x00S\x00\ \x00\x004server_version\x0014.5 (Ubuntu 14.5-1.pgdg22.04+1)\x00S\x00\x00\x00 session_authorization\x00admin\x00S\x00\x00\x00\ #standard_conforming_strings\x00on\x00S\x00\x00\x00\x1aTimeZone\x00Europe/Paris\x00K\x00\x00\x00\x0c\x00\x00\x0a\x04EE\x04\xb9Z\x00\x00\x00\x05I" .into(), ), ]) .unwrap(); // Connect to local mocked PostgreSQL server let mut client = Client::configure() .host("localhost") .user("admin") .password("password") .dbname("mockeddatabase") .connect_timeout(Duration::from_secs(1)) .tcp_user_timeout(Duration::from_secs(1)) .keepalives(false) .port(server.port()) .connect(NoTls) .unwrap(); // Check connection message sent by the client to mock server is correct assert_eq!( b"\x00\x00\x00A\x00\x03\x00\x00client_encoding\x00UTF8\x00user\x00admin\x00database\x00mockeddatabase\x00\x00", server.pop_received_message().unwrap().as_slice() ); // Cannot verify the authentication message sent by the client to mock server because it contains a random salt server.pop_received_message().unwrap(); // Add mock instructions corresponding to the client INSERT query server .add_mock_instructions(vec![ ReceiveMessage, SendMessage(b"1\x00\x00\x00\x04t\x00\x00\x00\x0e\x00\x02\x00\x00\x04\x13\x00\x00\x04\x13n\x00\x00\x00\x04Z\x00\x00\x00\x05I".into()), ReceiveMessage, SendMessage(b"2\x00\x00\x00\x04C\x00\x00\x00\x0fINSERT 0 1\x00Z\x00\x00\x00\x05I".into()), StopExchange, // PG client will attempt proper session closing which we are not simulating, so just stop early ]) .unwrap(); // Execute the INSERT query client .execute( "INSERT INTO playground (data1, data2) VALUES ($1, $2)", &[&"test1", &"test2"], ) .unwrap(); // Check that no error has been raised by the mocked server assert!(server.pop_server_error().is_none()); } #[test] fn postgres_select_mock() { // Mock PostgreSQL server on a random free port (default PostgresSQL port is 5432) let server = ServerMocker::tcp().unwrap(); // Add mock binary messages corresponding to client connection and authentication server .add_mock_instructions(vec![ ReceiveMessage, SendMessage(b"R\x00\x00\x00\x0c\x00\x00\x00\x05\xb8(/\xf6".into()), ReceiveMessage, SendMessage(b"\ R\x00\x00\x00\x08\x00\x00\x00\x00S\x00\x00\x00\x16application_name\x00\x00S\x00\x00\x00\x19client_encoding\x00\ UTF8\x00S\x00\x00\x00\x17DateStyle\x00ISO, DMY\x00S\x00\x00\x00&default_transaction_read_only\x00off\x00\ S\x00\x00\x00\x17in_hot_standby\x00off\x00S\x00\x00\x00\x19integer_datetimes\x00on\x00S\x00\x00\x00\x1bIntervalStyle\x00\ postgres\x00S\x00\x00\x00\x14is_superuser\x00on\x00S\x00\x00\x00\x19server_encoding\x00UTF8\x00S\x00\x00\x004server_version\x00\ 14.5 (Ubuntu 14.5-1.pgdg22.04+1)\x00S\x00\x00\x00 session_authorization\x00admin\x00S\x00\x00\x00#standard_conforming_strings\x00\ on\x00S\x00\x00\x00\x1aTimeZone\x00Europe/Paris\x00K\x00\x00\x00\x0c\x00\x00\x0a\xb6\xe4kH\xa2Z\x00\x00\x00\x05I".into()), ]) .unwrap(); // Connect to local mocked PostgreSQL server let mut client = Client::configure() .host("localhost") .user("admin") .password("password") .dbname("mockeddatabase") .connect_timeout(Duration::from_secs(1)) .tcp_user_timeout(Duration::from_secs(1)) .keepalives(false) .port(server.port()) .connect(NoTls) .unwrap(); // Check connection message sent by the client to mock server is correct assert_eq!( b"\x00\x00\x00A\x00\x03\x00\x00client_encoding\x00UTF8\x00user\x00admin\x00database\x00mockeddatabase\x00\x00", server.pop_received_message().unwrap().as_slice() ); // Cannot verify the authentication message sent by the client to mock server because it contains a random salt server.pop_received_message().unwrap(); // Add mock instructions corresponding to the client SELECT query server .add_mock_instructions(vec![ ReceiveMessage, SendMessage(b"1\x00\x00\x00\x04t\x00\x00\x00\x06\x00\x00T\x00\x00\x00K\x00\x03id\x00\x00\x00@\x0a\x00\x01\x00\x00\x00\x17\x00\x04\xff\xff\xff\xff\x00\x00data1\x00\x00\x00@\x0a\x00\x02\x00\x00\x04\x13\xff\xff\x00\x00\x00\x36\x00\x00data2\x00\x00\x00@\x0a\x00\x03\x00\x00\x04\x13\xff\xff\x00\x00\x006\x00\x00Z\x00\x00\x00\x05I".into()), ReceiveMessage, SendMessage(b"2\x00\x00\x00\x04D\x00\x00\x00 \x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x05test1\x00\x00\x00\x05test2C\x00\x00\x00\x0dSELECT 1\x00Z\x00\x00\x00\x05I".into()), StopExchange, // PG client will attempt proper session closing which we are not simulating, so just stop early ]) .unwrap(); // Execute the client SELECT query let rows = client.query("SELECT * FROM playground", &[]).unwrap(); // Check the SELECT query result assert_eq!(1, rows.len()); assert_eq!(1, rows[0].get::<_, i32>("id")); assert_eq!("test1", rows[0].get::<_, String>("data1")); assert_eq!("test2", rows[0].get::<_, String>("data2")); // Check that no error has been raised by the mocked server assert!(server.pop_server_error().is_none()); }