Repository: GoogleCloudPlatform/pgadapter
Pull request #1180 (2023-11-10 13:46:36)

test: setup emulator tests
Sets up integration tests against the emulator and fixes some minor issues found while testing against the emulator.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) Report
> Merging [#1180](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (3347a5d) into [postgresql-dialect](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/commit/ffdb271b9ed65cd62c3a2e9b2225a443f97a3cf4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (ffdb271) will **decrease** coverage by `0.08%`.
> Report is 5 commits behind head on postgresql-dialect.
> The diff coverage is `88.88%`.

```diff
@@                   Coverage Diff                    @@
##             postgresql-dialect    #1180      +/-   ##
========================================================
- Coverage                 90.53%   90.45%   -0.08%     
- Complexity                 2561     2563       +2     
========================================================
  Files                       141      141              
  Lines                      8652     8656       +4     
  Branches                   1263     1266       +3     
========================================================
- Hits                       7833     7830       -3     
- Misses                      557      562       +5     
- Partials                    262      264       +2     
```

| [Flag](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) | Coverage Δ | |
|---|---|---|
| [all_tests](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) | `90.45% <88.88%> (-0.08%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) | Coverage Δ | |
|---|---|---|
| [...ud/spanner/pgadapter/error/PGExceptionFactory.java](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvcGdhZGFwdGVyL2Vycm9yL1BHRXhjZXB0aW9uRmFjdG9yeS5qYXZh) | `98.43% <100.00%> (+0.07%)` | :arrow_up: |
| [...ud/spanner/pgadapter/statements/CopyStatement.java](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvcGdhZGFwdGVyL3N0YXRlbWVudHMvQ29weVN0YXRlbWVudC5qYXZh) | `95.38% <100.00%> (+0.01%)` | :arrow_up: |
| [...ud/spanner/pgadapter/metadata/OptionsMetadata.java](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvcGdhZGFwdGVyL21ldGFkYXRhL09wdGlvbnNNZXRhZGF0YS5qYXZh) | `86.04% <0.00%> (-0.26%)` | :arrow_down: |

... and [3 files with indirect coverage changes](https://app.codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/1180/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform)

:mega: Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in [Chrome](https://chrome.google.com/webstore/detail/codecov/gedikamndpbemklijjkncpnolildpbgo) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecov/) today!


Patch:
diff --git a/.github/workflows/integration-emulator.yaml b/.github/workflows/integration-emulator.yaml
new file mode 100644
index 00000000..36b922aa
--- /dev/null
+++ b/.github/workflows/integration-emulator.yaml
@@ -0,0 +1,74 @@
+on:
+  push:
+    branches:
+      - postgresql-dialect
+  pull_request:
+  workflow_dispatch:
+name: integration-emulator
+env:
+  GOOGLE_CLOUD_PROJECT: "test-project"
+  GOOGLE_CLOUD_INSTANCE: "test-instance"
+  GOOGLE_CLOUD_DATABASE: "test-db"
+jobs:
+  integration-test-emulator:
+    runs-on: ubuntu-latest
+    # Add a PostgreSQL Docker container and the emulator.
+    services:
+      postgres:
+        image: postgres:14
+        env:
+          POSTGRES_PASSWORD: postgres
+        # Set health checks to wait until postgres has started
+        ports:
+          - 5432:5432
+        volumes:
+          - /pg:/var/run/postgresql
+          - /tmp:/tmp
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+      emulator:
+        image: gcr.io/cloud-spanner-emulator/emulator:latest
+        ports:
+          - 9010:9010
+          - 9020:9020
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Java
+        uses: actions/setup-java@v3
+        with:
+          distribution: zulu
+          java-version: 17
+      - run: java -version
+      - name: Setup Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: '^1.17.7'
+      - run: go version
+      - uses: actions/setup-python@v4
+        with:
+          python-version: '3.9'
+      - uses: actions/setup-node@v4
+        with:
+          node-version: 18
+      - uses: ruby/setup-ruby@v1
+        with:
+          ruby-version: '3.0'
+          bundler-cache: true
+      - name: Setup psql
+        run: |
+          sudo apt-get update
+          sudo apt-get install --yes --no-install-recommends postgresql-client-14
+      - name: Create PostgreSQL test database
+        run: |
+          psql -h /pg -U postgres -c "CREATE DATABASE pgadapter"
+      - name: Run integration tests
+        run: mvn verify -B -Dclirr.skip=true -DskipUnits=true -DskipITs=false
+        env:
+          POSTGRES_HOST: /pg
+          POSTGRES_PORT: 5432
+          POSTGRES_USER: postgres
+          POSTGRES_DATABASE: pgadapter
+          SPANNER_EMULATOR_HOST: localhost:9010
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactory.java b/src/main/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactory.java
index 4941aab2..347011c6 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactory.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactory.java
@@ -85,19 +85,22 @@ public class PGExceptionFactory {
           .setSQLState(SQLState.SerializationFailure)
           .build();
     }
-    if (spannerException.getErrorCode() == ErrorCode.INVALID_ARGUMENT
-        && RELATION_NOT_FOUND_PATTERN.matcher(spannerException.getMessage()).find()) {
+    if (spannerException.getErrorCode() == ErrorCode.NOT_FOUND
+        || spannerException.getErrorCode() == ErrorCode.INVALID_ARGUMENT
+            && RELATION_NOT_FOUND_PATTERN.matcher(spannerException.getMessage()).find()) {
       return PGException.newBuilder(extractMessage(spannerException))
           .setSQLState(SQLState.UndefinedTable)
           .build();
-    } else if (spannerException.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+    } else if ((spannerException.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+            || spannerException.getErrorCode() == ErrorCode.INVALID_ARGUMENT)
         && CANNOT_DROP_TABLE_WITH_INDICES_PATTERN.matcher(spannerException.getMessage()).find()) {
       return PGException.newBuilder(extractMessage(spannerException))
           .setSQLState(SQLState.FeatureNotSupported)
           .setHints(
               "Execute 'set spanner.support_drop_cascade=true' to enable dropping tables with indices")
           .build();
-    } else if (spannerException.getErrorCode() == ErrorCode.INVALID_ARGUMENT
+    } else if ((spannerException.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+            || spannerException.getErrorCode() == ErrorCode.INVALID_ARGUMENT)
         && ONLY_RESTRICT_BEHAVIOR.matcher(spannerException.getMessage()).find()) {
       return PGException.newBuilder(extractMessage(spannerException))
           .setSQLState(SQLState.FeatureNotSupported)
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java
index 51830aae..e96faa5a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java
@@ -66,6 +66,7 @@ public class OptionsMetadata {
    * </ol>
    */
   public static class Builder {
+    private Map<String, String> environment = System.getenv();
     private String project;
     private String instance;
     private String database;
@@ -89,6 +90,11 @@ public class OptionsMetadata {
 
     Builder() {}
 
+    Builder setEnvironment(Map<String, String> environment) {
+      this.environment = Preconditions.checkNotNull(environment);
+      return this;
+    }
+
     /**
      * (Optional) The Google Cloud project ID that PGAdapter should connect to.
      *
@@ -509,6 +515,7 @@ public class OptionsMetadata {
       "skip_internal_debug_warning";
   private static final String OPTION_DEBUG_MODE = "debug";
 
+  private final Map<String, String> environment;
   private final String osName;
   private final CommandLine commandLine;
   private final Credentials credentials;
@@ -543,26 +550,29 @@ public class OptionsMetadata {
    * of calling this method directly.
    */
   public OptionsMetadata(String[] args) {
-    this(System.getProperty("os.name", ""), args);
+    this(System.getenv(), System.getProperty("os.name", ""), args);
   }
 
   private OptionsMetadata(Builder builder) {
     this(
+        builder.environment,
         System.getProperty("os.name", ""),
         builder.toCommandLineArguments(),
         builder.credentials,
         builder.sessionPoolOptions);
   }
 
-  OptionsMetadata(String osName, String[] args) {
-    this(osName, args, null, null);
+  OptionsMetadata(Map<String, String> environment, String osName, String[] args) {
+    this(environment, osName, args, null, null);
   }
 
   OptionsMetadata(
+      Map<String, String> environment,
       String osName,
       String[] args,
       @Nullable Credentials credentials,
       @Nullable SessionPoolOptions sessionPoolOptions) {
+    this.environment = Preconditions.checkNotNull(environment);
     this.osName = osName;
     this.commandLine = buildOptions(args);
     this.credentials = credentials;
@@ -637,6 +647,7 @@ public class OptionsMetadata {
       boolean replaceJdbcMetadataQueries,
       JSONObject commandMetadata) {
     this(
+        System.getenv(),
         System.getProperty("os.name", ""),
         defaultConnectionUrl,
         proxyPort,
@@ -650,6 +661,7 @@ public class OptionsMetadata {
 
   @VisibleForTesting
   OptionsMetadata(
+      Map<String, String> environment,
       String osName,
       String defaultConnectionUrl,
       int proxyPort,
@@ -659,6 +671,7 @@ public class OptionsMetadata {
       boolean requiresMatcher,
       boolean replaceJdbcMetadataQueries,
       JSONObject commandMetadata) {
+    this.environment = Preconditions.checkNotNull(environment);
     this.osName = osName;
     this.commandLine = null;
     this.credentials = null;
@@ -865,7 +878,7 @@ public class OptionsMetadata {
     // Note that Credentials here is the credentials file, not the actual credentials
     String url = String.format("%s%s;userAgent=%s", endpoint, databaseName, DEFAULT_USER_AGENT);
 
-    if (!shouldAuthenticate()) {
+    if (!shouldAuthenticate() && environment.get("SPANNER_EMULATOR_HOST") == null) {
       String credentials = buildCredentialsFile();
       if (!Strings.isNullOrEmpty(credentials)) {
         url = String.format("%s;credentials=%s", url, credentials);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
index 1eb9e94c..73d3d71a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
@@ -326,6 +326,7 @@ public class CopyStatement extends IntermediatePortalStatement {
                   .mapToObj(i -> String.format("$%d", i))
                   .collect(Collectors.joining(", ", "(", ")"));
     }
+    sql += " ORDER BY ordinal_position";
     Statement.Builder builder =
         Statement.newBuilder(sql)
             .bind("p1")
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutor.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutor.java
index 1b79f457..6f87e64f 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutor.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutor.java
@@ -113,8 +113,8 @@ class DdlExecutor {
     }
   }
 
-  private Tuple<ParsedStatement, Statement> replace(
-      ParsedStatement parsedStatement, Statement statement) {
+  @VisibleForTesting
+  Tuple<ParsedStatement, Statement> replace(ParsedStatement parsedStatement, Statement statement) {
     if (!ddlStatementReplacements.get().isEmpty()) {
       String replaced = applyReplacers(statement.getSql());
       if (!replaced.equals(statement.getSql())) {


Test patch:
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/CopyInMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/CopyInMockServerTest.java
index 751af5a9..64051ace 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/CopyInMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/CopyInMockServerTest.java
@@ -1920,7 +1920,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
     mockSpanner.putStatementResult(
         StatementResult.query(
             com.google.cloud.spanner.Statement.newBuilder(
-                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ")
+                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2  ORDER BY ordinal_position")
                 .bind("p1")
                 .to(usersSchema)
                 .bind("p2")
@@ -1930,7 +1930,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
     mockSpanner.putStatementResult(
         StatementResult.query(
             com.google.cloud.spanner.Statement.newBuilder(
-                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 and column_name in ($3, $4)")
+                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 and column_name in ($3, $4) ORDER BY ordinal_position")
                 .bind("p1")
                 .to(usersSchema)
                 .bind("p2")
@@ -1944,7 +1944,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
     mockSpanner.putStatementResult(
         StatementResult.query(
             com.google.cloud.spanner.Statement.newBuilder(
-                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 and column_name in ($3, $4)")
+                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 and column_name in ($3, $4) ORDER BY ordinal_position")
                 .bind("p1")
                 .to(usersSchema)
                 .bind("p2")
@@ -2013,7 +2013,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
     mockSpanner.putStatementResult(
         StatementResult.query(
             com.google.cloud.spanner.Statement.newBuilder(
-                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ")
+                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2  ORDER BY ordinal_position")
                 .bind("p1")
                 .to("public")
                 .bind("p2")
@@ -2026,7 +2026,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
                     "SELECT column_name, spanner_type "
                         + "FROM information_schema.columns "
                         + "WHERE table_schema = $1 AND table_name = $2 "
-                        + "and column_name in ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12)")
+                        + "and column_name in ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12) ORDER BY ordinal_position")
                 .bind("p1")
                 .to("public")
                 .bind("p2")
@@ -2219,7 +2219,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
     mockSpanner.putStatementResult(
         StatementResult.query(
             com.google.cloud.spanner.Statement.newBuilder(
-                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ")
+                    "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2  ORDER BY ordinal_position")
                 .bind("p1")
                 .to("public")
                 .bind("p2")
@@ -2232,7 +2232,7 @@ public class CopyInMockServerTest extends AbstractMockServerTest {
                     "SELECT column_name, spanner_type "
                         + "FROM information_schema.columns "
                         + "WHERE table_schema = $1 AND table_name = $2 "
-                        + "and column_name in ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12)")
+                        + "and column_name in ($3, $4, $5, $6, $7, $8, $9, $10, $11, $12) ORDER BY ordinal_position")
                 .bind("p1")
                 .to("public")
                 .bind("p2")
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java
index 6d7cf5ce..b8fd43c3 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java
@@ -115,6 +115,7 @@ public class ITAuthTest implements IntegrationTest {
 
   @Test
   public void testConnectWithOAuth2Token() throws Exception {
+    skipOnEmulator("Credentials are never used with the emulator");
     String credentialsFile = System.getenv("GOOGLE_APPLICATION_CREDENTIALS");
     assumeTrue("Auth tests require default (service account) credentials", credentialsFile != null);
     assumeTrue("OAuth2 test requires gcloud", isGcloudAvailable());
@@ -153,6 +154,7 @@ public class ITAuthTest implements IntegrationTest {
 
   @Test
   public void testConnectWithCredentials() throws Exception {
+    skipOnEmulator("Credentials are never used with the emulator");
     String credentialsFile = System.getenv("GOOGLE_APPLICATION_CREDENTIALS");
     assumeTrue("Auth tests require default (service account) credentials", credentialsFile != null);
 
@@ -175,6 +177,7 @@ public class ITAuthTest implements IntegrationTest {
 
   @Test
   public void testConnectWithPrivateKey() throws Exception {
+    skipOnEmulator("Credentials are never used with the emulator");
     String credentialsFile = System.getenv("GOOGLE_APPLICATION_CREDENTIALS");
     assumeTrue("Auth tests require default (service account) credentials", credentialsFile != null);
 
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITDdlTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITDdlTest.java
index 0096ddf2..dd5a1f88 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITDdlTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITDdlTest.java
@@ -113,8 +113,6 @@ public class ITDdlTest implements IntegrationTest {
           assertThrows(
               PSQLException.class,
               () -> connection.createStatement().execute("drop table parent cascade"));
-      System.out.println(exception.getErrorCode());
-      System.out.println(exception.getMessage());
       assertEquals(SQLState.FeatureNotSupported.toString(), exception.getSQLState());
       assertNotNull(exception.getServerErrorMessage());
       assertEquals(
@@ -192,9 +190,11 @@ public class ITDdlTest implements IntegrationTest {
         statement.addBatch("create index idx_concerts_start_time on concerts (start_time)");
         statement.addBatch("create unique index idx_tracks_title on tracks (id, title)");
         statement.addBatch(
-            "create view v_singers sql security invoker as select first_name, last_name from singers order by last_name");
+            "create view v_singers sql security invoker as select first_name, last_name from singers"
+                + (isRunningOnEmulator() ? "" : " order by last_name"));
         statement.addBatch(
-            "create view v_venues sql security invoker as select name, description from venues order by name");
+            "create view v_venues sql security invoker as select name, description from venues"
+                + (isRunningOnEmulator() ? "" : " order by name"));
 
         assertArrayEquals(new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, statement.executeBatch());
       }
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcDescribeStatementTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcDescribeStatementTest.java
index ba5968c4..cc47eb77 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcDescribeStatementTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcDescribeStatementTest.java
@@ -110,6 +110,8 @@ public class ITJdbcDescribeStatementTest implements IntegrationTest {
 
   @Test
   public void testQuery() throws SQLException {
+    skipOnEmulator("Equality operator for numeric is not supported on the emulator");
+
     String sql =
         "select col_bigint, col_bool, col_bytea, col_float8, col_int, col_numeric, col_timestamptz, col_date, col_varchar "
             + "from all_types "
@@ -146,6 +148,8 @@ public class ITJdbcDescribeStatementTest implements IntegrationTest {
 
   @Test
   public void testParameterMetaData() throws SQLException {
+    skipOnEmulator("Equality operator for numeric is not supported on the emulator");
+
     for (String sql :
         new String[] {
           "select col_bigint, col_bool, col_bytea, col_float8, col_int, col_numeric, col_timestamptz, col_date, col_varchar, col_jsonb "
@@ -375,6 +379,8 @@ public class ITJdbcDescribeStatementTest implements IntegrationTest {
 
   @Test
   public void testResultSetMetaData() throws SQLException {
+    skipOnEmulator("Equality operator for numeric is not supported on the emulator");
+
     ImmutableList<String> columnNames =
         ImmutableList.of(
             "col_bigint",
@@ -427,6 +433,8 @@ public class ITJdbcDescribeStatementTest implements IntegrationTest {
 
   @Test
   public void testSelectWithParameters() throws SQLException {
+    skipOnEmulator("Equality operator for numeric is not supported on the emulator");
+
     String sql =
         "select col_bigint, col_bool, col_bytea, col_float8, col_int, col_numeric, col_timestamptz, col_date, col_varchar "
             + "from all_types "
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcMetadataTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcMetadataTest.java
index 8098148e..9f27b0a2 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcMetadataTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcMetadataTest.java
@@ -378,6 +378,8 @@ public class ITJdbcMetadataTest implements IntegrationTest {
 
   @Test
   public void testDatabaseMetaDataColumns() throws Exception {
+    skipOnEmulator("jsonb is not supported on the emulator in combination with the JDBC driver");
+
     runForAllVersions(
         (connection, version) -> {
           try {
@@ -495,6 +497,8 @@ public class ITJdbcMetadataTest implements IntegrationTest {
 
   @Test
   public void testDatabaseMetaDataColumns_FilteredByName() throws Exception {
+    skipOnEmulator("jsonb is not supported on the emulator in combination with the JDBC driver");
+
     runForAllVersions(
         (connection, version) -> {
           try {
@@ -836,6 +840,8 @@ public class ITJdbcMetadataTest implements IntegrationTest {
 
   @Test
   public void testDatabaseMetaDataTypeInfo() throws Exception {
+    skipOnEmulator("jsonb is not supported on the emulator in combination with the JDBC driver");
+
     runForAllVersions(
         (connection, version) -> {
           try {
@@ -1042,6 +1048,8 @@ public class ITJdbcMetadataTest implements IntegrationTest {
 
   @Test
   public void testDatabaseMetaDataSchemas() throws Exception {
+    skipOnEmulator("Catalog name is not supported by emulator");
+
     runForAllVersions(
         (connection, version) -> {
           try {
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcTest.java
index bcb5696d..0d3b178b 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITJdbcTest.java
@@ -290,6 +290,8 @@ public class ITJdbcTest implements IntegrationTest {
 
   @Test
   public void testSelectWithParameters() throws SQLException {
+    skipOnEmulator("Casting jsonb to text is not supported on the emulator");
+
     boolean isSimpleMode = "simple".equalsIgnoreCase(preferQueryMode);
     String sql =
         "select col_bigint, col_bool, col_bytea, col_float8, col_int, col_numeric, col_timestamptz, col_date, col_varchar, col_jsonb, "
@@ -394,6 +396,8 @@ public class ITJdbcTest implements IntegrationTest {
 
   @Test
   public void testInsertWithParameters() throws SQLException {
+    skipOnEmulator("jsonb[] is not fully supported on the emulator");
+
     boolean isSimpleMode = "simple".equalsIgnoreCase(preferQueryMode);
     try (Connection connection = DriverManager.getConnection(getConnectionUrl())) {
       try (PreparedStatement statement =
@@ -884,7 +888,9 @@ public class ITJdbcTest implements IntegrationTest {
 
       // Delete the imported data to prevent the cleanup method to fail on 'Too many mutations'
       // when it tries to delete all data using a normal transaction.
-      connection.createStatement().execute("delete from all_types");
+      connection
+          .createStatement()
+          .execute("delete from all_types" + (isRunningOnEmulator() ? " where true" : ""));
     }
   }
 
@@ -1009,7 +1015,15 @@ public class ITJdbcTest implements IntegrationTest {
               if (resultSet.getLong(1) == 3L) {
                 assertNull(resultSet.getObject(col));
               } else {
-                assertNotNull(resultSet.getObject(col));
+                // JSONB (array) is currently not supported using the PG JDBC driver with the
+                // emulator, as the PG JDBC driver tries to dynamically load information about the
+                // type from teh pg_catalog tables.
+                if (!(resultSet.getMetaData().getColumnName(col).equals("col_jsonb")
+                    || resultSet.getMetaData().getColumnName(col).equals("col_array_jsonb"))) {
+                  assertNotNull(
+                      "Column " + resultSet.getMetaData().getColumnName(col),
+                      resultSet.getObject(col));
+                }
               }
             }
           }
@@ -1100,8 +1114,10 @@ public class ITJdbcTest implements IntegrationTest {
         assertTrue(namespaces.next());
         assertEquals("public", namespaces.getString(1));
         assertTrue(namespaces.next());
-        assertEquals("pg_catalog", namespaces.getString(1));
-        assertTrue(namespaces.next());
+        if (!isRunningOnEmulator()) {
+          assertEquals("pg_catalog", namespaces.getString(1));
+          assertTrue(namespaces.next());
+        }
         assertEquals("information_schema", namespaces.getString(1));
         assertTrue(namespaces.next());
         assertEquals("spanner_sys", namespaces.getString(1));
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITPgClassTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITPgClassTest.java
index 19b36972..f92af891 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITPgClassTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITPgClassTest.java
@@ -171,6 +171,8 @@ public class ITPgClassTest implements IntegrationTest {
 
   @Test
   public void testPgIndex() throws SQLException {
+    skipOnEmulator("pg_index replacement query is not supported on the emulator");
+
     String sql =
         "select indexrelid, indrelid, indnatts, indnkeyatts, indisunique, "
             + "indnullsnotdistinct, indisprimary, indpred "
@@ -609,7 +611,10 @@ public class ITPgClassTest implements IntegrationTest {
           assertEquals(expected.confupdtype, resultSet.getString("confupdtype"));
           assertEquals(expected.confdeltype, resultSet.getString("confdeltype"));
           assertEquals(expected.confmatchtype, resultSet.getString("confmatchtype"));
-          assertArrayEquals(expected.conkey, (Long[]) resultSet.getArray("conkey").getArray());
+          // TODO: Remove check when we array_agg with order by is supported.
+          if (!isRunningOnEmulator()) {
+            assertArrayEquals(expected.conkey, (Long[]) resultSet.getArray("conkey").getArray());
+          }
           assertArrayEquals(
               expected.confkey,
               resultSet.getArray("confkey") == null
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
index b527817a..0c9333f2 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
@@ -189,7 +189,9 @@ public class ITPsqlTest implements IntegrationTest {
                         + "as\n"
                         + "select id, full_name\n"
                         + "from singers\n"
-                        + "order by last_name, id")
+                        + (System.getenv("SPANNER_EMULATOR_HOST") == null
+                            ? "order by last_name, id"
+                            : ""))
                 .add("create index idx_singers_last_name on singers (last_name)")
                 .add(
                     "create unique index idx_tracks_title on tracks (id, title) interleave in albums")
@@ -201,8 +203,11 @@ public class ITPsqlTest implements IntegrationTest {
                     "create change stream cs_singers for singers (first_name, last_name, active)\n"
                         + "with (retention_period = '2d',\n"
                         + "value_capture_type = 'OLD_AND_NEW_VALUES')")
-                .add("create role hr_manager")
-                .add("grant select on table singers to hr_manager")
+                .addAll(
+                    System.getenv("SPANNER_EMULATOR_HOST") == null
+                        ? ImmutableList.of(
+                            "create role hr_manager", "grant select on table singers to hr_manager")
+                        : ImmutableList.of())
                 .build());
     testEnv.startPGAdapterServer(Collections.emptyList());
   }
@@ -307,7 +312,9 @@ public class ITPsqlTest implements IntegrationTest {
               + "drop table if exists numbers;\n"
               + "drop foreign table if exists f_all_types;\n"
               + "drop foreign table if exists f_numbers;\n"
-              + "drop role if exists hr_manager;\n"
+              + (System.getenv("SPANNER_EMULATOR_HOST") == null
+                  ? "drop role if exists hr_manager;\n"
+                  : "")
         };
     ProcessBuilder builder = new ProcessBuilder().command(dropTablesCommand);
     setPgPassword(builder);
@@ -439,6 +446,8 @@ public class ITPsqlTest implements IntegrationTest {
 
   @Test
   public void testSelectPgCatalogTables() throws IOException, InterruptedException {
+    skipOnEmulator("Not all pg_catalog tables are supported on the emulator yet");
+
     String sql =
         "select pg_type.typname\n"
             + "from pg_catalog.pg_type\n"
@@ -485,6 +494,8 @@ public class ITPsqlTest implements IntegrationTest {
 
   @Test
   public void testPrepareExecuteDeallocate() throws IOException, InterruptedException {
+    skipOnEmulator("Uses a cast that is not yet supported on the emulator");
+
     Tuple<String, String> result =
         runUsingPsql(
             ImmutableList.of(
@@ -557,6 +568,8 @@ public class ITPsqlTest implements IntegrationTest {
    */
   @Test
   public void testCopyBetweenPostgreSQLAndCloudSpanner() throws Exception {
+    skipOnEmulator("Uses a cast that is not yet supported on the emulator");
+
     int numRows = 100;
 
     truncatePostgreSQLAllTypesTable();
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITQueryTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITQueryTest.java
index 39036341..0075b0c3 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITQueryTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITQueryTest.java
@@ -122,7 +122,7 @@ public final class ITQueryTest implements IntegrationTest {
     testEnv.consumeStartUpMessages(in);
 
     // Send query.
-    String payload = "SELECT id, name, age, data FROM users\0";
+    String payload = "SELECT id, name, age, data FROM users order by id\0";
     byte[] messageMetadata = {'Q', 0, 0, 0, (byte) (payload.length() + 4)};
     byte[] message = Bytes.concat(messageMetadata, payload.getBytes());
     out.write(message, 0, message.length);
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITSpringDataJpaSampleTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITSpringDataJpaSampleTest.java
index 85dd963d..f3c157d6 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITSpringDataJpaSampleTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITSpringDataJpaSampleTest.java
@@ -39,7 +39,7 @@ import org.junit.runners.JUnit4;
 
 @Category(IntegrationTest.class)
 @RunWith(JUnit4.class)
-public class ITSpringDataJpaSampleTest {
+public class ITSpringDataJpaSampleTest implements IntegrationTest {
   private static final String SPRING_DATA_JPA_SAMPLE_DIRECTORY = "samples/java/spring-data-jpa";
   private static final String APPLICATION_PROPERTIES_FILE =
       SPRING_DATA_JPA_SAMPLE_DIRECTORY + "/src/main/resources/application.properties";
@@ -48,6 +48,10 @@ public class ITSpringDataJpaSampleTest {
 
   @BeforeClass
   public static void setup() throws ClassNotFoundException, IOException, SQLException {
+    assumeTrue(
+        "Sample test is not made compatible with emulator",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     assumeFalse("Spring Data sample requires at least Java 9", OptionsMetadata.isJava8());
     assumeTrue("This test only works on the default Spanner URL", testEnv.getSpannerUrl() == null);
 
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java
index 8ead943a..dad24118 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java
@@ -14,5 +14,15 @@
 
 package com.google.cloud.spanner.pgadapter;
 
+import static org.junit.Assume.assumeFalse;
+
 /** Integration Test interface. */
-public interface IntegrationTest {}
+public interface IntegrationTest {
+  default void skipOnEmulator(String reason) {
+    assumeFalse(reason, isRunningOnEmulator());
+  }
+
+  default boolean isRunningOnEmulator() {
+    return System.getenv("SPANNER_EMULATOR_HOST") != null;
+  }
+}
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java
index 3d7b3376..966c13b6 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java
@@ -189,6 +189,7 @@ public class PgAdapterTestEnv {
   private void startPGAdapterServer(
       String databaseId, Iterable<String> additionalPGAdapterOptions) {
     if (PG_ADAPTER_ADDRESS == null) {
+      additionalPGAdapterOptions = maybeAddAutoConfigEmulator(additionalPGAdapterOptions);
       String credentials = getCredentials();
       ImmutableList.Builder<String> argsListBuilder =
           ImmutableList.<String>builder().add("-p", getProjectId(), "-i", getInstanceId());
@@ -213,6 +214,39 @@ public class PgAdapterTestEnv {
     }
   }
 
+  private Iterable<String> maybeAddAutoConfigEmulator(Iterable<String> additionalPGAdapterOptions) {
+    if (System.getenv("SPANNER_EMULATOR_HOST") == null) {
+      return additionalPGAdapterOptions;
+    }
+    List<String> result = new ArrayList<>();
+    Iterables.addAll(result, additionalPGAdapterOptions);
+    boolean foundExistingOption = false;
+    for (int index = 0; index < result.size(); index++) {
+      String option = result.get(index);
+      if ("-r".equals(option)) {
+        foundExistingOption = true;
+        // Append to the existing property.
+        if (index < result.size() - 1) {
+          String value = result.get(index + 1);
+          result.remove(index + 1);
+          if (value == null) {
+            result.add(index + 1, "autoConfigEmulator=true");
+          } else {
+            result.add(index + 1, value + ";autoConfigEmulator=true");
+          }
+        } else {
+          result.add("autoConfigEmulator=true");
+        }
+        break;
+      }
+    }
+    if (!foundExistingOption) {
+      result.add("-r");
+      result.add("autoConfigEmulator=true");
+    }
+    return result;
+  }
+
   public void stopPGAdapterServer() {
     if (server != null) {
       server.stopServer();
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/csharp/ITNpgsqlTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/csharp/ITNpgsqlTest.java
index 6047152d..9b03d9fd 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/csharp/ITNpgsqlTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/csharp/ITNpgsqlTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.ByteArray;
 import com.google.cloud.Date;
@@ -67,6 +68,9 @@ public class ITNpgsqlTest implements IntegrationTest {
 
   @BeforeClass
   public static void setup() throws Exception {
+    assumeTrue(
+        "This test requires pg_type support", System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     testEnv.setUp();
     database = testEnv.createDatabase(getDdlStatements());
     testEnv.startPGAdapterServerWithDefaultDatabase(database.getId(), Collections.emptyList());
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactoryTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactoryTest.java
index 727b57c0..68c5556e 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactoryTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/error/PGExceptionFactoryTest.java
@@ -42,7 +42,7 @@ public class PGExceptionFactoryTest {
         PGExceptionFactory.newPGException("Table not found: foo"),
         toPGException(
             SpannerExceptionFactory.newSpannerException(
-                ErrorCode.NOT_FOUND, "Table not found: foo")));
+                ErrorCode.FAILED_PRECONDITION, "Table not found: foo")));
     assertEquals(
         PGExceptionFactory.newPGException("Table not found: foo"),
         toPGException(
@@ -50,7 +50,7 @@ public class PGExceptionFactoryTest {
                 new ApiException(
                     "Table not found: foo",
                     /* cause= */ null,
-                    GrpcStatusCode.of(Code.NOT_FOUND),
+                    GrpcStatusCode.of(Code.FAILED_PRECONDITION),
                     /* retryable= */ false))));
     assertEquals(
         SQLState.SerializationFailure,
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITGormSampleTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITGormSampleTest.java
index d725745c..cdc0f737 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITGormSampleTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITGormSampleTest.java
@@ -16,6 +16,7 @@ package com.google.cloud.spanner.pgadapter.golang;
 
 import static org.junit.Assert.*;
 import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.spanner.Database;
 import com.google.cloud.spanner.pgadapter.IntegrationTest;
@@ -40,6 +41,10 @@ public class ITGormSampleTest implements IntegrationTest {
 
   @BeforeClass
   public static void setup() throws Exception {
+    assumeTrue(
+        "RETURNING generated value does not work with the emulator",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     try {
       gormSampleTest =
           GolangTest.compile("../../../samples/golang/gorm/sample.go", GormSampleTest.class);
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgx5Test.java b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgx5Test.java
index 55cdc248..1c04efd5 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgx5Test.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgx5Test.java
@@ -177,6 +177,8 @@ public class ITPgx5Test implements IntegrationTest {
 
   @Test
   public void testPrepareSelectStatement() {
+    skipOnEmulator("jsonb comparison is not yet supported on the emulator");
+
     assertNull(pgxTest.TestPrepareSelectStatement(createConnString()));
   }
 
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxSampleTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxSampleTest.java
index 33c02681..bd9594a7 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxSampleTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxSampleTest.java
@@ -43,6 +43,9 @@ public class ITPgxSampleTest implements IntegrationTest {
   @BeforeClass
   public static void setup() throws Exception {
     assumeTrue("This test only works on the default Spanner URL", testEnv.getSpannerUrl() == null);
+    assumeTrue(
+        "This test only works on the default Spanner URL",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
 
     String currentPath = new java.io.File(".").getCanonicalPath();
     String relativeSampleFilePath = "samples/golang/pgx/pgx_sample.go";
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxTest.java
index 1f695c76..bbed92e5 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/golang/ITPgxTest.java
@@ -177,6 +177,8 @@ public class ITPgxTest implements IntegrationTest {
 
   @Test
   public void testPrepareSelectStatement() {
+    skipOnEmulator("jsonb comparison is not yet supported on the emulator");
+
     assertNull(pgxTest.TestPrepareSelectStatement(createConnString()));
   }
 
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/hibernate/ITHibernateTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/hibernate/ITHibernateTest.java
index 778e1a94..55842665 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/hibernate/ITHibernateTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/hibernate/ITHibernateTest.java
@@ -15,6 +15,7 @@
 package com.google.cloud.spanner.pgadapter.hibernate;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.spanner.Database;
 import com.google.cloud.spanner.pgadapter.IntegrationTest;
@@ -57,6 +58,10 @@ public class ITHibernateTest {
   @BeforeClass
   public static void setup()
       throws ClassNotFoundException, IOException, SQLException, InterruptedException {
+    assumeTrue(
+        "This test is not supported on the emulator",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     // Make sure the PG JDBC driver is loaded.
     Class.forName("org.postgresql.Driver");
 
@@ -104,8 +109,10 @@ public class ITHibernateTest {
 
   @AfterClass
   public static void teardown() throws IOException {
-    try (FileWriter writer = new FileWriter(HIBERNATE_PROPERTIES_FILE)) {
-      writer.write(originalHibernateProperties);
+    if (originalHibernateProperties != null) {
+      try (FileWriter writer = new FileWriter(HIBERNATE_PROPERTIES_FILE)) {
+        writer.write(originalHibernateProperties);
+      }
     }
     testEnv.stopPGAdapterServer();
     testEnv.cleanUp();
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java
index 2ddcd3a3..334b1efe 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.Timestamp;
 import com.google.cloud.spanner.Database;
@@ -60,6 +61,10 @@ public class ITLiquibaseTest {
 
   @BeforeClass
   public static void setup() throws ClassNotFoundException, IOException, SQLException {
+    assumeTrue(
+        "This test is not supported on the emulator due to a failure to add a column of type numeric with a default value to a non-empty table",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     // Make sure the PG JDBC driver is loaded.
     Class.forName("org.postgresql.Driver");
 
@@ -90,8 +95,10 @@ public class ITLiquibaseTest {
 
   @AfterClass
   public static void teardown() throws IOException {
-    try (FileWriter writer = new FileWriter(LIQUIBASE_PROPERTIES_FILE)) {
-      writer.write(originalLiquibaseProperties);
+    if (originalLiquibaseProperties != null) {
+      try (FileWriter writer = new FileWriter(LIQUIBASE_PROPERTIES_FILE)) {
+        writer.write(originalLiquibaseProperties);
+      }
     }
     testEnv.stopPGAdapterServer();
     testEnv.cleanUp();
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java
index d9e1ad50..23fc5d99 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java
@@ -33,10 +33,12 @@ import com.google.cloud.spanner.SpannerException;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata.DdlTransactionMode;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata.SslMode;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata.TextFormat;
+import com.google.common.collect.ImmutableMap;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.util.Collections;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -48,7 +50,10 @@ public class OptionsMetadataTest {
   public void testDefaultDomainSocketFile() {
     for (String os : new String[] {"ubuntu", "windows"}) {
       OptionsMetadata options =
-          new OptionsMetadata(os, new String[] {"-p", "p", "-i", "i", "-c", "credentials.json"});
+          new OptionsMetadata(
+              Collections.emptyMap(),
+              os,
+              new String[] {"-p", "p", "-i", "i", "-c", "credentials.json"});
       if (options.isWindows()) {
         assertEquals("", options.getSocketFile(5432));
         assertFalse(options.isDomainSocketEnabled());
@@ -63,7 +68,10 @@ public class OptionsMetadataTest {
   public void testCustomDomainSocketFile() {
     for (String os : new String[] {"ubuntu", "windows"}) {
       OptionsMetadata options =
-          new OptionsMetadata(os, new String[] {"-p p", "-i i", "-c \"\"", "-dir /pgadapter"});
+          new OptionsMetadata(
+              Collections.emptyMap(),
+              os,
+              new String[] {"-p p", "-i i", "-c \"\"", "-dir /pgadapter"});
       assertEquals(
           "/pgadapter" + File.separatorChar + ".s.PGSQL.5432", options.getSocketFile(5432));
       assertTrue(options.isDomainSocketEnabled());
@@ -503,6 +511,7 @@ public class OptionsMetadataTest {
     assertEquals(
         "cloudspanner:/projects/my-project/instances/my-instance/databases/my-database",
         new OptionsMetadata(
+                Collections.emptyMap(),
                 "linux",
                 "jdbc:cloudspanner:/projects/my-project/instances/my-instance/databases/my-database",
                 5432,
@@ -514,4 +523,21 @@ public class OptionsMetadataTest {
                 null)
             .getDefaultConnectionUrl());
   }
+
+  @Test
+  public void testBuildConnectionUrlWithEmulator() {
+    assertEquals(
+        "cloudspanner:/projects/my-project/instances/my-instance/databases/my-database;userAgent=pg-adapter",
+        OptionsMetadata.newBuilder()
+            .setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010"))
+            .build()
+            .buildConnectionURL("projects/my-project/instances/my-instance/databases/my-database"));
+    assertEquals(
+        "cloudspanner:/projects/my-project/instances/my-instance/databases/my-database;userAgent=pg-adapter",
+        OptionsMetadata.newBuilder()
+            .setRequireAuthentication()
+            .setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010"))
+            .build()
+            .buildConnectionURL("projects/my-project/instances/my-instance/databases/my-database"));
+  }
 }
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/nodejs/ITNodePostgresTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/nodejs/ITNodePostgresTest.java
index ee0bfa9b..bdec62c3 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/nodejs/ITNodePostgresTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/nodejs/ITNodePostgresTest.java
@@ -62,6 +62,8 @@ public class ITNodePostgresTest implements IntegrationTest {
 
   @BeforeClass
   public static void setup() throws Exception {
+    NodeJSTest.installDependencies("node-postgres");
+
     testEnv.setUp();
     database = testEnv.createDatabase(getDdlStatements());
     testEnv.startPGAdapterServerWithDefaultDatabase(database.getId(), Collections.emptyList());
@@ -273,7 +275,9 @@ public class ITNodePostgresTest implements IntegrationTest {
 
     DatabaseClient client = testEnv.getSpanner().getDatabaseClient(database.getId());
     try (ResultSet resultSet =
-        client.singleUse().executeQuery(Statement.of("SELECT * FROM alltypes"))) {
+        client
+            .singleUse()
+            .executeQuery(Statement.of("SELECT * FROM alltypes ORDER BY col_bigint"))) {
       assertTrue(resultSet.next());
       assertEquals(1, resultSet.getLong("col_bigint"));
       assertTrue(resultSet.getBoolean("col_bool"));
@@ -358,11 +362,19 @@ public class ITNodePostgresTest implements IntegrationTest {
 
     String output =
         runTest("testErrorInReadWriteTransaction", getHost(), testEnv.getServer().getLocalPort());
-    assertEquals(
-        "Insert error: error: com.google.api.gax.rpc.AlreadyExistsException: io.grpc.StatusRuntimeException: ALREADY_EXISTS: Row [foo] in table users already exists\n"
-            + "Second insert failed with error: error: current transaction is aborted, commands ignored until end of transaction block\n"
-            + "SELECT 1 returned: 1\n",
-        output);
+    if (isRunningOnEmulator()) {
+      assertEquals(
+          "Insert error: error: com.google.api.gax.rpc.AlreadyExistsException: io.grpc.StatusRuntimeException: ALREADY_EXISTS: Failed to insert row with primary key ({pk#name:\"foo\"}) due to previously existing row\n"
+              + "Second insert failed with error: error: current transaction is aborted, commands ignored until end of transaction block\n"
+              + "SELECT 1 returned: 1\n",
+          output);
+    } else {
+      assertEquals(
+          "Insert error: error: com.google.api.gax.rpc.AlreadyExistsException: io.grpc.StatusRuntimeException: ALREADY_EXISTS: Row [foo] in table users already exists\n"
+              + "Second insert failed with error: error: current transaction is aborted, commands ignored until end of transaction block\n"
+              + "SELECT 1 returned: 1\n",
+          output);
+    }
   }
 
   @Test
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/python/django/ITDjangoTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/python/django/ITDjangoTest.java
index 023928a3..3a3227d2 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/python/django/ITDjangoTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/python/django/ITDjangoTest.java
@@ -15,6 +15,7 @@
 package com.google.cloud.spanner.pgadapter.python.django;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.spanner.Database;
 import com.google.cloud.spanner.pgadapter.IntegrationTest;
@@ -44,6 +45,10 @@ public class ITDjangoTest extends DjangoTestSetup {
 
   @BeforeClass
   public static void setup() throws Exception {
+    assumeTrue(
+        "This test is not supported on the emulator",
+        System.getenv("SPANNER_EMULATOR_HOST") == null);
+
     if (!isPythonAvailable()
         && !System.getProperty("allowSkipUnsupportedEcosystemTest", "false")
             .equalsIgnoreCase("true")) {
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg2/PythonCopyTests.java b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg2/PythonCopyTests.java
index c64bdea3..59f17e2f 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg2/PythonCopyTests.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg2/PythonCopyTests.java
@@ -129,7 +129,7 @@ public class PythonCopyTests extends AbstractPsycopg2Test {
     String copyType = "FROM";
     String file = "1,hello\n2,world\n";
     String sql1 =
-        "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2";
+        "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2  ORDER BY ordinal_position";
     String sql2 =
         "SELECT COUNT(*) FROM information_schema.index_columns WHERE table_schema='public' and table_name=$1 and column_name in ($2, $3)";
     Statement s1 = Statement.newBuilder(sql1).bind("p1").to("public").bind("p2").to("test").build();
@@ -173,7 +173,7 @@ public class PythonCopyTests extends AbstractPsycopg2Test {
     String copyType = "SIMPLE_FROM";
     String file = "1\thello\n2\tworld\n";
     String sql1 =
-        "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ";
+        "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2  ORDER BY ordinal_position";
     String sql2 =
         "SELECT COUNT(*) FROM information_schema.index_columns WHERE table_schema=$1 and table_name=$2 and column_name in ($3, $4)";
     Statement s1 = Statement.newBuilder(sql1).bind("p1").to("public").bind("p2").to("test").build();
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/ITPsycopg3Test.java b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/ITPsycopg3Test.java
index a7ffc271..78161bba 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/ITPsycopg3Test.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/ITPsycopg3Test.java
@@ -56,7 +56,7 @@ import org.junit.runners.Parameterized.Parameters;
 
 @Category(IntegrationTest.class)
 @RunWith(Parameterized.class)
-public class ITPsycopg3Test {
+public class ITPsycopg3Test implements IntegrationTest {
   private static final PgAdapterTestEnv testEnv = new PgAdapterTestEnv();
   private static Database database;
 
@@ -448,7 +448,14 @@ public class ITPsycopg3Test {
 
     String result = execute("batch_execution_error");
     assertTrue(result, result.contains("Executing batch failed with error"));
-    assertTrue(result, result.contains("Row [101] in table all_types already exists"));
+    if (isRunningOnEmulator()) {
+      assertTrue(
+          result,
+          result.contains(
+              "Failed to insert row with primary key ({pk#col_bigint:101}) due to previously existing row"));
+    } else {
+      assertTrue(result, result.contains("Row [101] in table all_types already exists"));
+    }
   }
 
   // TODO: Enable once the below PR has been merged
@@ -512,8 +519,7 @@ public class ITPsycopg3Test {
     // Add an extra NULL-row to the table.
     addNullRow();
 
-    String result = execute("binary_copy_out");
-    assertEquals(
+    String row1 =
         "col_bigint: 1\n"
             + "col_bool: True\n"
             + "col_bytea: b'test'\n"
@@ -533,8 +539,9 @@ public class ITPsycopg3Test {
             + "col_array_timestamptz: [datetime.datetime(2022, 2, 16, 16, 18, 2, 123456, tzinfo=<UTC>), None, datetime.datetime(2000, 1, 1, 0, 0, tzinfo=<UTC>)]\n"
             + "col_array_date: [datetime.date(2023, 2, 20), None, datetime.date(2000, 1, 1)]\n"
             + "col_array_string: ['string1', None, 'string2']\n"
-            + "col_array_jsonb: [{'key': 'value1'}, None, {'key': 'value2'}]\n"
-            + "col_bigint: 2\n"
+            + "col_array_jsonb: [{'key': 'value1'}, None, {'key': 'value2'}]\n";
+    String row2 =
+        "col_bigint: 2\n"
             + "col_bool: None\n"
             + "col_bytea: None\n"
             + "col_float8: None\n"
@@ -553,8 +560,14 @@ public class ITPsycopg3Test {
             + "col_array_timestamptz: None\n"
             + "col_array_date: None\n"
             + "col_array_string: None\n"
-            + "col_array_jsonb: None\n",
-        result);
+            + "col_array_jsonb: None\n";
+
+    String result = execute("binary_copy_out");
+    if (isRunningOnEmulator()) {
+      assertEquals(row2 + row1, result);
+    } else {
+      assertEquals(row1 + row2, result);
+    }
   }
 
   @Test
@@ -562,8 +575,7 @@ public class ITPsycopg3Test {
     // Add an extra NULL-row to the table.
     addNullRow();
 
-    String result = execute("text_copy_out");
-    assertEquals(
+    String row1 =
         "col_bigint: 1\n"
             + "col_bool: True\n"
             + "col_bytea: b'test'\n"
@@ -583,8 +595,9 @@ public class ITPsycopg3Test {
             + "col_array_timestamptz: [datetime.datetime(2022, 2, 16, 16, 18, 2, 123456, tzinfo=<UTC>), None, datetime.datetime(2000, 1, 1, 0, 0, tzinfo=<UTC>)]\n"
             + "col_array_date: [datetime.date(2023, 2, 20), None, datetime.date(2000, 1, 1)]\n"
             + "col_array_string: ['string1', None, 'string2']\n"
-            + "col_array_jsonb: [{'key': 'value1'}, None, {'key': 'value2'}]\n"
-            + "col_bigint: 2\n"
+            + "col_array_jsonb: [{'key': 'value1'}, None, {'key': 'value2'}]\n";
+    String row2 =
+        "col_bigint: 2\n"
             + "col_bool: None\n"
             + "col_bytea: None\n"
             + "col_float8: None\n"
@@ -603,8 +616,14 @@ public class ITPsycopg3Test {
             + "col_array_timestamptz: None\n"
             + "col_array_date: None\n"
             + "col_array_string: None\n"
-            + "col_array_jsonb: None\n",
-        result);
+            + "col_array_jsonb: None\n";
+
+    String result = execute("text_copy_out");
+    if (isRunningOnEmulator()) {
+      assertEquals(row2 + row1, result);
+    } else {
+      assertEquals(row1 + row2, result);
+    }
   }
 
   @Test
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutorTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutorTest.java
index e14b126c..75399ceb 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutorTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/statements/DdlExecutorTest.java
@@ -20,9 +20,11 @@ import static org.junit.Assert.assertSame;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import com.google.cloud.Tuple;
 import com.google.cloud.spanner.Dialect;
 import com.google.cloud.spanner.Statement;
 import com.google.cloud.spanner.connection.AbstractStatementParser;
+import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStatement;
 import com.google.cloud.spanner.pgadapter.session.SessionState;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.cloud.spanner.pgadapter.utils.RegexQueryPartReplacer;
@@ -235,19 +237,29 @@ public class DdlExecutorTest {
     assertSame(sql, ddlExecutorWithReplacements.applyReplacers(sql));
     assertEquals(
         "create table my_table (id bigint primary key, value timestamptz)",
-        ddlExecutorWithReplacements.applyReplacers(
+        applyReplacers(
+            ddlExecutorWithReplacements,
             "create table my_table (id bigint primary key, value timestamp)"));
     assertEquals(
         "create table databasechangelog_replaced (id bigint primary key)",
-        ddlExecutorWithReplacements.applyReplacers(
-            "create table databasechangelog (id bigint primary key)"));
+        applyReplacers(
+            ddlExecutorWithReplacements, "create table databasechangelog (id bigint primary key)"));
     assertEquals(
         "create table databasechangelog_replaced (id bigint primary key); create table databasechangelog_replaced (id bigint primary key)",
-        ddlExecutorWithReplacements.applyReplacers(
+        applyReplacers(
+            ddlExecutorWithReplacements,
             "create table databasechangelog (id bigint primary key); create table databasechangelog (id bigint primary key)"));
     assertEquals(
         "create table databasechangelog_reverted (id bigint primary key)",
-        ddlExecutorWithReplacements.applyReplacers(
+        applyReplacers(
+            ddlExecutorWithReplacements,
             "create table databasechangelog_replaced (id bigint primary key)"));
   }
+
+  private String applyReplacers(DdlExecutor ddlExecutor, String sql) {
+    Statement statement = Statement.of(sql);
+    ParsedStatement parsedStatement = PARSER.parse(Statement.of(sql));
+    Tuple<ParsedStatement, Statement> result = ddlExecutor.replace(parsedStatement, statement);
+    return result.y().getSql();
+  }
 }
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/BoostrapMessageTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/BoostrapMessageTest.java
index 0f0a66b7..7ad6824a 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/BoostrapMessageTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/BoostrapMessageTest.java
@@ -15,6 +15,7 @@
 package com.google.cloud.spanner.pgadapter.wireprotocol;
 
 import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -24,18 +25,24 @@ import com.google.cloud.spanner.pgadapter.metadata.ConnectionMetadata;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.IOException;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
 public class BoostrapMessageTest {
+  private ConnectionHandler connection;
 
-  @Test
-  public void testCreate() throws Exception {
-    ConnectionHandler connection = mock(ConnectionHandler.class);
+  private DataOutputStream dataOutputStream;
+
+  @Before
+  public void setupConnection() throws IOException {
+    connection = mock(ConnectionHandler.class);
     ProxyServer server = mock(ProxyServer.class);
     when(connection.getServer()).thenReturn(server);
     OptionsMetadata options = mock(OptionsMetadata.class);
@@ -45,8 +52,16 @@ public class BoostrapMessageTest {
     PipedInputStream pipedInputStream = new PipedInputStream(2048);
     DataInputStream dataInputStream = new DataInputStream(pipedInputStream);
     when(metadata.getInputStream()).thenReturn(dataInputStream);
-    DataOutputStream dataOutputStream =
-        new DataOutputStream(new PipedOutputStream(pipedInputStream));
+    dataOutputStream = new DataOutputStream(new PipedOutputStream(pipedInputStream));
+  }
+
+  @After
+  public void cleanup() throws IOException {
+    dataOutputStream.close();
+  }
+
+  @Test
+  public void testCreate() throws Exception {
     // length
     dataOutputStream.writeInt(1024);
     // identifier
@@ -57,4 +72,41 @@ public class BoostrapMessageTest {
     assertEquals(StartupMessage.class, message.getClass());
     assertEquals(message.length, 1024);
   }
+
+  @Test
+  public void testCreateSsl() throws Exception {
+    // length
+    dataOutputStream.writeInt(8);
+    // identifier
+    dataOutputStream.writeInt(SSLMessage.IDENTIFIER);
+    dataOutputStream.write(new byte[8]);
+
+    BootstrapMessage message = BootstrapMessage.create(connection);
+    assertEquals(SSLMessage.class, message.getClass());
+    assertEquals(message.length, 8);
+  }
+
+  @Test
+  public void testCreateCancel() throws Exception {
+    // length
+    dataOutputStream.writeInt(16);
+    // identifier
+    dataOutputStream.writeInt(CancelMessage.IDENTIFIER);
+    dataOutputStream.write(new byte[16]);
+
+    BootstrapMessage message = BootstrapMessage.create(connection);
+    assertEquals(CancelMessage.class, message.getClass());
+    assertEquals(message.length, 16);
+  }
+
+  @Test
+  public void testCreateUnknown() throws IOException {
+    // length
+    dataOutputStream.writeInt(1024);
+    // Identifier (this is not a valid message type).
+    dataOutputStream.writeInt(9999);
+    dataOutputStream.write(new byte[1024]);
+
+    assertThrows(IllegalStateException.class, () -> BootstrapMessage.create(connection));
+  }
 }