View Javadoc
1   package org.dbunit.ext.postgresql;
2   
3   import static org.assertj.core.api.Assertions.assertThat;
4   
5   import java.io.StringReader;
6   import java.sql.Statement;
7   import java.sql.Types;
8   import java.util.Objects;
9   
10  import org.dbunit.DatabaseEnvironment;
11  import org.dbunit.database.DatabaseConfig;
12  import org.dbunit.database.IDatabaseConnection;
13  import org.dbunit.dataset.Column;
14  import org.dbunit.dataset.IDataSet;
15  import org.dbunit.dataset.ITable;
16  import org.dbunit.dataset.ITableMetaData;
17  import org.dbunit.dataset.ReplacementDataSet;
18  import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
19  import org.dbunit.operation.DatabaseOperation;
20  import org.junit.jupiter.api.AfterEach;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
24  import org.xml.sax.InputSource;
25  
26  /**
27   * Integration test proving {@link ArrayType} round-trips PostgreSQL array
28   * column values - including a whole-column null, a null element, an empty
29   * array, and an element requiring quoting - through a real database (issue
30   * 646).
31   *
32   * @author Jeff Jensen
33   * @since 3.5.0
34   */
35  @EnabledIfSystemProperty(named = "dbunit.profile", matches = "postgresql")
36  class PostgresqlArrayIT
37  {
38      private IDatabaseConnection _connection;
39      private final String testTable = "array_test";
40      // @formatter:off
41      private static final String xmlData = "<?xml version=\"1.0\"?>" +
42              "<dataset>" +
43              "<ARRAY_TEST ID=\"1\" NUMS=\"{1,2,3}\" TAGS=\"{&quot;a&quot;,&quot;b,c&quot;}\" />" +
44              "<ARRAY_TEST ID=\"2\" NUMS=\"[NULL]\" TAGS=\"[NULL]\" />" +
45              "<ARRAY_TEST ID=\"3\" NUMS=\"{1,NULL,3}\" TAGS=\"{}\" />" +
46              "</dataset>";
47      // @formatter:on
48  
49      @BeforeEach
50      protected void setUp() throws Exception
51      {
52          // Load active postgreSQL profile and connection from Maven pom.xml.
53          _connection = DatabaseEnvironment.getInstance().getConnection();
54          try (Statement stat = _connection.getConnection().createStatement())
55          {
56              stat.execute("DROP TABLE IF EXISTS " + testTable + ";");
57              stat.execute("CREATE TABLE " + testTable
58                      + "(ID INTEGER NOT NULL, NUMS integer[], TAGS text[]);");
59          }
60          // Mirrors PostgresqlUuidIT: the table isn't visible to a fresh
61          // dataset without reopening the connection.
62          _connection.close();
63          _connection = DatabaseEnvironment.getInstance().getConnection();
64      }
65  
66      @AfterEach
67      protected void tearDown() throws Exception
68      {
69          if (!Objects.isNull(_connection))
70          {
71              try (Statement stat =
72                      _connection.getConnection().createStatement())
73              {
74                  stat.execute("DROP TABLE IF EXISTS " + testTable + ";");
75              } finally
76              {
77                  _connection.close();
78                  _connection = null;
79              }
80          }
81      }
82  
83      @Test
84      void testArrayDataType_withIntegerAndTextArrayColumns_roundTripsThroughDatabase()
85              throws Exception
86      {
87          assertThat(_connection).as("didn't get a connection.").isNotNull();
88          final DatabaseConfig config = _connection.getConfig();
89          config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
90                  new PostgresqlDataTypeFactory());
91  
92          final ReplacementDataSet dataSet =
93                  new ReplacementDataSet(new FlatXmlDataSetBuilder()
94                          .build(new InputSource(new StringReader(xmlData))));
95          dataSet.addReplacementObject("[NULL]", null);
96          dataSet.setStrictReplacement(true);
97  
98          final IDataSet metaDataSet = _connection.createDataSet();
99          final ITableMetaData itmd = metaDataSet.getTableMetaData(testTable);
100         boolean numsChecked = false;
101         boolean tagsChecked = false;
102         for (final Column col : itmd.getColumns())
103         {
104             if ("NUMS".equalsIgnoreCase(col.getColumnName()))
105             {
106                 numsChecked = true;
107                 assertThat(col.getDataType().getSqlType())
108                         .as("NUMS column sql type.").isEqualTo(Types.ARRAY);
109                 assertThat(col.getSqlTypeName()).as("NUMS column sql type name.")
110                         .isEqualTo("_int4");
111             } else if ("TAGS".equalsIgnoreCase(col.getColumnName()))
112             {
113                 tagsChecked = true;
114                 assertThat(col.getDataType().getSqlType())
115                         .as("TAGS column sql type.").isEqualTo(Types.ARRAY);
116                 assertThat(col.getSqlTypeName()).as("TAGS column sql type name.")
117                         .isEqualTo("_text");
118             }
119         }
120         assertThat(numsChecked).as("The NUMS column should be present in the metadata.")
121                 .isTrue();
122         assertThat(tagsChecked).as("The TAGS column should be present in the metadata.")
123                 .isTrue();
124 
125         DatabaseOperation.CLEAN_INSERT.execute(_connection, dataSet);
126 
127         // A plain createDataSet().getTable() issues no ORDER BY, so row
128         // order is not guaranteed; order explicitly since the assertions
129         // below index rows by their insertion order.
130         final ITable actualTable = _connection.createQueryTable(testTable,
131                 "SELECT * FROM " + testTable + " ORDER BY ID");
132 
133         assertThat(actualTable.getValue(0, "NUMS"))
134                 .as("Row 0 NUMS should round-trip its integer elements.")
135                 .isEqualTo("{1,2,3}");
136         assertThat(actualTable.getValue(0, "TAGS"))
137                 .as("Row 0 TAGS should round-trip, quoting only the element "
138                         + "containing the delimiter.")
139                 .isEqualTo("{a,\"b,c\"}");
140 
141         assertThat(actualTable.getValue(1, "NUMS"))
142                 .as("Row 1 NUMS should be null.").isNull();
143         assertThat(actualTable.getValue(1, "TAGS"))
144                 .as("Row 1 TAGS should be null.").isNull();
145 
146         assertThat(actualTable.getValue(2, "NUMS"))
147                 .as("Row 2 NUMS should round-trip its null element.")
148                 .isEqualTo("{1,NULL,3}");
149         assertThat(actualTable.getValue(2, "TAGS"))
150                 .as("Row 2 TAGS should round-trip as an empty array.")
151                 .isEqualTo("{}");
152     }
153 }