You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by al...@apache.org on 2025/03/11 14:57:56 UTC

(datafusion) branch main updated: Add `insta` / snapshot testing to CLI & set up AWS mock (#13672)

This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 51f0833dfe Add `insta` / snapshot testing to CLI & set up AWS mock (#13672)
51f0833dfe is described below

commit 51f0833dfeb7d191d1aaf119f7fd83c501d4f6b9
Author: Dmitrii Blaginin <dm...@blaginin.me>
AuthorDate: Tue Mar 11 14:57:44 2025 +0000

    Add `insta` / snapshot testing to CLI & set up AWS mock (#13672)
    
    * Do not normalize values
    
    * Fix tests & update docs
    
    * Prettier
    
    * Lowercase config params
    
    * Add snap to CLI & set up AWS mock
    
    * Refactor tests
    
    * Unify transform and parse
    
    * Fix tests
    
    * Setup CLI
    
    * Show minio output
    
    * Format Cargo.toml
    
    * Do not hardcode AWS params
    
    * Test options parsing
    
    * Add allow http
    
    * Fix aws build
    
    * Fix ip
    
    * Remove slash ☠️
    
    * Format cargo toml
    
    * Remove integration_setup.bash
    
    * Update docs
    
    * Do not hardcode test names
    
    * Relock cargo
    
    * Remove aws sdk and set up minio in-place
    
    * Nit: Add missing ready local to the docs
    
    * Fix backslash test
    
    * Add missing backslash
    
    * put integration scripts in a separate folder
    
    * Move s3 tests from extended to rust flow
    
    * Reorganise the docs
    
    * Prettier
    
    * Do not use rust container to get docker
    
    * Add missing protobuf
    
    * revert change to extended.yml
    
    ---------
    
    Co-authored-by: Andrew Lamb <an...@nerdnetworks.org>
---
 .github/workflows/rust.yml                         |  32 +++++-
 Cargo.lock                                         |  49 ++++++++
 datafusion-cli/CONTRIBUTING.md                     |  75 +++++++++++++
 datafusion-cli/Cargo.toml                          |   2 +
 datafusion-cli/tests/cli_integration.rs            | 123 +++++++++++++++++----
 datafusion-cli/tests/snapshots/aws_options.snap    |  25 +++++
 .../tests/snapshots/cli@load_local_csv.sql.snap    |  26 +++++
 .../tests/snapshots/cli@load_s3_csv.sql.snap       |  26 +++++
 datafusion-cli/tests/snapshots/cli@select.sql.snap |  23 ++++
 .../tests/snapshots/cli_format@automatic.snap      |  21 ++++
 datafusion-cli/tests/snapshots/cli_format@csv.snap |  18 +++
 .../tests/snapshots/cli_format@json.snap           |  17 +++
 .../tests/snapshots/cli_format@nd-json.snap        |  17 +++
 .../tests/snapshots/cli_format@table.snap          |  21 ++++
 datafusion-cli/tests/snapshots/cli_format@tsv.snap |  18 +++
 .../tests/snapshots/cli_quick_test@backslash.snap  |  17 +++
 .../tests/snapshots/cli_quick_test@batch_size.snap |  21 ++++
 .../tests/snapshots/cli_quick_test@files.snap      |  19 ++++
 .../tests/snapshots/cli_quick_test@statements.snap |  24 ++++
 .../{data/backslash.txt => sql/backslash.sql}      |   0
 .../tests/sql/integration/load_local_csv.sql       |   6 +
 .../tests/sql/integration/load_s3_csv.sql          |   5 +
 .../tests/{data/sql.txt => sql/select.sql}         |   0
 docs/source/contributor-guide/testing.md           |  12 ++
 24 files changed, 572 insertions(+), 25 deletions(-)

diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 4618600585..2f7ab418ca 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -171,21 +171,41 @@ jobs:
     name: cargo test (amd64)
     needs: linux-build-lib
     runs-on: ubuntu-latest
-    container:
-      image: amd64/rust
     steps:
       - uses: actions/checkout@v4
         with:
           submodules: true
           fetch-depth: 1
       - name: Setup Rust toolchain
-        uses: ./.github/actions/setup-builder
-        with:
-          rust-version: stable
+        run: rustup toolchain install stable
+      - name: Install Protobuf Compiler
+        run: sudo apt-get install -y protobuf-compiler
+      - name: Setup Minio - S3-compatible storage
+        run: |
+          docker run -d --name minio-container \
+            -p 9000:9000 \
+            -e MINIO_ROOT_USER=TEST-DataFusionLogin -e MINIO_ROOT_PASSWORD=TEST-DataFusionPassword \
+            -v $(pwd)/datafusion/core/tests/data:/source quay.io/minio/minio \
+            server /data
+          docker exec minio-container /bin/sh -c "\
+            mc ready local
+            mc alias set localminio http://localhost:9000 TEST-DataFusionLogin TEST-DataFusionPassword && \
+            mc mb localminio/data && \
+            mc cp -r /source/* localminio/data"
       - name: Run tests (excluding doctests)
-        run: cargo test --profile ci --exclude datafusion-examples --exclude ffi_example_table_provider --exclude datafusion-benchmarks --workspace --lib --tests --bins --features serde,avro,json,backtrace,integration-tests
+        env:
+          RUST_BACKTRACE: 1
+          AWS_ENDPOINT: http://127.0.0.1:9000
+          AWS_ACCESS_KEY_ID: TEST-DataFusionLogin
+          AWS_SECRET_ACCESS_KEY: TEST-DataFusionPassword
+          TEST_STORAGE_INTEGRATION: 1
+          AWS_ALLOW_HTTP: true
+        run: cargo test --profile ci --exclude datafusion-examples --exclude ffi_example_table_provider --exclude datafusion-benchmarks --workspace --lib --tests --bins --features avro,json,backtrace,integration-tests
       - name: Verify Working Directory Clean
         run: git diff --exit-code
+      - name: Minio Output
+        if: ${{ !cancelled() }}
+        run: docker logs minio-container
 
   linux-test-example:
     name: cargo examples (amd64)
diff --git a/Cargo.lock b/Cargo.lock
index 87275112d2..430468f763 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1883,6 +1883,8 @@ dependencies = [
  "dirs",
  "env_logger",
  "futures",
+ "insta",
+ "insta-cmd",
  "mimalloc",
  "object_store",
  "parking_lot",
@@ -3031,6 +3033,19 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
 
+[[package]]
+name = "globset"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
 [[package]]
 name = "h2"
 version = "0.3.26"
@@ -3579,6 +3594,34 @@ version = "2.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
 
+[[package]]
+name = "insta"
+version = "1.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
+dependencies = [
+ "console",
+ "globset",
+ "linked-hash-map",
+ "once_cell",
+ "pin-project",
+ "regex",
+ "serde",
+ "similar",
+ "walkdir",
+]
+
+[[package]]
+name = "insta-cmd"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffeeefa927925cced49ccb01bf3e57c9d4cd132df21e576eb9415baeab2d3de6"
+dependencies = [
+ "insta",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "integer-encoding"
 version = "3.0.4"
@@ -3809,6 +3852,12 @@ dependencies = [
  "escape8259",
 ]
 
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
 [[package]]
 name = "linux-raw-sys"
 version = "0.4.15"
diff --git a/datafusion-cli/CONTRIBUTING.md b/datafusion-cli/CONTRIBUTING.md
new file mode 100644
index 0000000000..4b464dffc5
--- /dev/null
+++ b/datafusion-cli/CONTRIBUTING.md
@@ -0,0 +1,75 @@
+<!---
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+# Development instructions
+
+## Running Tests
+
+Tests can be run using `cargo`
+
+```shell
+cargo test
+```
+
+## Running Storage Integration Tests
+
+By default, storage integration tests are not run. To run them you will need to set `TEST_STORAGE_INTEGRATION=1` and
+then provide the necessary configuration for that object store.
+
+For some of the tests, [snapshots](https://datafusion.apache.org/contributor-guide/testing.html#snapshot-testing) are used.
+
+### AWS
+
+To test the S3 integration against [Minio](https://github.com/minio/minio)
+
+First start up a container with Minio and load test files.
+
+```shell
+docker run -d \
+  --name datafusion-test-minio \
+  -p 9000:9000 \
+  -e MINIO_ROOT_USER=TEST-DataFusionLogin \
+  -e MINIO_ROOT_PASSWORD=TEST-DataFusionPassword \
+  -v $(pwd)/../datafusion/core/tests/data:/source \
+  quay.io/minio/minio server /data
+
+docker exec datafusion-test-minio /bin/sh -c "\
+  mc ready local
+  mc alias set localminio http://localhost:9000 TEST-DataFusionLogin TEST-DataFusionPassword && \
+  mc mb localminio/data && \
+  mc cp -r /source/* localminio/data"
+```
+
+Setup environment
+
+```shell
+export TEST_STORAGE_INTEGRATION=1
+export AWS_ACCESS_KEY_ID=TEST-DataFusionLogin
+export AWS_SECRET_ACCESS_KEY=TEST-DataFusionPassword
+export AWS_ENDPOINT=http://127.0.0.1:9000
+export AWS_ALLOW_HTTP=true
+```
+
+Note that `AWS_ENDPOINT` is set without slash at the end.
+
+Run tests
+
+```shell
+cargo test
+```
diff --git a/datafusion-cli/Cargo.toml b/datafusion-cli/Cargo.toml
index 13246804cd..258fd995a7 100644
--- a/datafusion-cli/Cargo.toml
+++ b/datafusion-cli/Cargo.toml
@@ -67,5 +67,7 @@ url = { workspace = true }
 [dev-dependencies]
 assert_cmd = "2.0"
 ctor = { workspace = true }
+insta = { version = "1.41.1", features = ["glob", "filters"] }
+insta-cmd = "0.6.0"
 predicates = "3.0"
 rstest = { workspace = true }
diff --git a/datafusion-cli/tests/cli_integration.rs b/datafusion-cli/tests/cli_integration.rs
index fa170ae192..a54a920e97 100644
--- a/datafusion-cli/tests/cli_integration.rs
+++ b/datafusion-cli/tests/cli_integration.rs
@@ -17,10 +17,24 @@
 
 use std::process::Command;
 
-use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt};
-use predicates::prelude::predicate;
 use rstest::rstest;
 
+use insta::{glob, Settings};
+use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
+use std::{env, fs};
+
+fn cli() -> Command {
+    Command::new(get_cargo_bin("datafusion-cli"))
+}
+
+fn make_settings() -> Settings {
+    let mut settings = Settings::clone_current();
+    settings.set_prepend_module_to_snapshot(false);
+    settings.add_filter(r"Elapsed .* seconds\.", "[ELAPSED]");
+    settings.add_filter(r"DataFusion CLI v.*", "[CLI_VERSION]");
+    settings
+}
+
 #[cfg(test)]
 #[ctor::ctor]
 fn init() {
@@ -28,35 +42,106 @@ fn init() {
     let _ = env_logger::try_init();
 }
 
-// Disabled due to https://github.com/apache/datafusion/issues/10793
-#[cfg(not(target_family = "windows"))]
 #[rstest]
-#[case::exec_from_commands(
-    ["--command", "select 1", "--format", "json", "-q"],
-    "[{\"Int64(1)\":1}]\n"
-)]
 #[case::exec_multiple_statements(
-    ["--command", "select 1; select 2;", "--format", "json", "-q"],
-    "[{\"Int64(1)\":1}]\n[{\"Int64(2)\":2}]\n"
+    "statements",
+    ["--command", "select 1; select 2;", "-q"],
 )]
 #[case::exec_backslash(
-    ["--file", "tests/data/backslash.txt", "--format", "json", "-q"],
-    "[{\"Utf8(\\\"\\\\\\\")\":\"\\\\\",\"Utf8(\\\"\\\\\\\\\\\")\":\"\\\\\\\\\",\"Utf8(\\\"\\\\\\\\\\\\\\\\\\\\\\\")\":\"\\\\\\\\\\\\\\\\\\\\\",\"Utf8(\\\"dsdsds\\\\\\\\\\\\\\\\\\\")\":\"dsdsds\\\\\\\\\\\\\\\\\",\"Utf8(\\\"\\\\t\\\")\":\"\\\\t\",\"Utf8(\\\"\\\\0\\\")\":\"\\\\0\",\"Utf8(\\\"\\\\n\\\")\":\"\\\\n\"}]\n"
+    "backslash",
+    ["--file", "tests/sql/backslash.sql", "--format", "json", "-q"],
 )]
 #[case::exec_from_files(
-    ["--file", "tests/data/sql.txt", "--format", "json", "-q"],
-    "[{\"Int64(1)\":1}]\n"
+    "files",
+    ["--file", "tests/sql/select.sql", "-q"],
 )]
 #[case::set_batch_size(
-    ["--command", "show datafusion.execution.batch_size", "--format", "json", "-q", "-b", "1"],
-    "[{\"name\":\"datafusion.execution.batch_size\",\"value\":\"1\"}]\n"
+    "batch_size",
+    ["--command", "show datafusion.execution.batch_size", "-q", "-b", "1"],
 )]
 #[test]
 fn cli_quick_test<'a>(
+    #[case] snapshot_name: &'a str,
     #[case] args: impl IntoIterator<Item = &'a str>,
-    #[case] expected: &str,
 ) {
-    let mut cmd = Command::cargo_bin("datafusion-cli").unwrap();
+    let mut settings = make_settings();
+    settings.set_snapshot_suffix(snapshot_name);
+    let _bound = settings.bind_to_scope();
+
+    let mut cmd = cli();
     cmd.args(args);
-    cmd.assert().stdout(predicate::eq(expected));
+
+    assert_cmd_snapshot!(cmd);
+}
+
+#[rstest]
+#[case("csv")]
+#[case("tsv")]
+#[case("table")]
+#[case("json")]
+#[case("nd-json")]
+#[case("automatic")]
+#[test]
+fn test_cli_format<'a>(#[case] format: &'a str) {
+    let mut settings = make_settings();
+    settings.set_snapshot_suffix(format);
+    let _bound = settings.bind_to_scope();
+
+    let mut cmd = cli();
+    cmd.args(["--command", "select 1", "-q", "--format", format]);
+
+    assert_cmd_snapshot!(cmd);
+}
+
+#[tokio::test]
+async fn test_cli() {
+    if env::var("TEST_STORAGE_INTEGRATION").is_err() {
+        eprintln!("Skipping external storages integration tests");
+        return;
+    }
+
+    let settings = make_settings();
+    let _bound = settings.bind_to_scope();
+
+    glob!("sql/integration/*.sql", |path| {
+        let input = fs::read_to_string(path).unwrap();
+        assert_cmd_snapshot!(cli().pass_stdin(input))
+    });
+}
+
+#[tokio::test]
+async fn test_aws_options() {
+    // Separate test is needed to pass aws as options in sql and not via env
+
+    if env::var("TEST_STORAGE_INTEGRATION").is_err() {
+        eprintln!("Skipping external storages integration tests");
+        return;
+    }
+
+    let settings = make_settings();
+    let _bound = settings.bind_to_scope();
+
+    let access_key_id =
+        env::var("AWS_ACCESS_KEY_ID").expect("AWS_ACCESS_KEY_ID is not set");
+    let secret_access_key =
+        env::var("AWS_SECRET_ACCESS_KEY").expect("AWS_SECRET_ACCESS_KEY is not set");
+    let endpoint_url = env::var("AWS_ENDPOINT").expect("AWS_ENDPOINT is not set");
+
+    let input = format!(
+        r#"CREATE EXTERNAL TABLE CARS
+STORED AS CSV
+LOCATION 's3://data/cars.csv'
+OPTIONS(
+    'aws.access_key_id' '{}',
+    'aws.secret_access_key' '{}',
+    'aws.endpoint' '{}',
+    'aws.allow_http' 'true'
+);
+
+SELECT * FROM CARS limit 1;
+"#,
+        access_key_id, secret_access_key, endpoint_url
+    );
+
+    assert_cmd_snapshot!(cli().env_clear().pass_stdin(input));
 }
diff --git a/datafusion-cli/tests/snapshots/aws_options.snap b/datafusion-cli/tests/snapshots/aws_options.snap
new file mode 100644
index 0000000000..283cf57bc6
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/aws_options.snap
@@ -0,0 +1,25 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args: []
+  stdin: "CREATE EXTERNAL TABLE CARS\nSTORED AS CSV\nLOCATION 's3://data/cars.csv'\nOPTIONS(\n    'aws.access_key_id' 'TEST-DataFusionLogin',\n    'aws.secret_access_key' 'TEST-DataFusionPassword',\n    'aws.endpoint' 'http://127.0.0.1:9000',\n    'aws.allow_http' 'true'\n);\n\nSELECT * FROM CARS limit 1;\n"
+---
+success: true
+exit_code: 0
+----- stdout -----
+[CLI_VERSION]
+0 row(s) fetched. 
+[ELAPSED]
+
++-----+-------+---------------------+
+| car | speed | time                |
++-----+-------+---------------------+
+| red | 20.0  | 1996-04-12T12:05:03 |
++-----+-------+---------------------+
+1 row(s) fetched. 
+[ELAPSED]
+
+\q
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli@load_local_csv.sql.snap b/datafusion-cli/tests/snapshots/cli@load_local_csv.sql.snap
new file mode 100644
index 0000000000..029d5f8d5b
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli@load_local_csv.sql.snap
@@ -0,0 +1,26 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args: []
+  stdin: "CREATE EXTERNAL TABLE CARS\nSTORED AS CSV\nLOCATION '../datafusion/core/tests/data/cars.csv'\nOPTIONS ('has_header' 'TRUE');\n\nSELECT * FROM CARS limit 1;"
+input_file: tests/sql/load_local_csv.sql
+---
+success: true
+exit_code: 0
+----- stdout -----
+[CLI_VERSION]
+0 row(s) fetched. 
+[ELAPSED]
+
++-----+-------+---------------------+
+| car | speed | time                |
++-----+-------+---------------------+
+| red | 20.0  | 1996-04-12T12:05:03 |
++-----+-------+---------------------+
+1 row(s) fetched. 
+[ELAPSED]
+
+\q
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli@load_s3_csv.sql.snap b/datafusion-cli/tests/snapshots/cli@load_s3_csv.sql.snap
new file mode 100644
index 0000000000..858989621a
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli@load_s3_csv.sql.snap
@@ -0,0 +1,26 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args: []
+  stdin: "CREATE EXTERNAL TABLE CARS\nSTORED AS CSV\nLOCATION 's3://data/cars.csv';\n\nSELECT * FROM CARS limit 1;"
+input_file: tests/sql/load_s3_csv.sql
+---
+success: true
+exit_code: 0
+----- stdout -----
+[CLI_VERSION]
+0 row(s) fetched. 
+[ELAPSED]
+
++-----+-------+---------------------+
+| car | speed | time                |
++-----+-------+---------------------+
+| red | 20.0  | 1996-04-12T12:05:03 |
++-----+-------+---------------------+
+1 row(s) fetched. 
+[ELAPSED]
+
+\q
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli@select.sql.snap b/datafusion-cli/tests/snapshots/cli@select.sql.snap
new file mode 100644
index 0000000000..c137d9fe2b
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli@select.sql.snap
@@ -0,0 +1,23 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args: []
+  stdin: select 1;
+input_file: tests/sql/select.sql
+---
+success: true
+exit_code: 0
+----- stdout -----
+[CLI_VERSION]
++----------+
+| Int64(1) |
++----------+
+| 1        |
++----------+
+1 row(s) fetched. 
+[ELAPSED]
+
+\q
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@automatic.snap b/datafusion-cli/tests/snapshots/cli_format@automatic.snap
new file mode 100644
index 0000000000..2591f493e9
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@automatic.snap
@@ -0,0 +1,21 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - automatic
+---
+success: true
+exit_code: 0
+----- stdout -----
++----------+
+| Int64(1) |
++----------+
+| 1        |
++----------+
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@csv.snap b/datafusion-cli/tests/snapshots/cli_format@csv.snap
new file mode 100644
index 0000000000..c41b042298
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@csv.snap
@@ -0,0 +1,18 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - csv
+---
+success: true
+exit_code: 0
+----- stdout -----
+Int64(1)
+1
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@json.snap b/datafusion-cli/tests/snapshots/cli_format@json.snap
new file mode 100644
index 0000000000..8f804a337c
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@json.snap
@@ -0,0 +1,17 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - json
+---
+success: true
+exit_code: 0
+----- stdout -----
+[{"Int64(1)":1}]
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@nd-json.snap b/datafusion-cli/tests/snapshots/cli_format@nd-json.snap
new file mode 100644
index 0000000000..7b4ce1e253
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@nd-json.snap
@@ -0,0 +1,17 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - nd-json
+---
+success: true
+exit_code: 0
+----- stdout -----
+{"Int64(1)":1}
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@table.snap b/datafusion-cli/tests/snapshots/cli_format@table.snap
new file mode 100644
index 0000000000..9991418246
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@table.snap
@@ -0,0 +1,21 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - table
+---
+success: true
+exit_code: 0
+----- stdout -----
++----------+
+| Int64(1) |
++----------+
+| 1        |
++----------+
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_format@tsv.snap b/datafusion-cli/tests/snapshots/cli_format@tsv.snap
new file mode 100644
index 0000000000..968268c31d
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_format@tsv.snap
@@ -0,0 +1,18 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1
+    - "-q"
+    - "--format"
+    - tsv
+---
+success: true
+exit_code: 0
+----- stdout -----
+Int64(1)
+1
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_quick_test@backslash.snap b/datafusion-cli/tests/snapshots/cli_quick_test@backslash.snap
new file mode 100644
index 0000000000..c01699146a
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_quick_test@backslash.snap
@@ -0,0 +1,17 @@
+---
+source: datafusion-cli/tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--file"
+    - tests/sql/backslash.sql
+    - "--format"
+    - json
+    - "-q"
+---
+success: true
+exit_code: 0
+----- stdout -----
+[{"Utf8(\"\\\")":"\\","Utf8(\"\\\\\")":"\\\\","Utf8(\"\\\\\\\\\\\")":"\\\\\\\\\\","Utf8(\"dsdsds\\\\\\\\\")":"dsdsds\\\\\\\\","Utf8(\"\\t\")":"\\t","Utf8(\"\\0\")":"\\0","Utf8(\"\\n\")":"\\n"}]
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_quick_test@batch_size.snap b/datafusion-cli/tests/snapshots/cli_quick_test@batch_size.snap
new file mode 100644
index 0000000000..c27d527df0
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_quick_test@batch_size.snap
@@ -0,0 +1,21 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - show datafusion.execution.batch_size
+    - "-q"
+    - "-b"
+    - "1"
+---
+success: true
+exit_code: 0
+----- stdout -----
++---------------------------------+-------+
+| name                            | value |
++---------------------------------+-------+
+| datafusion.execution.batch_size | 1     |
++---------------------------------+-------+
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_quick_test@files.snap b/datafusion-cli/tests/snapshots/cli_quick_test@files.snap
new file mode 100644
index 0000000000..7c44e41729
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_quick_test@files.snap
@@ -0,0 +1,19 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--file"
+    - tests/sql/select.sql
+    - "-q"
+---
+success: true
+exit_code: 0
+----- stdout -----
++----------+
+| Int64(1) |
++----------+
+| 1        |
++----------+
+
+----- stderr -----
diff --git a/datafusion-cli/tests/snapshots/cli_quick_test@statements.snap b/datafusion-cli/tests/snapshots/cli_quick_test@statements.snap
new file mode 100644
index 0000000000..3b975bb6a9
--- /dev/null
+++ b/datafusion-cli/tests/snapshots/cli_quick_test@statements.snap
@@ -0,0 +1,24 @@
+---
+source: tests/cli_integration.rs
+info:
+  program: datafusion-cli
+  args:
+    - "--command"
+    - select 1; select 2;
+    - "-q"
+---
+success: true
+exit_code: 0
+----- stdout -----
++----------+
+| Int64(1) |
++----------+
+| 1        |
++----------+
++----------+
+| Int64(2) |
++----------+
+| 2        |
++----------+
+
+----- stderr -----
diff --git a/datafusion-cli/tests/data/backslash.txt b/datafusion-cli/tests/sql/backslash.sql
similarity index 100%
rename from datafusion-cli/tests/data/backslash.txt
rename to datafusion-cli/tests/sql/backslash.sql
diff --git a/datafusion-cli/tests/sql/integration/load_local_csv.sql b/datafusion-cli/tests/sql/integration/load_local_csv.sql
new file mode 100644
index 0000000000..8920c48c5f
--- /dev/null
+++ b/datafusion-cli/tests/sql/integration/load_local_csv.sql
@@ -0,0 +1,6 @@
+CREATE EXTERNAL TABLE CARS
+STORED AS CSV
+LOCATION '../datafusion/core/tests/data/cars.csv'
+OPTIONS ('has_header' 'TRUE');
+
+SELECT * FROM CARS limit 1;
\ No newline at end of file
diff --git a/datafusion-cli/tests/sql/integration/load_s3_csv.sql b/datafusion-cli/tests/sql/integration/load_s3_csv.sql
new file mode 100644
index 0000000000..10c2e38b97
--- /dev/null
+++ b/datafusion-cli/tests/sql/integration/load_s3_csv.sql
@@ -0,0 +1,5 @@
+CREATE EXTERNAL TABLE CARS
+STORED AS CSV
+LOCATION 's3://data/cars.csv';
+
+SELECT * FROM CARS limit 1;
\ No newline at end of file
diff --git a/datafusion-cli/tests/data/sql.txt b/datafusion-cli/tests/sql/select.sql
similarity index 100%
rename from datafusion-cli/tests/data/sql.txt
rename to datafusion-cli/tests/sql/select.sql
diff --git a/docs/source/contributor-guide/testing.md b/docs/source/contributor-guide/testing.md
index 2a9f22d22d..2868125c7f 100644
--- a/docs/source/contributor-guide/testing.md
+++ b/docs/source/contributor-guide/testing.md
@@ -58,6 +58,18 @@ Like similar systems such as [DuckDB](https://duckdb.org/dev/testing), DataFusio
 
 DataFusion has integrated [sqlite's test suite](https://sqlite.org/sqllogictest/doc/trunk/about.wiki) as a supplemental test suite that is run whenever a PR is merged into DataFusion. To run it manually please refer to the [README](https://github.com/apache/datafusion/blob/main/datafusion/sqllogictest/README.md#running-tests-sqlite) file for instructions.
 
+## Snapshot testing
+
+[Insta](https://github.com/mitsuhiko/insta) is used for snapshot testing. Snapshots are generated
+and compared on each test run. If the output changes, tests will fail.
+
+To review the changes, you can use Insta CLI:
+
+```shell
+cargo install cargo-insta
+cargo insta review
+```
+
 ## Extended Tests
 
 In addition to the standard CI test suite that is run on all PRs prior to merge,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org