From a210914763a28a292c0609335771a722670ae780 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 8 Jul 2025 22:21:51 +0800 Subject: [PATCH 01/11] add harvestConnection in DbSpecification --- .../client/java/sql/DbSpecification.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbSpecification.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbSpecification.java index 5f540bee6d..8a4d3e9557 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbSpecification.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbSpecification.java @@ -19,6 +19,11 @@ public class DbSpecification { */ public final Connection connection; + /** + * sql connection to data which collected eg during production + */ + public final Connection harvestConnection; + /** * schema name * TODO might remove this later if we could get such info with the connection @@ -45,17 +50,22 @@ public class DbSpecification { public final boolean employSmartDbClean; - private DbSpecification(DatabaseType dbType, Connection connection, List schemaNames, String initSqlScript, String initSqlOnResourcePath, boolean employSmartDbClean) { + private DbSpecification(DatabaseType dbType, Connection connection, Connection harvestConnection, List schemaNames, String initSqlScript, String initSqlOnResourcePath, boolean employSmartDbClean) { this.dbType = Objects.requireNonNull(dbType); this.connection = Objects.requireNonNull(connection); this.schemaNames = schemaNames; this.initSqlScript = initSqlScript; this.initSqlOnResourcePath = initSqlOnResourcePath; this.employSmartDbClean = employSmartDbClean; + this.harvestConnection = harvestConnection; } public DbSpecification(DatabaseType dbType, Connection connection) { - this(dbType, connection, null, null, null, true); + this(dbType, connection, null, null, null, null, true); + } + + public DbSpecification(DatabaseType dbType, Connection connection, Connection harvestConnection) { + this(dbType, connection, harvestConnection, null, null, null, true); } public DbSpecification withSchemas(String... schemas){ @@ -72,6 +82,7 @@ public DbSpecification withSchemas(String... schemas){ return new DbSpecification( this.dbType, this.connection, + this.harvestConnection, Arrays.asList(schemas), this.initSqlScript, this.initSqlOnResourcePath, @@ -89,6 +100,7 @@ public DbSpecification withDisabledSmartClean(){ return new DbSpecification( this.dbType, this.connection, + this.harvestConnection, this.schemaNames, this.initSqlScript, this.initSqlOnResourcePath, @@ -111,6 +123,7 @@ public DbSpecification withInitSqlScript(String script){ return new DbSpecification( this.dbType, this.connection, + this.harvestConnection, this.schemaNames, script, this.initSqlOnResourcePath, @@ -132,6 +145,7 @@ public DbSpecification withInitSqlOnResourcePath(String path){ return new DbSpecification( this.dbType, this.connection, + this.harvestConnection, this.schemaNames, this.initSqlScript, path, From c3706d7bd94bf831920a33551facdeeb3482c440 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 8 Jul 2025 22:49:12 +0800 Subject: [PATCH 02/11] extra configs --- .../main/kotlin/org/evomaster/core/EMConfig.kt | 18 ++++++++++++++++++ docs/options.md | 3 +++ 2 files changed, 21 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index f6bb1b0394..db5df71254 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1674,6 +1674,24 @@ class EMConfig { @Probability var probOfSamplingScheduleTask = 0.0 + @Experimental + @Cfg("Probability of harvesting database records using a given data source (e.g., production data)") + @Probability + var probOfHarvestingDbRecords = 0.0 + + @Experimental + @Cfg("Specify a maximum number of harvested records per table") + @Min(0.0) + var maxSizeOfHarvestedDbRecordsPerTable = 0 + + @Experimental + @Cfg("Specify a strategy to harvest database records") + var useDbRecordHarvestStrategy = DatabaseRecordHarvestStrategy.RANDOME + + enum class DatabaseRecordHarvestStrategy{ + RANDOME + } + @Experimental @Cfg("Specify a maximum number of handling (remove/add) resource size at once, e.g., add 3 resource at most") @Min(0.0) diff --git a/docs/options.md b/docs/options.md index a26770c807..2b2606c5d7 100644 --- a/docs/options.md +++ b/docs/options.md @@ -266,12 +266,14 @@ There are 3 types of options: |`maxSizeDataPool`| __Int__. How much data elements, per key, can be stored in the Data Pool. Once limit is reached, new old will replace old data. *Constraints*: `min=1.0`. *Default value*: `100`.| |`maxSizeOfExistingDataToSample`| __Int__. Specify a maximum number of existing data in the database to sample in a test when SQL handling is enabled. Note that a negative number means all existing data would be sampled. *Default value*: `-1`.| |`maxSizeOfHandlingResource`| __Int__. Specify a maximum number of handling (remove/add) resource size at once, e.g., add 3 resource at most. *Constraints*: `min=0.0`. *Default value*: `0`.| +|`maxSizeOfHarvestedDbRecordsPerTable`| __Int__. Specify a maximum number of harvested records per table. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxSizeOfMutatingInitAction`| __Int__. Specify a maximum number of handling (remove/add) init actions at once, e.g., add 3 init actions at most. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxTestSizeStrategy`| __Enum__. Specify a strategy to handle a max size of a test. *Valid values*: `SPECIFIED, DPC_INCREASING, DPC_DECREASING`. *Default value*: `SPECIFIED`.| |`maxTestsPerTestSuite`| __Int__. Specify the maximum number of tests to be generated in one test suite. Note that a negative number presents no limit per test suite. *Default value*: `-1`.| |`mutationTargetsSelectionStrategy`| __Enum__. Specify a strategy to select targets for evaluating mutation. *Valid values*: `FIRST_NOT_COVERED_TARGET, EXPANDED_UPDATED_NOT_COVERED_TARGET, UPDATED_NOT_COVERED_TARGET`. *Default value*: `FIRST_NOT_COVERED_TARGET`.| |`prematureStopStrategy`| __Enum__. Specify how 'improvement' is defined: either any kind of improvement even if partial (ANY), or at least one new target is fully covered (NEW). *Valid values*: `ANY, NEW`. *Default value*: `NEW`.| |`probOfHandlingLength`| __Double__. Specify a probability of applying length handling. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| +|`probOfHarvestingDbRecords`| __Double__. Probability of harvesting database records using a given data source (e.g., production data). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`probOfHarvestingResponsesFromActualExternalServices`| __Double__. a probability of harvesting actual responses from external services as seeds. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`probOfMutatingResponsesBasedOnActualResponse`| __Double__. a probability of mutating mocked responses based on actual responses. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`probOfPrioritizingSuccessfulHarvestedActualResponses`| __Double__. a probability of prioritizing to employ successful harvested actual responses from external services as seeds (e.g., 2xx from HTTP external service). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| @@ -290,6 +292,7 @@ There are 3 types of options: |`targetHeuristicsFile`| __String__. Where the target heuristic values file (if any) is going to be written (in CSV format). It is only used when processFormat is TARGET_HEURISTIC. *Default value*: `targets.csv`.| |`testResourcePathToSaveMockedResponse`| __String__. Specify test resource path where to save mocked responses as separated files. *Default value*: `""`.| |`thresholdDistanceForDataPool`| __Int__. Threshold of Levenshtein Distance for key-matching in Data Pool. *Constraints*: `min=0.0`. *Default value*: `2`.| +|`useDbRecordHarvestStrategy`| __Enum__. Specify a strategy to harvest database records. *Valid values*: `RANDOME`. *Default value*: `RANDOME`.| |`useGlobalTaintInfoProbability`| __Double__. When sampling new individual, check whether to use already existing info on tainted values. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`useInsertionForSqlHeuristics`| __Boolean__. Specify whether insertions should be used to calculate SQL heuristics instead of retrieving data from real databases. *Default value*: `false`.| |`useTestMethodOrder`| __Boolean__. Adds TestMethodOrder annotation for JUnit 5 tests. *Default value*: `false`.| From 66239f449922cfc2e406b77d4bb59a0e903c6673 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 9 Sep 2025 10:50:09 +0800 Subject: [PATCH 03/11] support to define harvest connection per db --- .../client/java/controller/internal/SutController.java | 7 +++++++ .../org/evomaster/client/java/sql/internal/SqlHandler.java | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index be9cdded94..9afd4ebfc1 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -311,6 +311,7 @@ public final void enableComputeSqlHeuristicsOrExtractExecution( */ public final void initSqlHandler() { sqlHandler.setConnection(getConnectionIfExist()); + sqlHandler.setHarvestConnection(getHarvestConnectionIfExist()); sqlHandler.setSchema(getSqlDatabaseSchema()); } @@ -348,6 +349,12 @@ public final Connection getConnectionIfExist(){ || getDbSpecifications().isEmpty())? null: getDbSpecifications().get(0).connection; } + + public final Connection getHarvestConnectionIfExist(){ + return (getDbSpecifications() == null + || getDbSpecifications().isEmpty())? null: getDbSpecifications().get(0).harvestConnection; + } + /** * * @return whether to employ smart db clean diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java index 49e9181bf0..b0eaadd179 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java @@ -61,6 +61,8 @@ public class SqlHandler { private volatile Connection connection; + private volatile Connection harvestConnection; + private volatile boolean calculateHeuristics; private volatile boolean extractSqlExecution; @@ -109,6 +111,10 @@ public void setConnection(Connection connection) { this.connection = connection; } + public void setHarvestConnection(Connection harvestConnection) { + this.harvestConnection = harvestConnection; + } + public void setSchema(DbInfoDto schema) { this.schema = schema; } From 38bd8396290470f08b46bf5c35e2583d96a7df0b Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 9 Sep 2025 10:51:34 +0800 Subject: [PATCH 04/11] add a configuration in EMConfig for handling limits of seeded records per table --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 5 +++++ docs/options.md | 1 + 2 files changed, 6 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index c36a838148..ba93baac62 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1769,6 +1769,11 @@ class EMConfig { @Probability var probOfHarvestingDbRecords = 0.0 + @Experimental + @Cfg("Specify a maximum number of harvested records per table as seeds at the beginning of the search") + @Min(0.0) + var maxSizeOfInitHarvestedDbRecordsPerTable = 0 + @Experimental @Cfg("Specify a maximum number of harvested records per table") @Min(0.0) diff --git a/docs/options.md b/docs/options.md index 2138d99cd7..4da9b7bd43 100644 --- a/docs/options.md +++ b/docs/options.md @@ -273,6 +273,7 @@ There are 3 types of options: |`maxSizeOfExistingDataToSample`| __Int__. Specify a maximum number of existing data in the database to sample in a test when SQL handling is enabled. Note that a negative number means all existing data would be sampled. *Default value*: `-1`.| |`maxSizeOfHandlingResource`| __Int__. Specify a maximum number of handling (remove/add) resource size at once, e.g., add 3 resource at most. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxSizeOfHarvestedDbRecordsPerTable`| __Int__. Specify a maximum number of harvested records per table. *Constraints*: `min=0.0`. *Default value*: `0`.| +|`maxSizeOfInitHarvestedDbRecordsPerTable`| __Int__. Specify a maximum number of harvested records per table as seeds at the beginning of the search. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxSizeOfMutatingInitAction`| __Int__. Specify a maximum number of handling (remove/add) init actions at once, e.g., add 3 init actions at most. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxTestSizeStrategy`| __Enum__. Specify a strategy to handle a max size of a test. *Valid values*: `SPECIFIED, DPC_INCREASING, DPC_DECREASING`. *Default value*: `SPECIFIED`.| |`maxTestsPerTestSuite`| __Int__. Specify the maximum number of tests to be generated in one test suite. Note that a negative number presents no limit per test suite. *Default value*: `-1`.| From 4e7c168e2425f41cb50b30d2c59f43625963297d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 9 Sep 2025 14:23:10 +0800 Subject: [PATCH 05/11] sortTablesByDependency --- .../client/java/sql/DbInfoExtractor.java | 2 +- .../java/sql/internal/SqlDbHarvester.java | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbInfoExtractor.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbInfoExtractor.java index 1a78c486c4..e8641b8ac9 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbInfoExtractor.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DbInfoExtractor.java @@ -816,7 +816,7 @@ private static void addForeignKeyToAutoIncrement(DbInfoDto schema) { /** * @return a table DTO for a particular table name */ - private static TableDto getTable(DbInfoDto schema, String tableName) { + public static TableDto getTable(DbInfoDto schema, String tableName) { return schema.tables.stream() .filter(t -> t.name.equalsIgnoreCase(tableName)) .findFirst().orElse(null); diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java new file mode 100644 index 0000000000..0451992a81 --- /dev/null +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java @@ -0,0 +1,93 @@ +package org.evomaster.client.java.sql.internal; + + +import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto; +import org.evomaster.client.java.controller.api.dto.database.schema.TableDto; +import org.evomaster.client.java.controller.api.dto.database.schema.ForeignKeyDto; + +import java.util.*; + +import static org.evomaster.client.java.sql.DbInfoExtractor.getTable; + + +public class SqlDbHarvester { + + + /** + * get topology of the tables in the schema + * @param schema the sql schema for databases + * @return a sequence of tables + */ + public static List sortTablesByDependency(DbInfoDto schema) { + + Objects.requireNonNull(schema); + Objects.requireNonNull(schema.tables); + + List tables = schema.tables; + + Map inDegree = new HashMap<>(); + Map> graph = new HashMap<>(); + Map tableMap = new HashMap<>(); + + // init + for (TableDto table : tables) { + String key = getTableKey(table); + inDegree.put(key, 0); + graph.put(key, new ArrayList<>()); + tableMap.put(key, table); + } + + // construct dependency graph based on schema + for (TableDto childTable : tables) { + String childKey = getTableKey(childTable); + for (ForeignKeyDto fk : childTable.foreignKeys) { + TableDto parentTable = getTable(schema, fk.targetTable); + String parentKey = getTableKey(parentTable); + if (tableMap.containsKey(parentKey)) { + graph.get(parentKey).add(childKey); + inDegree.put(childKey, inDegree.get(childKey) + 1); + } + } + } + + // Kahn + Queue queue = new LinkedList<>(); + for (String tableKey : inDegree.keySet()) { + if (inDegree.get(tableKey) == 0) { + queue.add(tableKey); + } + } + + List result = new ArrayList<>(); + while (!queue.isEmpty()) { + String currentKey = queue.poll(); + result.add(tableMap.get(currentKey)); + + for (String neighborKey : graph.get(currentKey)) { + inDegree.put(neighborKey, inDegree.get(neighborKey) - 1); + if (inDegree.get(neighborKey) == 0) { + queue.add(neighborKey); + } + } + } + + if (result.size() != tables.size()) { + throw new RuntimeException("Unable to perform topological sort: circular foreign key dependency exists between tables"); + } + + return result; + } + + + private static String getTableKey(TableDto table) { + return (table.catalog != null ? table.catalog + "." : "") + + (table.schema != null ? table.schema + "." : "") + + table.name; + } + + private static String getTableKey(String catalog, String schema, String name) { + return (catalog != null ? catalog + "." : "") + + (schema != null ? schema + "." : "") + + name; + } +} From 843ad6e36a959c6bbd787176df3f617532221603 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 9 Sep 2025 14:27:12 +0800 Subject: [PATCH 06/11] minor --- .../java/sql/internal/SqlDbHarvester.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java index 0451992a81..c6ef5667be 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java @@ -14,7 +14,8 @@ public class SqlDbHarvester { /** - * get topology of the tables in the schema + * Sorts a list of tables from the schema in topological order based on their foreign key dependencies. + * This ensures that parent tables (referenced by foreign keys) are ordered before their child tables. * @param schema the sql schema for databases * @return a sequence of tables */ @@ -80,14 +81,18 @@ public static List sortTablesByDependency(DbInfoDto schema) { private static String getTableKey(TableDto table) { - return (table.catalog != null ? table.catalog + "." : "") + - (table.schema != null ? table.schema + "." : "") + - table.name; + return getTableKey(table.catalog, table.schema, table.name); } - private static String getTableKey(String catalog, String schema, String name) { - return (catalog != null ? catalog + "." : "") + - (schema != null ? schema + "." : "") + - name; + private static String getTableKey(String catalog, String schema, String tableName) { + StringBuilder key = new StringBuilder(); + if (catalog != null && !catalog.isEmpty()) { + key.append(catalog).append("."); + } + if (schema != null && !schema.isEmpty()) { + key.append(schema).append("."); + } + key.append(tableName); + return key.toString(); } } From de68a93fa74574798bf2a55534b12c562a76b731 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 10 Sep 2025 16:58:37 +0800 Subject: [PATCH 07/11] start to add tests --- .../internal/db/SqlDbHarvesterTest.java | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java new file mode 100644 index 0000000000..4bb212c252 --- /dev/null +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java @@ -0,0 +1,350 @@ +package org.evomaster.client.java.controller.internal.db; + +import org.evomaster.client.java.controller.DatabaseTestTemplate; +import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto; +import org.evomaster.client.java.controller.api.dto.database.schema.TableDto; +import org.evomaster.client.java.controller.internal.SutController; +import org.evomaster.client.java.controller.internal.db.sql.mysql.DatabaseFakeMySQLSutController; +import org.evomaster.client.java.controller.internal.db.sql.mysql.DatabaseMySQLTestInit; +import org.evomaster.client.java.sql.DbInfoExtractor; +import org.evomaster.client.java.sql.SqlScriptRunner; +import org.evomaster.client.java.sql.internal.SqlDbHarvester; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.util.List; + +public class SqlDbHarvesterTest extends DatabaseMySQLTestInit implements DatabaseTestTemplate { + + + + //@Test + public void testInitScript() throws Exception{ + String initTablesScripts = + "CREATE TABLE users (\n" + + " user_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " username VARCHAR(50) NOT NULL UNIQUE,\n" + + " email VARCHAR(100) NOT NULL UNIQUE,\n" + + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + ");\n" + + "\n" + + "CREATE TABLE user_addresses (\n" + + " address_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " user_id BIGINT NOT NULL,\n" + + " address_line VARCHAR(255) NOT NULL,\n" + + " city VARCHAR(100),\n" + + " postal_code VARCHAR(20),\n" + + " country VARCHAR(50) DEFAULT 'China',\n" + + " is_default BOOLEAN DEFAULT FALSE,\n" + + " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE\n" + + ");\n" + + "\n" + + "CREATE TABLE categories (\n" + + " category_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " name VARCHAR(100) NOT NULL UNIQUE,\n" + + " description TEXT\n" + + ");\n" + + "\n" + + "CREATE TABLE products (\n" + + " product_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " name VARCHAR(200) NOT NULL,\n" + + " description TEXT,\n" + + " price DECIMAL(10, 2) NOT NULL,\n" + + " stock INT DEFAULT 0,\n" + + " category_id BIGINT,\n" + + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + + " FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL\n" + + ");\n" + + "\n" + + "CREATE TABLE orders (\n" + + " order_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " user_id BIGINT NOT NULL,\n" + + " total_amount DECIMAL(12, 2) NOT NULL,\n" + + " status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',\n" + + " order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + + " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE\n" + + ");\n" + + "\n" + + "CREATE TABLE order_items (\n" + + " item_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " order_id BIGINT NOT NULL,\n" + + " product_id BIGINT NOT NULL,\n" + + " quantity INT NOT NULL CHECK (quantity > 0),\n" + + " unit_price DECIMAL(10, 2) NOT NULL,\n" + + " FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,\n" + + " FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE\n" + + ");\n" + + "\n" + + "CREATE TABLE payments (\n" + + " payment_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " order_id BIGINT NOT NULL UNIQUE,\n" + + " amount DECIMAL(12, 2) NOT NULL,\n" + + " payment_method VARCHAR(50) NOT NULL,\n" + + " payment_status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',\n" + + " transaction_id VARCHAR(100),\n" + + " paid_at TIMESTAMP NULL,\n" + + " FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE\n" + + ");\n" + + "\n" + + "CREATE TABLE reviews (\n" + + " review_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " user_id BIGINT NOT NULL,\n" + + " product_id BIGINT NOT NULL,\n" + + " rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5),\n" + + " comment TEXT,\n" + + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + + " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,\n" + + " FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE\n" + + ");\n" + + "\n" + + "CREATE TABLE coupons (\n" + + " coupon_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " code VARCHAR(50) NOT NULL UNIQUE,\n" + + " discount_percent DECIMAL(5, 2) CHECK (discount_percent BETWEEN 0 AND 100),\n" + + " valid_from DATE,\n" + + " valid_until DATE,\n" + + " is_active BOOLEAN DEFAULT TRUE\n" + + ");\n" + + "\n" + + "CREATE TABLE user_coupons (\n" + + " user_coupon_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + + " user_id BIGINT NOT NULL,\n" + + " coupon_id BIGINT NOT NULL,\n" + + " used BOOLEAN DEFAULT FALSE,\n" + + " used_at TIMESTAMP NULL,\n" + + " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,\n" + + " FOREIGN KEY (coupon_id) REFERENCES coupons(coupon_id) ON DELETE CASCADE,\n" + + " UNIQUE (user_id, coupon_id)\n" + + ");"; + + SqlScriptRunner.execCommand(getConnection(),initTablesScripts); + + + String initRecordsScripts = + "INSERT INTO users (username, email) VALUES\n" + + "('user01', 'user01@example.com'),\n" + + "('user02', 'user02@example.com'),\n" + + "('user03', 'user03@example.com'),\n" + + "('user04', 'user04@example.com'),\n" + + "('user05', 'user05@example.com'),\n" + + "('user06', 'user06@example.com'),\n" + + "('user07', 'user07@example.com'),\n" + + "('user08', 'user08@example.com'),\n" + + "('user09', 'user09@example.com'),\n" + + "('user10', 'user10@example.com'),\n" + + "('user11', 'user11@example.com'),\n" + + "('user12', 'user12@example.com'),\n" + + "('user13', 'user13@example.com'),\n" + + "('user14', 'user14@example.com'),\n" + + "('user15', 'user15@example.com'),\n" + + "('user16', 'user16@example.com'),\n" + + "('user17', 'user17@example.com'),\n" + + "('user18', 'user18@example.com'),\n" + + "('user19', 'user19@example.com'),\n" + + "('user20', 'user20@example.com');\n" + + "\n" + + "INSERT INTO categories (name, description) VALUES\n" + + "('Electronics', 'Smartphones, laptops, accessories'),\n" + + "('Books', 'Fiction, non-fiction, educational'),\n" + + "('Clothing', 'Men, women, and children apparel'),\n" + + "('Home & Kitchen', 'Furniture, utensils, decor'),\n" + + "('Sports', 'Fitness equipment, sportswear');\n" + + "\n" + + "INSERT INTO products (name, description, price, stock, category_id) VALUES\n" + + "('iPhone 15', 'Latest Apple smartphone', 999.99, 50, 1),\n" + + "('Samsung Galaxy S24', 'Android flagship', 899.99, 40, 1),\n" + + "('MacBook Pro 16\"', 'Powerful laptop for pros', 2499.99, 20, 1),\n" + + "('Dell XPS 13', 'Ultra portable business laptop', 1199.99, 25, 1),\n" + + "('iPad Air', 'Lightweight tablet', 599.99, 60, 1),\n" + + "('Harry Potter Set', 'Complete 7-book collection', 89.99, 100, 2),\n" + + "('The Alchemist', 'Paulo Coelho bestseller', 12.99, 200, 2),\n" + + "('Atomic Habits', 'Self-improvement guide', 14.99, 150, 2),\n" + + "('Dune', 'Sci-fi classic by Frank Herbert', 16.99, 120, 2),\n" + + "('T-Shirt Cotton White', 'Comfortable everyday wear', 19.99, 300, 3),\n" + + "('Jeans Slim Fit Blue', 'Classic denim jeans', 49.99, 150, 3),\n" + + "('Running Shoes Nike', 'Lightweight for jogging', 89.99, 80, 3),\n" + + "('Yoga Mat', 'Non-slip exercise mat', 29.99, 120, 5),\n" + + "('Dumbbell Set 10kg', 'Adjustable weight set', 199.99, 40, 5),\n" + + "('Coffee Maker', 'Automatic drip machine', 79.99, 70, 4),\n" + + "('Air Fryer', 'Healthy oil-free frying', 129.99, 50, 4),\n" + + "('Blender 1000W', 'Powerful kitchen blender', 59.99, 90, 4),\n" + + "('Desk Lamp LED', 'Adjustable brightness', 35.99, 110, 4),\n" + + "('Water Bottle 1L', 'Stainless steel insulated', 25.99, 200, 5),\n" + + "('Backpack Waterproof', 'For hiking and travel', 69.99, 80, 5);\n" + + "\n" + + "INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES\n" + + "(1, '123 Main St', 'Beijing', '100001', 'China', TRUE),\n" + + "(2, '456 Oak Ave', 'Shanghai', '200001', 'China', TRUE),\n" + + "(3, '789 Pine Rd', 'Guangzhou', '510001', 'China', FALSE),\n" + + "(4, '321 Elm Blvd', 'Shenzhen', '518001', 'China', TRUE),\n" + + "(5, '654 Cedar Ln', 'Hangzhou', '310001', 'China', TRUE),\n" + + "(6, '987 Maple Dr', 'Chengdu', '610001', 'China', FALSE),\n" + + "(7, '147 Birch St', 'Chongqing', '400001', 'China', TRUE),\n" + + "(8, '258 Spruce Ave', 'Nanjing', '210001', 'China', TRUE),\n" + + "(9, '369 Willow Rd', 'Wuhan', '430001', 'China', FALSE),\n" + + "(10, '159 Aspen Blvd', 'Xi’an', '710001', 'China', TRUE),\n" + + "(11, '357 Redwood Ln', 'Tianjin', '300001', 'China', TRUE),\n" + + "(12, '753 Sequoia Dr', 'Suzhou', '215001', 'China', FALSE),\n" + + "(13, '951 Fir St', 'Dalian', '116001', 'China', TRUE),\n" + + "(14, '852 Cypress Ave', 'Qingdao', '266001', 'China', TRUE),\n" + + "(15, '741 Palm Rd', 'Xiamen', '361001', 'China', FALSE),\n" + + "(16, '630 Olive Blvd', 'Changsha', '410001', 'China', TRUE),\n" + + "(17, '520 Cherry Ln', 'Zhengzhou', '450001', 'China', TRUE),\n" + + "(18, '410 Peach Dr', 'Harbin', '150001', 'China', FALSE),\n" + + "(19, '300 Plum St', 'Kunming', '650001', 'China', TRUE),\n" + + "(20, '200 Orange Ave', 'Fuzhou', '350001', 'China', TRUE);\n" + + "\n" + + "INSERT INTO orders (user_id, total_amount, status, order_date) VALUES\n" + + "(1, 1299.98, 'delivered', '2025-03-01 10:30:00'),\n" + + "(2, 89.99, 'shipped', '2025-03-02 14:15:00'),\n" + + "(3, 2499.99, 'paid', '2025-03-03 09:00:00'),\n" + + "(4, 109.98, 'pending', '2025-03-04 16:45:00'),\n" + + "(5, 199.99, 'delivered', '2025-02-28 11:20:00'),\n" + + "(6, 359.97, 'shipped', '2025-03-05 13:10:00'),\n" + + "(7, 79.99, 'delivered', '2025-03-01 17:50:00'),\n" + + "(8, 159.98, 'paid', '2025-03-06 08:30:00'),\n" + + "(9, 89.99, 'pending', '2025-03-07 12:00:00'),\n" + + "(10, 299.98, 'shipped', '2025-03-08 15:30:00'),\n" + + "(11, 59.99, 'delivered', '2025-03-02 10:00:00'),\n" + + "(12, 189.98, 'paid', '2025-03-09 14:20:00'),\n" + + "(13, 69.99, 'pending', '2025-03-10 09:45:00'),\n" + + "(14, 109.98, 'shipped', '2025-03-11 16:10:00'),\n" + + "(15, 2499.99, 'delivered', '2025-02-27 11:00:00'),\n" + + "(16, 149.98, 'paid', '2025-03-12 13:30:00'),\n" + + "(17, 129.99, 'pending', '2025-03-13 08:15:00'),\n" + + "(18, 39.98, 'shipped', '2025-03-14 17:00:00'),\n" + + "(19, 199.99, 'delivered', '2025-03-03 12:30:00'),\n" + + "(20, 89.99, 'paid', '2025-03-15 10:45:00');\n" + + "\n" + + "INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES\n" + + "(1, 1, 1, 999.99), -- iPhone\n" + + "(1, 6, 3, 89.99), -- Books\n" + + "(2, 6, 1, 89.99),\n" + + "(3, 3, 1, 2499.99),\n" + + "(4, 10, 2, 19.99), -- T-Shirts\n" + + "(4, 11, 1, 49.99), -- Jeans\n" + + "(5, 13, 1, 199.99), -- Dumbbell\n" + + "(6, 16, 1, 129.99), -- Air Fryer\n" + + "(6, 17, 1, 59.99), -- Blender\n" + + "(6, 18, 2, 35.99), -- Lamps\n" + + "(7, 16, 1, 79.99),\n" + + "(8, 12, 1, 89.99), -- Shoes\n" + + "(8, 14, 1, 29.99), -- Yoga Mat\n" + + "(9, 12, 1, 89.99),\n" + + "(10, 2, 1, 899.99), -- Galaxy\n" + + "(10, 19, 1, 25.99), -- Bottle\n" + + "(11, 17, 1, 59.99),\n" + + "(12, 10, 5, 19.99), -- 5 T-Shirts\n" + + "(12, 11, 2, 49.99), -- 2 Jeans\n" + + "(13, 20, 1, 69.99), -- Backpack\n" + + "(14, 7, 2, 12.99), -- 2x The Alchemist\n" + + "(14, 8, 2, 14.99), -- 2x Atomic Habits\n" + + "(15, 3, 1, 2499.99),\n" + + "(16, 8, 3, 14.99), -- 3x Atomic Habits\n" + + "(16, 9, 3, 16.99), -- 3x Dune\n" + + "(17, 16, 1, 129.99),\n" + + "(18, 18, 1, 35.99), -- Lamp\n" + + "(18, 19, 1, 25.99), -- Bottle\n" + + "(19, 13, 1, 199.99),\n" + + "(20, 12, 1, 89.99);\n" + + "\n" + + "INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES\n" + + "(1, 1299.98, 'Credit Card', 'completed', 'txn_001', '2025-03-01 10:35:00'),\n" + + "(2, 89.99, 'Alipay', 'completed', 'txn_002', '2025-03-02 14:20:00'),\n" + + "(3, 2499.99, 'WeChat Pay', 'completed', 'txn_003', '2025-03-03 09:05:00'),\n" + + "(4, 109.98, 'Credit Card', 'pending', NULL, NULL),\n" + + "(5, 199.99, 'Alipay', 'completed', 'txn_005', '2025-02-28 11:25:00'),\n" + + "(6, 359.97, 'WeChat Pay', 'completed', 'txn_006', '2025-03-05 13:15:00'),\n" + + "(7, 79.99, 'Credit Card', 'completed', 'txn_007', '2025-03-01 17:55:00'),\n" + + "(8, 159.98, 'Alipay', 'completed', 'txn_008', '2025-03-06 08:35:00'),\n" + + "(9, 89.99, 'WeChat Pay', 'pending', NULL, NULL),\n" + + "(10, 299.98, 'Credit Card', 'completed', 'txn_010', '2025-03-08 15:35:00'),\n" + + "(11, 59.99, 'Alipay', 'completed', 'txn_011', '2025-03-02 10:05:00'),\n" + + "(12, 189.98, 'WeChat Pay', 'completed', 'txn_012', '2025-03-09 14:25:00'),\n" + + "(13, 69.99, 'Credit Card', 'pending', NULL, NULL),\n" + + "(14, 109.98, 'Alipay', 'completed', 'txn_014', '2025-03-11 16:15:00'),\n" + + "(15, 2499.99, 'Credit Card', 'completed', 'txn_015', '2025-02-27 11:05:00'),\n" + + "(16, 149.98, 'WeChat Pay', 'completed', 'txn_016', '2025-03-12 13:35:00'),\n" + + "(17, 129.99, 'Alipay', 'pending', NULL, NULL),\n" + + "(18, 39.98, 'Credit Card', 'completed', 'txn_018', '2025-03-14 17:05:00'),\n" + + "(19, 199.99, 'WeChat Pay', 'completed', 'txn_019', '2025-03-03 12:35:00'),\n" + + "(20, 89.99, 'Alipay', 'completed', 'txn_020', '2025-03-15 10:50:00');\n" + + "\n" + + "INSERT INTO reviews (user_id, product_id, rating, comment) VALUES\n" + + "(1, 1, 5, 'Amazing phone, worth every penny!'),\n" + + "(2, 6, 4, 'Great book set for Harry Potter fans.'),\n" + + "(3, 3, 5, 'Best laptop I have ever used.'),\n" + + "(4, 10, 3, 'Comfortable but runs a bit small.'),\n" + + "(5, 13, 4, 'Good quality dumbbells, solid build.'),\n" + + "(6, 16, 5, 'Love my air fryer, cooks perfectly.'),\n" + + "(7, 16, 4, 'Easy to use, cleans up nicely.'),\n" + + "(8, 12, 5, 'Perfect for my morning runs.'),\n" + + "(9, 12, 4, 'Good grip and cushioning.'),\n" + + "(10, 2, 4, 'Great Android phone, battery life is good.'),\n" + + "(11, 17, 3, 'Blender is okay, a bit noisy.'),\n" + + "(12, 10, 5, 'Bought 5 for my team, all love them!'),\n" + + "(13, 20, 4, 'Spacious and waterproof, great for travel.'),\n" + + "(14, 7, 5, 'Life-changing book, highly recommend.'),\n" + + "(15, 3, 5, 'Worth the investment for professionals.'),\n" + + "(16, 8, 5, 'Helped me build better habits.'),\n" + + "(17, 16, 4, 'Heats up fast, easy controls.'),\n" + + "(18, 18, 3, 'Lamp is functional but design is plain.'),\n" + + "(19, 13, 5, 'Perfect for home workouts.'),\n" + + "(20, 12, 4, 'Comfortable for long walks too.');\n" + + "\n" + + "INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES\n" + + "('WELCOME10', 10.00, '2025-01-01', '2025-12-31', TRUE),\n" + + "('SPRING20', 20.00, '2025-03-01', '2025-04-30', TRUE),\n" + + "('SUMMER25', 25.00, '2025-06-01', '2025-08-31', FALSE),\n" + + "('HOLIDAY30', 30.00, '2025-12-01', '2025-12-31', FALSE),\n" + + "('FLASH5', 5.00, '2025-03-15', '2025-03-16', TRUE),\n" + + "('VIP15', 15.00, '2025-01-01', '2025-12-31', TRUE),\n" + + "('NEWUSER10', 10.00, '2025-01-01', '2025-12-31', TRUE),\n" + + "('ELECTRO15', 15.00, '2025-03-01', '2025-03-31', TRUE),\n" + + "('BOOKS20', 20.00, '2025-03-01', '2025-03-31', TRUE),\n" + + "('FASHION10', 10.00, '2025-03-01', '2025-04-15', TRUE),\n" + + "('FITNESS25', 25.00, '2025-05-01', '2025-06-30', FALSE),\n" + + "('KITCHEN15', 15.00, '2025-03-01', '2025-04-30', TRUE),\n" + + "('BACKTOSCHOOL', 12.00, '2025-08-01', '2025-09-15', FALSE),\n" + + "('LOYALTY5', 5.00, '2025-01-01', '2025-12-31', TRUE),\n" + + "('FREESHIP', 0.00, '2025-03-01', '2025-03-31', TRUE); -- Free shipping coupon\n" + + "\n" + + "INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES\n" + + "(1, 1, TRUE, '2025-03-01 10:30:00'),\n" + + "(2, 1, TRUE, '2025-03-02 14:15:00'),\n" + + "(3, 2, FALSE, NULL),\n" + + "(4, 3, FALSE, NULL),\n" + + "(5, 4, FALSE, NULL),\n" + + "(6, 5, TRUE, '2025-03-05 13:10:00'),\n" + + "(7, 6, TRUE, '2025-03-01 17:50:00'),\n" + + "(8, 7, TRUE, '2025-03-06 08:30:00'),\n" + + "(9, 8, FALSE, NULL),\n" + + "(10, 9, TRUE, '2025-03-08 15:30:00'),\n" + + "(11, 10, TRUE, '2025-03-02 10:00:00'),\n" + + "(12, 11, FALSE, NULL),\n" + + "(13, 12, FALSE, NULL),\n" + + "(14, 13, FALSE, NULL),\n" + + "(15, 14, TRUE, '2025-02-27 11:00:00'),\n" + + "(16, 15, TRUE, '2025-03-12 13:30:00'),\n" + + "(17, 1, TRUE, '2025-03-13 08:15:00'),\n" + + "(18, 2, FALSE, NULL),\n" + + "(19, 3, FALSE, NULL),\n" + + "(20, 4, FALSE, NULL);"; + + SqlScriptRunner.execCommand(getConnection(),initRecordsScripts); + DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection()); + List tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto); + // TODO + + } + + @Override + public Connection getConnection() { + return connection; + } + + @Override + public SutController getSutController() { + return new DatabaseFakeMySQLSutController(connection); + } +} From a25172f4a5c4e2fc1857d171efed47cbcf954184 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 11 Sep 2025 16:52:15 +0800 Subject: [PATCH 08/11] add tests for sortTablesByDependency --- .../internal/db/SqlDbHarvesterTest.java | 333 +----------------- .../src/test/resources/db_schemas/order.sql | 104 ++++++ .../test/resources/db_schemas/order_init.sql | 209 +++++++++++ 3 files changed, 330 insertions(+), 316 deletions(-) create mode 100644 client-java/controller/src/test/resources/db_schemas/order.sql create mode 100644 client-java/controller/src/test/resources/db_schemas/order_init.sql diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java index 4bb212c252..5265c1dc9f 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java @@ -12,329 +12,30 @@ import org.junit.jupiter.api.Test; import java.sql.Connection; +import java.util.Arrays; import java.util.List; -public class SqlDbHarvesterTest extends DatabaseMySQLTestInit implements DatabaseTestTemplate { - - - - //@Test - public void testInitScript() throws Exception{ - String initTablesScripts = - "CREATE TABLE users (\n" + - " user_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " username VARCHAR(50) NOT NULL UNIQUE,\n" + - " email VARCHAR(100) NOT NULL UNIQUE,\n" + - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + - ");\n" + - "\n" + - "CREATE TABLE user_addresses (\n" + - " address_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " user_id BIGINT NOT NULL,\n" + - " address_line VARCHAR(255) NOT NULL,\n" + - " city VARCHAR(100),\n" + - " postal_code VARCHAR(20),\n" + - " country VARCHAR(50) DEFAULT 'China',\n" + - " is_default BOOLEAN DEFAULT FALSE,\n" + - " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE\n" + - ");\n" + - "\n" + - "CREATE TABLE categories (\n" + - " category_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " name VARCHAR(100) NOT NULL UNIQUE,\n" + - " description TEXT\n" + - ");\n" + - "\n" + - "CREATE TABLE products (\n" + - " product_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " name VARCHAR(200) NOT NULL,\n" + - " description TEXT,\n" + - " price DECIMAL(10, 2) NOT NULL,\n" + - " stock INT DEFAULT 0,\n" + - " category_id BIGINT,\n" + - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + - " FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL\n" + - ");\n" + - "\n" + - "CREATE TABLE orders (\n" + - " order_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " user_id BIGINT NOT NULL,\n" + - " total_amount DECIMAL(12, 2) NOT NULL,\n" + - " status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',\n" + - " order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + - " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE\n" + - ");\n" + - "\n" + - "CREATE TABLE order_items (\n" + - " item_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " order_id BIGINT NOT NULL,\n" + - " product_id BIGINT NOT NULL,\n" + - " quantity INT NOT NULL CHECK (quantity > 0),\n" + - " unit_price DECIMAL(10, 2) NOT NULL,\n" + - " FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,\n" + - " FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE\n" + - ");\n" + - "\n" + - "CREATE TABLE payments (\n" + - " payment_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " order_id BIGINT NOT NULL UNIQUE,\n" + - " amount DECIMAL(12, 2) NOT NULL,\n" + - " payment_method VARCHAR(50) NOT NULL,\n" + - " payment_status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',\n" + - " transaction_id VARCHAR(100),\n" + - " paid_at TIMESTAMP NULL,\n" + - " FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE\n" + - ");\n" + - "\n" + - "CREATE TABLE reviews (\n" + - " review_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " user_id BIGINT NOT NULL,\n" + - " product_id BIGINT NOT NULL,\n" + - " rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5),\n" + - " comment TEXT,\n" + - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n" + - " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,\n" + - " FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE\n" + - ");\n" + - "\n" + - "CREATE TABLE coupons (\n" + - " coupon_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " code VARCHAR(50) NOT NULL UNIQUE,\n" + - " discount_percent DECIMAL(5, 2) CHECK (discount_percent BETWEEN 0 AND 100),\n" + - " valid_from DATE,\n" + - " valid_until DATE,\n" + - " is_active BOOLEAN DEFAULT TRUE\n" + - ");\n" + - "\n" + - "CREATE TABLE user_coupons (\n" + - " user_coupon_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" + - " user_id BIGINT NOT NULL,\n" + - " coupon_id BIGINT NOT NULL,\n" + - " used BOOLEAN DEFAULT FALSE,\n" + - " used_at TIMESTAMP NULL,\n" + - " FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,\n" + - " FOREIGN KEY (coupon_id) REFERENCES coupons(coupon_id) ON DELETE CASCADE,\n" + - " UNIQUE (user_id, coupon_id)\n" + - ");"; - - SqlScriptRunner.execCommand(getConnection(),initTablesScripts); +import static org.junit.jupiter.api.Assertions.assertEquals; +public class SqlDbHarvesterTest extends DatabaseMySQLTestInit implements DatabaseTestTemplate { - String initRecordsScripts = - "INSERT INTO users (username, email) VALUES\n" + - "('user01', 'user01@example.com'),\n" + - "('user02', 'user02@example.com'),\n" + - "('user03', 'user03@example.com'),\n" + - "('user04', 'user04@example.com'),\n" + - "('user05', 'user05@example.com'),\n" + - "('user06', 'user06@example.com'),\n" + - "('user07', 'user07@example.com'),\n" + - "('user08', 'user08@example.com'),\n" + - "('user09', 'user09@example.com'),\n" + - "('user10', 'user10@example.com'),\n" + - "('user11', 'user11@example.com'),\n" + - "('user12', 'user12@example.com'),\n" + - "('user13', 'user13@example.com'),\n" + - "('user14', 'user14@example.com'),\n" + - "('user15', 'user15@example.com'),\n" + - "('user16', 'user16@example.com'),\n" + - "('user17', 'user17@example.com'),\n" + - "('user18', 'user18@example.com'),\n" + - "('user19', 'user19@example.com'),\n" + - "('user20', 'user20@example.com');\n" + - "\n" + - "INSERT INTO categories (name, description) VALUES\n" + - "('Electronics', 'Smartphones, laptops, accessories'),\n" + - "('Books', 'Fiction, non-fiction, educational'),\n" + - "('Clothing', 'Men, women, and children apparel'),\n" + - "('Home & Kitchen', 'Furniture, utensils, decor'),\n" + - "('Sports', 'Fitness equipment, sportswear');\n" + - "\n" + - "INSERT INTO products (name, description, price, stock, category_id) VALUES\n" + - "('iPhone 15', 'Latest Apple smartphone', 999.99, 50, 1),\n" + - "('Samsung Galaxy S24', 'Android flagship', 899.99, 40, 1),\n" + - "('MacBook Pro 16\"', 'Powerful laptop for pros', 2499.99, 20, 1),\n" + - "('Dell XPS 13', 'Ultra portable business laptop', 1199.99, 25, 1),\n" + - "('iPad Air', 'Lightweight tablet', 599.99, 60, 1),\n" + - "('Harry Potter Set', 'Complete 7-book collection', 89.99, 100, 2),\n" + - "('The Alchemist', 'Paulo Coelho bestseller', 12.99, 200, 2),\n" + - "('Atomic Habits', 'Self-improvement guide', 14.99, 150, 2),\n" + - "('Dune', 'Sci-fi classic by Frank Herbert', 16.99, 120, 2),\n" + - "('T-Shirt Cotton White', 'Comfortable everyday wear', 19.99, 300, 3),\n" + - "('Jeans Slim Fit Blue', 'Classic denim jeans', 49.99, 150, 3),\n" + - "('Running Shoes Nike', 'Lightweight for jogging', 89.99, 80, 3),\n" + - "('Yoga Mat', 'Non-slip exercise mat', 29.99, 120, 5),\n" + - "('Dumbbell Set 10kg', 'Adjustable weight set', 199.99, 40, 5),\n" + - "('Coffee Maker', 'Automatic drip machine', 79.99, 70, 4),\n" + - "('Air Fryer', 'Healthy oil-free frying', 129.99, 50, 4),\n" + - "('Blender 1000W', 'Powerful kitchen blender', 59.99, 90, 4),\n" + - "('Desk Lamp LED', 'Adjustable brightness', 35.99, 110, 4),\n" + - "('Water Bottle 1L', 'Stainless steel insulated', 25.99, 200, 5),\n" + - "('Backpack Waterproof', 'For hiking and travel', 69.99, 80, 5);\n" + - "\n" + - "INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES\n" + - "(1, '123 Main St', 'Beijing', '100001', 'China', TRUE),\n" + - "(2, '456 Oak Ave', 'Shanghai', '200001', 'China', TRUE),\n" + - "(3, '789 Pine Rd', 'Guangzhou', '510001', 'China', FALSE),\n" + - "(4, '321 Elm Blvd', 'Shenzhen', '518001', 'China', TRUE),\n" + - "(5, '654 Cedar Ln', 'Hangzhou', '310001', 'China', TRUE),\n" + - "(6, '987 Maple Dr', 'Chengdu', '610001', 'China', FALSE),\n" + - "(7, '147 Birch St', 'Chongqing', '400001', 'China', TRUE),\n" + - "(8, '258 Spruce Ave', 'Nanjing', '210001', 'China', TRUE),\n" + - "(9, '369 Willow Rd', 'Wuhan', '430001', 'China', FALSE),\n" + - "(10, '159 Aspen Blvd', 'Xi’an', '710001', 'China', TRUE),\n" + - "(11, '357 Redwood Ln', 'Tianjin', '300001', 'China', TRUE),\n" + - "(12, '753 Sequoia Dr', 'Suzhou', '215001', 'China', FALSE),\n" + - "(13, '951 Fir St', 'Dalian', '116001', 'China', TRUE),\n" + - "(14, '852 Cypress Ave', 'Qingdao', '266001', 'China', TRUE),\n" + - "(15, '741 Palm Rd', 'Xiamen', '361001', 'China', FALSE),\n" + - "(16, '630 Olive Blvd', 'Changsha', '410001', 'China', TRUE),\n" + - "(17, '520 Cherry Ln', 'Zhengzhou', '450001', 'China', TRUE),\n" + - "(18, '410 Peach Dr', 'Harbin', '150001', 'China', FALSE),\n" + - "(19, '300 Plum St', 'Kunming', '650001', 'China', TRUE),\n" + - "(20, '200 Orange Ave', 'Fuzhou', '350001', 'China', TRUE);\n" + - "\n" + - "INSERT INTO orders (user_id, total_amount, status, order_date) VALUES\n" + - "(1, 1299.98, 'delivered', '2025-03-01 10:30:00'),\n" + - "(2, 89.99, 'shipped', '2025-03-02 14:15:00'),\n" + - "(3, 2499.99, 'paid', '2025-03-03 09:00:00'),\n" + - "(4, 109.98, 'pending', '2025-03-04 16:45:00'),\n" + - "(5, 199.99, 'delivered', '2025-02-28 11:20:00'),\n" + - "(6, 359.97, 'shipped', '2025-03-05 13:10:00'),\n" + - "(7, 79.99, 'delivered', '2025-03-01 17:50:00'),\n" + - "(8, 159.98, 'paid', '2025-03-06 08:30:00'),\n" + - "(9, 89.99, 'pending', '2025-03-07 12:00:00'),\n" + - "(10, 299.98, 'shipped', '2025-03-08 15:30:00'),\n" + - "(11, 59.99, 'delivered', '2025-03-02 10:00:00'),\n" + - "(12, 189.98, 'paid', '2025-03-09 14:20:00'),\n" + - "(13, 69.99, 'pending', '2025-03-10 09:45:00'),\n" + - "(14, 109.98, 'shipped', '2025-03-11 16:10:00'),\n" + - "(15, 2499.99, 'delivered', '2025-02-27 11:00:00'),\n" + - "(16, 149.98, 'paid', '2025-03-12 13:30:00'),\n" + - "(17, 129.99, 'pending', '2025-03-13 08:15:00'),\n" + - "(18, 39.98, 'shipped', '2025-03-14 17:00:00'),\n" + - "(19, 199.99, 'delivered', '2025-03-03 12:30:00'),\n" + - "(20, 89.99, 'paid', '2025-03-15 10:45:00');\n" + - "\n" + - "INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES\n" + - "(1, 1, 1, 999.99), -- iPhone\n" + - "(1, 6, 3, 89.99), -- Books\n" + - "(2, 6, 1, 89.99),\n" + - "(3, 3, 1, 2499.99),\n" + - "(4, 10, 2, 19.99), -- T-Shirts\n" + - "(4, 11, 1, 49.99), -- Jeans\n" + - "(5, 13, 1, 199.99), -- Dumbbell\n" + - "(6, 16, 1, 129.99), -- Air Fryer\n" + - "(6, 17, 1, 59.99), -- Blender\n" + - "(6, 18, 2, 35.99), -- Lamps\n" + - "(7, 16, 1, 79.99),\n" + - "(8, 12, 1, 89.99), -- Shoes\n" + - "(8, 14, 1, 29.99), -- Yoga Mat\n" + - "(9, 12, 1, 89.99),\n" + - "(10, 2, 1, 899.99), -- Galaxy\n" + - "(10, 19, 1, 25.99), -- Bottle\n" + - "(11, 17, 1, 59.99),\n" + - "(12, 10, 5, 19.99), -- 5 T-Shirts\n" + - "(12, 11, 2, 49.99), -- 2 Jeans\n" + - "(13, 20, 1, 69.99), -- Backpack\n" + - "(14, 7, 2, 12.99), -- 2x The Alchemist\n" + - "(14, 8, 2, 14.99), -- 2x Atomic Habits\n" + - "(15, 3, 1, 2499.99),\n" + - "(16, 8, 3, 14.99), -- 3x Atomic Habits\n" + - "(16, 9, 3, 16.99), -- 3x Dune\n" + - "(17, 16, 1, 129.99),\n" + - "(18, 18, 1, 35.99), -- Lamp\n" + - "(18, 19, 1, 25.99), -- Bottle\n" + - "(19, 13, 1, 199.99),\n" + - "(20, 12, 1, 89.99);\n" + - "\n" + - "INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES\n" + - "(1, 1299.98, 'Credit Card', 'completed', 'txn_001', '2025-03-01 10:35:00'),\n" + - "(2, 89.99, 'Alipay', 'completed', 'txn_002', '2025-03-02 14:20:00'),\n" + - "(3, 2499.99, 'WeChat Pay', 'completed', 'txn_003', '2025-03-03 09:05:00'),\n" + - "(4, 109.98, 'Credit Card', 'pending', NULL, NULL),\n" + - "(5, 199.99, 'Alipay', 'completed', 'txn_005', '2025-02-28 11:25:00'),\n" + - "(6, 359.97, 'WeChat Pay', 'completed', 'txn_006', '2025-03-05 13:15:00'),\n" + - "(7, 79.99, 'Credit Card', 'completed', 'txn_007', '2025-03-01 17:55:00'),\n" + - "(8, 159.98, 'Alipay', 'completed', 'txn_008', '2025-03-06 08:35:00'),\n" + - "(9, 89.99, 'WeChat Pay', 'pending', NULL, NULL),\n" + - "(10, 299.98, 'Credit Card', 'completed', 'txn_010', '2025-03-08 15:35:00'),\n" + - "(11, 59.99, 'Alipay', 'completed', 'txn_011', '2025-03-02 10:05:00'),\n" + - "(12, 189.98, 'WeChat Pay', 'completed', 'txn_012', '2025-03-09 14:25:00'),\n" + - "(13, 69.99, 'Credit Card', 'pending', NULL, NULL),\n" + - "(14, 109.98, 'Alipay', 'completed', 'txn_014', '2025-03-11 16:15:00'),\n" + - "(15, 2499.99, 'Credit Card', 'completed', 'txn_015', '2025-02-27 11:05:00'),\n" + - "(16, 149.98, 'WeChat Pay', 'completed', 'txn_016', '2025-03-12 13:35:00'),\n" + - "(17, 129.99, 'Alipay', 'pending', NULL, NULL),\n" + - "(18, 39.98, 'Credit Card', 'completed', 'txn_018', '2025-03-14 17:05:00'),\n" + - "(19, 199.99, 'WeChat Pay', 'completed', 'txn_019', '2025-03-03 12:35:00'),\n" + - "(20, 89.99, 'Alipay', 'completed', 'txn_020', '2025-03-15 10:50:00');\n" + - "\n" + - "INSERT INTO reviews (user_id, product_id, rating, comment) VALUES\n" + - "(1, 1, 5, 'Amazing phone, worth every penny!'),\n" + - "(2, 6, 4, 'Great book set for Harry Potter fans.'),\n" + - "(3, 3, 5, 'Best laptop I have ever used.'),\n" + - "(4, 10, 3, 'Comfortable but runs a bit small.'),\n" + - "(5, 13, 4, 'Good quality dumbbells, solid build.'),\n" + - "(6, 16, 5, 'Love my air fryer, cooks perfectly.'),\n" + - "(7, 16, 4, 'Easy to use, cleans up nicely.'),\n" + - "(8, 12, 5, 'Perfect for my morning runs.'),\n" + - "(9, 12, 4, 'Good grip and cushioning.'),\n" + - "(10, 2, 4, 'Great Android phone, battery life is good.'),\n" + - "(11, 17, 3, 'Blender is okay, a bit noisy.'),\n" + - "(12, 10, 5, 'Bought 5 for my team, all love them!'),\n" + - "(13, 20, 4, 'Spacious and waterproof, great for travel.'),\n" + - "(14, 7, 5, 'Life-changing book, highly recommend.'),\n" + - "(15, 3, 5, 'Worth the investment for professionals.'),\n" + - "(16, 8, 5, 'Helped me build better habits.'),\n" + - "(17, 16, 4, 'Heats up fast, easy controls.'),\n" + - "(18, 18, 3, 'Lamp is functional but design is plain.'),\n" + - "(19, 13, 5, 'Perfect for home workouts.'),\n" + - "(20, 12, 4, 'Comfortable for long walks too.');\n" + - "\n" + - "INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES\n" + - "('WELCOME10', 10.00, '2025-01-01', '2025-12-31', TRUE),\n" + - "('SPRING20', 20.00, '2025-03-01', '2025-04-30', TRUE),\n" + - "('SUMMER25', 25.00, '2025-06-01', '2025-08-31', FALSE),\n" + - "('HOLIDAY30', 30.00, '2025-12-01', '2025-12-31', FALSE),\n" + - "('FLASH5', 5.00, '2025-03-15', '2025-03-16', TRUE),\n" + - "('VIP15', 15.00, '2025-01-01', '2025-12-31', TRUE),\n" + - "('NEWUSER10', 10.00, '2025-01-01', '2025-12-31', TRUE),\n" + - "('ELECTRO15', 15.00, '2025-03-01', '2025-03-31', TRUE),\n" + - "('BOOKS20', 20.00, '2025-03-01', '2025-03-31', TRUE),\n" + - "('FASHION10', 10.00, '2025-03-01', '2025-04-15', TRUE),\n" + - "('FITNESS25', 25.00, '2025-05-01', '2025-06-30', FALSE),\n" + - "('KITCHEN15', 15.00, '2025-03-01', '2025-04-30', TRUE),\n" + - "('BACKTOSCHOOL', 12.00, '2025-08-01', '2025-09-15', FALSE),\n" + - "('LOYALTY5', 5.00, '2025-01-01', '2025-12-31', TRUE),\n" + - "('FREESHIP', 0.00, '2025-03-01', '2025-03-31', TRUE); -- Free shipping coupon\n" + - "\n" + - "INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES\n" + - "(1, 1, TRUE, '2025-03-01 10:30:00'),\n" + - "(2, 1, TRUE, '2025-03-02 14:15:00'),\n" + - "(3, 2, FALSE, NULL),\n" + - "(4, 3, FALSE, NULL),\n" + - "(5, 4, FALSE, NULL),\n" + - "(6, 5, TRUE, '2025-03-05 13:10:00'),\n" + - "(7, 6, TRUE, '2025-03-01 17:50:00'),\n" + - "(8, 7, TRUE, '2025-03-06 08:30:00'),\n" + - "(9, 8, FALSE, NULL),\n" + - "(10, 9, TRUE, '2025-03-08 15:30:00'),\n" + - "(11, 10, TRUE, '2025-03-02 10:00:00'),\n" + - "(12, 11, FALSE, NULL),\n" + - "(13, 12, FALSE, NULL),\n" + - "(14, 13, FALSE, NULL),\n" + - "(15, 14, TRUE, '2025-02-27 11:00:00'),\n" + - "(16, 15, TRUE, '2025-03-12 13:30:00'),\n" + - "(17, 1, TRUE, '2025-03-13 08:15:00'),\n" + - "(18, 2, FALSE, NULL),\n" + - "(19, 3, FALSE, NULL),\n" + - "(20, 4, FALSE, NULL);"; - SqlScriptRunner.execCommand(getConnection(),initRecordsScripts); + @Test + public void testSortTablesByDependency() throws Exception{ + SqlScriptRunner.runScriptFromResourceFile(getConnection(), "/db_schemas/order.sql"); DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection()); List tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto); - // TODO + assertEquals(tables.size(), 10); + Arrays.asList("coupons","categories","users","products").stream().allMatch( + s -> tables.subList(0,4).contains(s) + ); + Arrays.asList("orders","user_addresses").stream().allMatch( + s -> tables.subList(4,6).contains(s) + ); + + Arrays.asList("user_coupons","reviews","order_items","payments").stream().allMatch( + s -> tables.subList(6,10).contains(s) + ); } diff --git a/client-java/controller/src/test/resources/db_schemas/order.sql b/client-java/controller/src/test/resources/db_schemas/order.sql new file mode 100644 index 0000000000..4f5eb38ec3 --- /dev/null +++ b/client-java/controller/src/test/resources/db_schemas/order.sql @@ -0,0 +1,104 @@ +CREATE TABLE users ( + user_id BIGINT NOT NULL AUTO_INCREMENT, + username VARCHAR(50) NOT NULL UNIQUE, + email VARCHAR(100) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id)); + +CREATE TABLE user_addresses ( + address_id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL, + address_line VARCHAR(255) NOT NULL, + city VARCHAR(100), + postal_code VARCHAR(20), + country VARCHAR(50) DEFAULT 'China', + is_default BOOLEAN DEFAULT FALSE, + PRIMARY KEY (address_id), + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +); + +CREATE TABLE categories ( + category_id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(100) NOT NULL UNIQUE, + PRIMARY KEY (category_id), + description TEXT +); + +CREATE TABLE products ( + product_id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + description TEXT, + price DECIMAL(10, 2) NOT NULL, + stock INT DEFAULT 0, + category_id BIGINT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (product_id), + FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL +); + +CREATE TABLE orders ( + order_id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL, + total_amount DECIMAL(12, 2) NOT NULL, + status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending', + order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (order_id), + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +); + +CREATE TABLE order_items ( + item_id BIGINT NOT NULL AUTO_INCREMENT, + order_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + quantity INT NOT NULL CHECK (quantity > 0), + unit_price DECIMAL(10, 2) NOT NULL, + PRIMARY KEY (item_id), + FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, + FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE +); + +CREATE TABLE payments ( + payment_id BIGINT NOT NULL AUTO_INCREMENT, + order_id BIGINT NOT NULL UNIQUE, + amount DECIMAL(12, 2) NOT NULL, + payment_method VARCHAR(50) NOT NULL, + payment_status ENUM('pending', 'completed', 'failed') DEFAULT 'pending', + transaction_id VARCHAR(100), + paid_at TIMESTAMP NULL, + PRIMARY KEY (payment_id), + FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE +); + +CREATE TABLE reviews ( + review_id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5), + comment TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (review_id), + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE +); + +CREATE TABLE coupons ( + coupon_id BIGINT NOT NULL AUTO_INCREMENT, + code VARCHAR(50) NOT NULL UNIQUE, + discount_percent DECIMAL(5, 2) CHECK (discount_percent BETWEEN 0 AND 100), + valid_from DATE, + valid_until DATE, + is_active BOOLEAN DEFAULT TRUE, + PRIMARY KEY (coupon_id) +); + +CREATE TABLE user_coupons ( + user_coupon_id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL, + coupon_id BIGINT NOT NULL, + used BOOLEAN DEFAULT FALSE, + used_at TIMESTAMP NULL, + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + FOREIGN KEY (coupon_id) REFERENCES coupons(coupon_id) ON DELETE CASCADE, + UNIQUE (user_id, coupon_id), + PRIMARY KEY (user_coupon_id) +); \ No newline at end of file diff --git a/client-java/controller/src/test/resources/db_schemas/order_init.sql b/client-java/controller/src/test/resources/db_schemas/order_init.sql new file mode 100644 index 0000000000..2cfbf9d39d --- /dev/null +++ b/client-java/controller/src/test/resources/db_schemas/order_init.sql @@ -0,0 +1,209 @@ +INSERT INTO users (username, email) VALUES + ('user01', 'user01@example.com'), + ('user02', 'user02@example.com'), + ('user03', 'user03@example.com'), + ('user04', 'user04@example.com'), + ('user05', 'user05@example.com'), + ('user06', 'user06@example.com'), + ('user07', 'user07@example.com'), + ('user08', 'user08@example.com'), + ('user09', 'user09@example.com'), + ('user10', 'user10@example.com'), + ('user11', 'user11@example.com'), + ('user12', 'user12@example.com'), + ('user13', 'user13@example.com'), + ('user14', 'user14@example.com'), + ('user15', 'user15@example.com'), + ('user16', 'user16@example.com'), + ('user17', 'user17@example.com'), + ('user18', 'user18@example.com'), + ('user19', 'user19@example.com'), + ('user20', 'user20@example.com'); + +INSERT INTO categories (name, description) VALUES + ('Electronics', 'Smartphones, laptops, accessories'), + ('Books', 'Fiction, non-fiction, educational'), + ('Clothing', 'Men, women, and children apparel'), + ('Home & Kitchen', 'Furniture, utensils, decor'), + ('Sports', 'Fitness equipment, sportswear'); + +INSERT INTO products (name, description, price, stock, category_id) VALUES + ('iPhone 15', 'Latest Apple smartphone', 999.99, 50, 1), + ('Samsung Galaxy S24', 'Android flagship', 899.99, 40, 1), + ('MacBook Pro 16"', 'Powerful laptop for pros', 2499.99, 20, 1), + ('Dell XPS 13', 'Ultra portable business laptop', 1199.99, 25, 1), + ('iPad Air', 'Lightweight tablet', 599.99, 60, 1), + ('Harry Potter Set', 'Complete 7-book collection', 89.99, 100, 2), + ('The Alchemist', 'Paulo Coelho bestseller', 12.99, 200, 2), + ('Atomic Habits', 'Self-improvement guide', 14.99, 150, 2), + ('Dune', 'Sci-fi classic by Frank Herbert', 16.99, 120, 2), + ('T-Shirt Cotton White', 'Comfortable everyday wear', 19.99, 300, 3), + ('Jeans Slim Fit Blue', 'Classic denim jeans', 49.99, 150, 3), + ('Running Shoes Nike', 'Lightweight for jogging', 89.99, 80, 3), + ('Yoga Mat', 'Non-slip exercise mat', 29.99, 120, 5), + ('Dumbbell Set 10kg', 'Adjustable weight set', 199.99, 40, 5), + ('Coffee Maker', 'Automatic drip machine', 79.99, 70, 4), + ('Air Fryer', 'Healthy oil-free frying', 129.99, 50, 4), + ('Blender 1000W', 'Powerful kitchen blender', 59.99, 90, 4), + ('Desk Lamp LED', 'Adjustable brightness', 35.99, 110, 4), + ('Water Bottle 1L', 'Stainless steel insulated', 25.99, 200, 5), + ('Backpack Waterproof', 'For hiking and travel', 69.99, 80, 5); + +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES + (1, '123 Main St', 'Beijing', '100001', 'China', TRUE), + (2, '456 Oak Ave', 'Shanghai', '200001', 'China', TRUE), + (3, '789 Pine Rd', 'Guangzhou', '510001', 'China', FALSE), + (4, '321 Elm Blvd', 'Shenzhen', '518001', 'China', TRUE), + (5, '654 Cedar Ln', 'Hangzhou', '310001', 'China', TRUE), + (6, '987 Maple Dr', 'Chengdu', '610001', 'China', FALSE), + (7, '147 Birch St', 'Chongqing', '400001', 'China', TRUE), + (8, '258 Spruce Ave', 'Nanjing', '210001', 'China', TRUE), + (9, '369 Willow Rd', 'Wuhan', '430001', 'China', FALSE), + (10, '159 Aspen Blvd', 'Xi’an', '710001', 'China', TRUE), + (11, '357 Redwood Ln', 'Tianjin', '300001', 'China', TRUE), + (12, '753 Sequoia Dr', 'Suzhou', '215001', 'China', FALSE), + (13, '951 Fir St', 'Dalian', '116001', 'China', TRUE), + (14, '852 Cypress Ave', 'Qingdao', '266001', 'China', TRUE), + (15, '741 Palm Rd', 'Xiamen', '361001', 'China', FALSE), + (16, '630 Olive Blvd', 'Changsha', '410001', 'China', TRUE), + (17, '520 Cherry Ln', 'Zhengzhou', '450001', 'China', TRUE), + (18, '410 Peach Dr', 'Harbin', '150001', 'China', FALSE), + (19, '300 Plum St', 'Kunming', '650001', 'China', TRUE), + (20, '200 Orange Ave', 'Fuzhou', '350001', 'China', TRUE); + +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES + (1, 1299.98, 'delivered', '2025-03-01 10:30:00'), + (2, 89.99, 'shipped', '2025-03-02 14:15:00'), + (3, 2499.99, 'paid', '2025-03-03 09:00:00'), + (4, 109.98, 'pending', '2025-03-04 16:45:00'), + (5, 199.99, 'delivered', '2025-02-28 11:20:00'), + (6, 359.97, 'shipped', '2025-03-05 13:10:00'), + (7, 79.99, 'delivered', '2025-03-01 17:50:00'), + (8, 159.98, 'paid', '2025-03-06 08:30:00'), + (9, 89.99, 'pending', '2025-03-07 12:00:00'), + (10, 299.98, 'shipped', '2025-03-08 15:30:00'), + (11, 59.99, 'delivered', '2025-03-02 10:00:00'), + (12, 189.98, 'paid', '2025-03-09 14:20:00'), + (13, 69.99, 'pending', '2025-03-10 09:45:00'), + (14, 109.98, 'shipped', '2025-03-11 16:10:00'), + (15, 2499.99, 'delivered', '2025-02-27 11:00:00'), + (16, 149.98, 'paid', '2025-03-12 13:30:00'), + (17, 129.99, 'pending', '2025-03-13 08:15:00'), + (18, 39.98, 'shipped', '2025-03-14 17:00:00'), + (19, 199.99, 'delivered', '2025-03-03 12:30:00'), + (20, 89.99, 'paid', '2025-03-15 10:45:00'); + +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES + (1, 1, 1, 999.99), -- iPhone + (1, 6, 3, 89.99), -- Books + (2, 6, 1, 89.99), + (3, 3, 1, 2499.99), + (4, 10, 2, 19.99), -- T-Shirts + (4, 11, 1, 49.99), -- Jeans + (5, 13, 1, 199.99), -- Dumbbell + (6, 16, 1, 129.99), -- Air Fryer + (6, 17, 1, 59.99), -- Blender + (6, 18, 2, 35.99), -- Lamps + (7, 16, 1, 79.99), + (8, 12, 1, 89.99), -- Shoes + (8, 14, 1, 29.99), -- Yoga Mat + (9, 12, 1, 89.99), + (10, 2, 1, 899.99), -- Galaxy + (10, 19, 1, 25.99), -- Bottle + (11, 17, 1, 59.99), + (12, 10, 5, 19.99), -- 5 T-Shirts + (12, 11, 2, 49.99), -- 2 Jeans + (13, 20, 1, 69.99), -- Backpack + (14, 7, 2, 12.99), -- 2x The Alchemist + (14, 8, 2, 14.99), -- 2x Atomic Habits + (15, 3, 1, 2499.99), + (16, 8, 3, 14.99), -- 3x Atomic Habits + (16, 9, 3, 16.99), -- 3x Dune + (17, 16, 1, 129.99), + (18, 18, 1, 35.99), -- Lamp + (18, 19, 1, 25.99), -- Bottle + (19, 13, 1, 199.99), + (20, 12, 1, 89.99); + +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES + (1, 1299.98, 'Credit Card', 'completed', 'txn_001', '2025-03-01 10:35:00'), + (2, 89.99, 'Alipay', 'completed', 'txn_002', '2025-03-02 14:20:00'), + (3, 2499.99, 'WeChat Pay', 'completed', 'txn_003', '2025-03-03 09:05:00'), + (4, 109.98, 'Credit Card', 'pending', NULL, NULL), + (5, 199.99, 'Alipay', 'completed', 'txn_005', '2025-02-28 11:25:00'), + (6, 359.97, 'WeChat Pay', 'completed', 'txn_006', '2025-03-05 13:15:00'), + (7, 79.99, 'Credit Card', 'completed', 'txn_007', '2025-03-01 17:55:00'), + (8, 159.98, 'Alipay', 'completed', 'txn_008', '2025-03-06 08:35:00'), + (9, 89.99, 'WeChat Pay', 'pending', NULL, NULL), + (10, 299.98, 'Credit Card', 'completed', 'txn_010', '2025-03-08 15:35:00'), + (11, 59.99, 'Alipay', 'completed', 'txn_011', '2025-03-02 10:05:00'), + (12, 189.98, 'WeChat Pay', 'completed', 'txn_012', '2025-03-09 14:25:00'), + (13, 69.99, 'Credit Card', 'pending', NULL, NULL), + (14, 109.98, 'Alipay', 'completed', 'txn_014', '2025-03-11 16:15:00'), + (15, 2499.99, 'Credit Card', 'completed', 'txn_015', '2025-02-27 11:05:00'), + (16, 149.98, 'WeChat Pay', 'completed', 'txn_016', '2025-03-12 13:35:00'), + (17, 129.99, 'Alipay', 'pending', NULL, NULL), + (18, 39.98, 'Credit Card', 'completed', 'txn_018', '2025-03-14 17:05:00'), + (19, 199.99, 'WeChat Pay', 'completed', 'txn_019', '2025-03-03 12:35:00'), + (20, 89.99, 'Alipay', 'completed', 'txn_020', '2025-03-15 10:50:00'); + +INSERT INTO reviews (user_id, product_id, rating, comment) VALUES + (1, 1, 5, 'Amazing phone, worth every penny!'), + (2, 6, 4, 'Great book set for Harry Potter fans.'), + (3, 3, 5, 'Best laptop I have ever used.'), + (4, 10, 3, 'Comfortable but runs a bit small.'), + (5, 13, 4, 'Good quality dumbbells, solid build.'), + (6, 16, 5, 'Love my air fryer, cooks perfectly.'), + (7, 16, 4, 'Easy to use, cleans up nicely.'), + (8, 12, 5, 'Perfect for my morning runs.'), + (9, 12, 4, 'Good grip and cushioning.'), + (10, 2, 4, 'Great Android phone, battery life is good.'), + (11, 17, 3, 'Blender is okay, a bit noisy.'), + (12, 10, 5, 'Bought 5 for my team, all love them!'), + (13, 20, 4, 'Spacious and waterproof, great for travel.'), + (14, 7, 5, 'Life-changing book, highly recommend.'), + (15, 3, 5, 'Worth the investment for professionals.'), + (16, 8, 5, 'Helped me build better habits.'), + (17, 16, 4, 'Heats up fast, easy controls.'), + (18, 18, 3, 'Lamp is functional but design is plain.'), + (19, 13, 5, 'Perfect for home workouts.'), + (20, 12, 4, 'Comfortable for long walks too.'); + +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES + ('WELCOME10', 10.00, '2025-01-01', '2025-12-31', TRUE), + ('SPRING20', 20.00, '2025-03-01', '2025-04-30', TRUE), + ('SUMMER25', 25.00, '2025-06-01', '2025-08-31', FALSE), + ('HOLIDAY30', 30.00, '2025-12-01', '2025-12-31', FALSE), + ('FLASH5', 5.00, '2025-03-15', '2025-03-16', TRUE), + ('VIP15', 15.00, '2025-01-01', '2025-12-31', TRUE), + ('NEWUSER10', 10.00, '2025-01-01', '2025-12-31', TRUE), + ('ELECTRO15', 15.00, '2025-03-01', '2025-03-31', TRUE), + ('BOOKS20', 20.00, '2025-03-01', '2025-03-31', TRUE), + ('FASHION10', 10.00, '2025-03-01', '2025-04-15', TRUE), + ('FITNESS25', 25.00, '2025-05-01', '2025-06-30', FALSE), + ('KITCHEN15', 15.00, '2025-03-01', '2025-04-30', TRUE), + ('BACKTOSCHOOL', 12.00, '2025-08-01', '2025-09-15', FALSE), + ('LOYALTY5', 5.00, '2025-01-01', '2025-12-31', TRUE), + ('FREESHIP', 0.00, '2025-03-01', '2025-03-31', TRUE); -- Free shipping coupon + +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES + (1, 1, TRUE, '2025-03-01 10:30:00'), + (2, 1, TRUE, '2025-03-02 14:15:00'), + (3, 2, FALSE, NULL), + (4, 3, FALSE, NULL), + (5, 4, FALSE, NULL), + (6, 5, TRUE, '2025-03-05 13:10:00'), + (7, 6, TRUE, '2025-03-01 17:50:00'), + (8, 7, TRUE, '2025-03-06 08:30:00'), + (9, 8, FALSE, NULL), + (10, 9, TRUE, '2025-03-08 15:30:00'), + (11, 10, TRUE, '2025-03-02 10:00:00'), + (12, 11, FALSE, NULL), + (13, 12, FALSE, NULL), + (14, 13, FALSE, NULL), + (15, 14, TRUE, '2025-02-27 11:00:00'), + (16, 15, TRUE, '2025-03-12 13:30:00'), + (17, 1, TRUE, '2025-03-13 08:15:00'), + (18, 2, FALSE, NULL), + (19, 3, FALSE, NULL), + (20, 4, FALSE, NULL); \ No newline at end of file From de34007b3c74b64b3a129df44eb777fdde53feeb Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 12 Sep 2025 00:08:34 +0800 Subject: [PATCH 09/11] generateSelectSqls --- .../java/sql/internal/SqlDbHarvester.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java index c6ef5667be..b017e0f7dd 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDbHarvester.java @@ -6,6 +6,7 @@ import org.evomaster.client.java.controller.api.dto.database.schema.ForeignKeyDto; import java.util.*; +import java.util.stream.Collectors; import static org.evomaster.client.java.sql.DbInfoExtractor.getTable; @@ -80,6 +81,42 @@ public static List sortTablesByDependency(DbInfoDto schema) { } + public static Map generateSelectSqls(List tables, int limit) { + Map selectSqls = new LinkedHashMap<>(); + + for (TableDto table : tables) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT "); + + String columns = table.columns.stream() + .map(c -> quoteIdentifier(table.name)+"."+quoteIdentifier(c.name)) + .collect(Collectors.joining(", ")); + sql.append(columns).append(" "); + + sql.append("FROM ").append(quoteIdentifier(table.name)); + + for (ForeignKeyDto fk : table.foreignKeys) { + sql.append(" JOIN ").append(quoteIdentifier(fk.targetTable)).append(" ON "); + for (int i = 0; i < fk.sourceColumns.size(); i++) { + if (i > 0) sql.append(" AND "); + sql.append(quoteIdentifier(table.name)).append(".").append(quoteIdentifier(fk.sourceColumns.get(i))) + .append(" = ") + .append(quoteIdentifier(fk.targetTable)).append(".").append(quoteIdentifier(fk.sourceColumns.get(i))); + } + } + + sql.append(" LIMIT ").append(limit).append(";"); + + selectSqls.put(table.name, sql.toString()); + } + + return selectSqls; + } + + private static String quoteIdentifier(String identifier) { + return "`" + identifier.replace("`", "``") + "`"; + } + private static String getTableKey(TableDto table) { return getTableKey(table.catalog, table.schema, table.name); } From 7243042bf98daacad20ff1b0a14b7ca8da20d9ed Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 12 Sep 2025 00:10:59 +0800 Subject: [PATCH 10/11] refactor test --- .../internal/db/SqlDbHarvesterTest.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java index 5265c1dc9f..436faab10d 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java @@ -7,25 +7,38 @@ import org.evomaster.client.java.controller.internal.db.sql.mysql.DatabaseFakeMySQLSutController; import org.evomaster.client.java.controller.internal.db.sql.mysql.DatabaseMySQLTestInit; import org.evomaster.client.java.sql.DbInfoExtractor; +import org.evomaster.client.java.sql.QueryResult; import org.evomaster.client.java.sql.SqlScriptRunner; import org.evomaster.client.java.sql.internal.SqlDbHarvester; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; public class SqlDbHarvesterTest extends DatabaseMySQLTestInit implements DatabaseTestTemplate { + @BeforeAll + public static void createTable() throws Exception { + SqlScriptRunner.runScriptFromResourceFile(connection, "/db_schemas/order.sql"); + SqlScriptRunner.runScriptFromResourceFile(connection, "/db_schemas/order_init.sql"); + } + + @Test + public void testInitTableSize() throws Exception{ + DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection()); + assertEquals(dbInfoDto.tables.size(), 10); + + } @Test public void testSortTablesByDependency() throws Exception{ - SqlScriptRunner.runScriptFromResourceFile(getConnection(), "/db_schemas/order.sql"); DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection()); List tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto); - assertEquals(tables.size(), 10); Arrays.asList("coupons","categories","users","products").stream().allMatch( s -> tables.subList(0,4).contains(s) ); @@ -36,7 +49,6 @@ public void testSortTablesByDependency() throws Exception{ Arrays.asList("user_coupons","reviews","order_items","payments").stream().allMatch( s -> tables.subList(6,10).contains(s) ); - } @Override From 9e42c959fb72bfc1a609c6526df8ba1b543854a5 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 12 Sep 2025 00:16:21 +0800 Subject: [PATCH 11/11] add testGenerateSelectSqls --- .../internal/db/SqlDbHarvesterTest.java | 23 ++ .../test/resources/db_schemas/order_init.sql | 370 ++++++++---------- 2 files changed, 193 insertions(+), 200 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java index 436faab10d..6442ded493 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/SqlDbHarvesterTest.java @@ -50,6 +50,29 @@ public void testSortTablesByDependency() throws Exception{ s -> tables.subList(6,10).contains(s) ); } + @Test + public void testGenerateSelectSqls() throws Exception{ + DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection()); + List tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto); + Map selects = SqlDbHarvester.generateSelectSqls(tables, 3); + List expectedSelectSqls = Arrays.asList("SELECT `coupons`.`coupon_id`, `coupons`.`code`, `coupons`.`discount_percent`, `coupons`.`valid_from`, `coupons`.`valid_until`, `coupons`.`is_active` FROM `coupons` LIMIT 3;", + "SELECT `categories`.`category_id`, `categories`.`name`, `categories`.`description` FROM `categories` LIMIT 3;", + "SELECT `users`.`user_id`, `users`.`username`, `users`.`email`, `users`.`created_at` FROM `users` LIMIT 3;", + "SELECT `products`.`product_id`, `products`.`name`, `products`.`description`, `products`.`price`, `products`.`stock`, `products`.`category_id`, `products`.`created_at` FROM `products` JOIN `categories` ON `products`.`category_id` = `categories`.`category_id` LIMIT 3;", + "SELECT `orders`.`order_id`, `orders`.`user_id`, `orders`.`total_amount`, `orders`.`status`, `orders`.`order_date` FROM `orders` JOIN `users` ON `orders`.`user_id` = `users`.`user_id` LIMIT 3;", + "SELECT `user_addresses`.`address_id`, `user_addresses`.`user_id`, `user_addresses`.`address_line`, `user_addresses`.`city`, `user_addresses`.`postal_code`, `user_addresses`.`country`, `user_addresses`.`is_default` FROM `user_addresses` JOIN `users` ON `user_addresses`.`user_id` = `users`.`user_id` LIMIT 3;", + "SELECT `user_coupons`.`user_coupon_id`, `user_coupons`.`user_id`, `user_coupons`.`coupon_id`, `user_coupons`.`used`, `user_coupons`.`used_at` FROM `user_coupons` JOIN `coupons` ON `user_coupons`.`coupon_id` = `coupons`.`coupon_id` JOIN `users` ON `user_coupons`.`user_id` = `users`.`user_id` LIMIT 3;", + "SELECT `reviews`.`review_id`, `reviews`.`user_id`, `reviews`.`product_id`, `reviews`.`rating`, `reviews`.`comment`, `reviews`.`created_at` FROM `reviews` JOIN `products` ON `reviews`.`product_id` = `products`.`product_id` JOIN `users` ON `reviews`.`user_id` = `users`.`user_id` LIMIT 3;", + "SELECT `order_items`.`item_id`, `order_items`.`order_id`, `order_items`.`product_id`, `order_items`.`quantity`, `order_items`.`unit_price` FROM `order_items` JOIN `orders` ON `order_items`.`order_id` = `orders`.`order_id` JOIN `products` ON `order_items`.`product_id` = `products`.`product_id` LIMIT 3;", + "SELECT `payments`.`payment_id`, `payments`.`order_id`, `payments`.`amount`, `payments`.`payment_method`, `payments`.`payment_status`, `payments`.`transaction_id`, `payments`.`paid_at` FROM `payments` JOIN `orders` ON `payments`.`order_id` = `orders`.`order_id` LIMIT 3;" + ); + + assertEquals(expectedSelectSqls.size(), 10); + expectedSelectSqls.stream().allMatch(s -> selects.values().contains(s)); +// for (Map.Entry entry : selects.entrySet()) { +// QueryResult qr = SqlScriptRunner.execCommand(getConnection(), entry.getValue()); +// } + } @Override public Connection getConnection() { diff --git a/client-java/controller/src/test/resources/db_schemas/order_init.sql b/client-java/controller/src/test/resources/db_schemas/order_init.sql index 2cfbf9d39d..df0e1d3e25 100644 --- a/client-java/controller/src/test/resources/db_schemas/order_init.sql +++ b/client-java/controller/src/test/resources/db_schemas/order_init.sql @@ -1,209 +1,179 @@ -INSERT INTO users (username, email) VALUES - ('user01', 'user01@example.com'), - ('user02', 'user02@example.com'), - ('user03', 'user03@example.com'), - ('user04', 'user04@example.com'), - ('user05', 'user05@example.com'), - ('user06', 'user06@example.com'), - ('user07', 'user07@example.com'), - ('user08', 'user08@example.com'), - ('user09', 'user09@example.com'), - ('user10', 'user10@example.com'), - ('user11', 'user11@example.com'), - ('user12', 'user12@example.com'), - ('user13', 'user13@example.com'), - ('user14', 'user14@example.com'), - ('user15', 'user15@example.com'), - ('user16', 'user16@example.com'), - ('user17', 'user17@example.com'), - ('user18', 'user18@example.com'), - ('user19', 'user19@example.com'), - ('user20', 'user20@example.com'); +INSERT INTO users (username, email) VALUES ('user01', 'user01@example.com'); +INSERT INTO users (username, email) VALUES ('user02', 'user02@example.com'); +INSERT INTO users (username, email) VALUES ('user03', 'user03@example.com'); +INSERT INTO users (username, email) VALUES ('user04', 'user04@example.com'); +INSERT INTO users (username, email) VALUES ('user05', 'user05@example.com'); +INSERT INTO users (username, email) VALUES ('user06', 'user06@example.com'); +INSERT INTO users (username, email) VALUES ('user07', 'user07@example.com'); +INSERT INTO users (username, email) VALUES ('user08', 'user08@example.com'); +INSERT INTO users (username, email) VALUES ('user09', 'user09@example.com'); +INSERT INTO users (username, email) VALUES ('user10', 'user10@example.com'); +INSERT INTO users (username, email) VALUES ('user11', 'user11@example.com'); +INSERT INTO users (username, email) VALUES ('user12', 'user12@example.com'); +INSERT INTO users (username, email) VALUES ('user13', 'user13@example.com'); +INSERT INTO users (username, email) VALUES ('user14', 'user14@example.com'); +INSERT INTO users (username, email) VALUES ('user15', 'user15@example.com'); +INSERT INTO users (username, email) VALUES ('user16', 'user16@example.com'); +INSERT INTO users (username, email) VALUES ('user17', 'user17@example.com'); +INSERT INTO users (username, email) VALUES ('user18', 'user18@example.com'); +INSERT INTO users (username, email) VALUES ('user19', 'user19@example.com'); +INSERT INTO users (username, email) VALUES ('user20', 'user20@example.com'); -INSERT INTO categories (name, description) VALUES - ('Electronics', 'Smartphones, laptops, accessories'), - ('Books', 'Fiction, non-fiction, educational'), - ('Clothing', 'Men, women, and children apparel'), - ('Home & Kitchen', 'Furniture, utensils, decor'), - ('Sports', 'Fitness equipment, sportswear'); -INSERT INTO products (name, description, price, stock, category_id) VALUES - ('iPhone 15', 'Latest Apple smartphone', 999.99, 50, 1), - ('Samsung Galaxy S24', 'Android flagship', 899.99, 40, 1), - ('MacBook Pro 16"', 'Powerful laptop for pros', 2499.99, 20, 1), - ('Dell XPS 13', 'Ultra portable business laptop', 1199.99, 25, 1), - ('iPad Air', 'Lightweight tablet', 599.99, 60, 1), - ('Harry Potter Set', 'Complete 7-book collection', 89.99, 100, 2), - ('The Alchemist', 'Paulo Coelho bestseller', 12.99, 200, 2), - ('Atomic Habits', 'Self-improvement guide', 14.99, 150, 2), - ('Dune', 'Sci-fi classic by Frank Herbert', 16.99, 120, 2), - ('T-Shirt Cotton White', 'Comfortable everyday wear', 19.99, 300, 3), - ('Jeans Slim Fit Blue', 'Classic denim jeans', 49.99, 150, 3), - ('Running Shoes Nike', 'Lightweight for jogging', 89.99, 80, 3), - ('Yoga Mat', 'Non-slip exercise mat', 29.99, 120, 5), - ('Dumbbell Set 10kg', 'Adjustable weight set', 199.99, 40, 5), - ('Coffee Maker', 'Automatic drip machine', 79.99, 70, 4), - ('Air Fryer', 'Healthy oil-free frying', 129.99, 50, 4), - ('Blender 1000W', 'Powerful kitchen blender', 59.99, 90, 4), - ('Desk Lamp LED', 'Adjustable brightness', 35.99, 110, 4), - ('Water Bottle 1L', 'Stainless steel insulated', 25.99, 200, 5), - ('Backpack Waterproof', 'For hiking and travel', 69.99, 80, 5); +INSERT INTO categories (name, description) VALUES ('Electronics', 'Smartphones, laptops, accessories'); +INSERT INTO categories (name, description) VALUES ('Books', 'Fiction, non-fiction, educational'); +INSERT INTO categories (name, description) VALUES ('Clothing', 'Men, women, and children apparel'); +INSERT INTO categories (name, description) VALUES ('Home & Kitchen', 'Furniture, utensils, decor'); +INSERT INTO categories (name, description) VALUES ('Sports', 'Fitness equipment, sportswear'); -INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES - (1, '123 Main St', 'Beijing', '100001', 'China', TRUE), - (2, '456 Oak Ave', 'Shanghai', '200001', 'China', TRUE), - (3, '789 Pine Rd', 'Guangzhou', '510001', 'China', FALSE), - (4, '321 Elm Blvd', 'Shenzhen', '518001', 'China', TRUE), - (5, '654 Cedar Ln', 'Hangzhou', '310001', 'China', TRUE), - (6, '987 Maple Dr', 'Chengdu', '610001', 'China', FALSE), - (7, '147 Birch St', 'Chongqing', '400001', 'China', TRUE), - (8, '258 Spruce Ave', 'Nanjing', '210001', 'China', TRUE), - (9, '369 Willow Rd', 'Wuhan', '430001', 'China', FALSE), - (10, '159 Aspen Blvd', 'Xi’an', '710001', 'China', TRUE), - (11, '357 Redwood Ln', 'Tianjin', '300001', 'China', TRUE), - (12, '753 Sequoia Dr', 'Suzhou', '215001', 'China', FALSE), - (13, '951 Fir St', 'Dalian', '116001', 'China', TRUE), - (14, '852 Cypress Ave', 'Qingdao', '266001', 'China', TRUE), - (15, '741 Palm Rd', 'Xiamen', '361001', 'China', FALSE), - (16, '630 Olive Blvd', 'Changsha', '410001', 'China', TRUE), - (17, '520 Cherry Ln', 'Zhengzhou', '450001', 'China', TRUE), - (18, '410 Peach Dr', 'Harbin', '150001', 'China', FALSE), - (19, '300 Plum St', 'Kunming', '650001', 'China', TRUE), - (20, '200 Orange Ave', 'Fuzhou', '350001', 'China', TRUE); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('iPhone 15', 'Latest Apple smartphone', 999.99, 50, 1); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Samsung Galaxy S24', 'Android flagship', 899.99, 40, 1); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('MacBook Pro 16"', 'Powerful laptop for pros', 2499.99, 20, 1); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Dell XPS 13', 'Ultra portable business laptop', 1199.99, 25, 1); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('iPad Air', 'Lightweight tablet', 599.99, 60, 1); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Harry Potter Set', 'Complete 7-book collection', 89.99, 100, 2); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('The Alchemist', 'Paulo Coelho bestseller', 12.99, 200, 2); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Atomic Habits', 'Self-improvement guide', 14.99, 150, 2); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Dune', 'Sci-fi classic by Frank Herbert', 16.99, 120, 2); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('T-Shirt Cotton White', 'Comfortable everyday wear', 19.99, 300, 3); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Jeans Slim Fit Blue', 'Classic denim jeans', 49.99, 150, 3); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Running Shoes Nike', 'Lightweight for jogging', 89.99, 80, 3); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Yoga Mat', 'Non-slip exercise mat', 29.99, 120, 5); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Dumbbell Set 10kg', 'Adjustable weight set', 199.99, 40, 5); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Coffee Maker', 'Automatic drip machine', 79.99, 70, 4); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Air Fryer', 'Healthy oil-free frying', 129.99, 50, 4); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Blender 1000W', 'Powerful kitchen blender', 59.99, 90, 4); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Desk Lamp LED', 'Adjustable brightness', 35.99, 110, 4); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Water Bottle 1L', 'Stainless steel insulated', 25.99, 200, 5); +INSERT INTO products (name, description, price, stock, category_id) VALUES ('Backpack Waterproof', 'For hiking and travel', 69.99, 80, 5); -INSERT INTO orders (user_id, total_amount, status, order_date) VALUES - (1, 1299.98, 'delivered', '2025-03-01 10:30:00'), - (2, 89.99, 'shipped', '2025-03-02 14:15:00'), - (3, 2499.99, 'paid', '2025-03-03 09:00:00'), - (4, 109.98, 'pending', '2025-03-04 16:45:00'), - (5, 199.99, 'delivered', '2025-02-28 11:20:00'), - (6, 359.97, 'shipped', '2025-03-05 13:10:00'), - (7, 79.99, 'delivered', '2025-03-01 17:50:00'), - (8, 159.98, 'paid', '2025-03-06 08:30:00'), - (9, 89.99, 'pending', '2025-03-07 12:00:00'), - (10, 299.98, 'shipped', '2025-03-08 15:30:00'), - (11, 59.99, 'delivered', '2025-03-02 10:00:00'), - (12, 189.98, 'paid', '2025-03-09 14:20:00'), - (13, 69.99, 'pending', '2025-03-10 09:45:00'), - (14, 109.98, 'shipped', '2025-03-11 16:10:00'), - (15, 2499.99, 'delivered', '2025-02-27 11:00:00'), - (16, 149.98, 'paid', '2025-03-12 13:30:00'), - (17, 129.99, 'pending', '2025-03-13 08:15:00'), - (18, 39.98, 'shipped', '2025-03-14 17:00:00'), - (19, 199.99, 'delivered', '2025-03-03 12:30:00'), - (20, 89.99, 'paid', '2025-03-15 10:45:00'); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (1, '123 Main St', 'Beijing', '100001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (2, '456 Oak Ave', 'Shanghai', '200001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (3, '789 Pine Rd', 'Guangzhou', '510001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (4, '321 Elm Blvd', 'Shenzhen', '518001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (5, '654 Cedar Ln', 'Hangzhou', '310001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (6, '987 Maple Dr', 'Chengdu', '610001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (7, '147 Birch St', 'Chongqing', '400001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (8, '258 Spruce Ave', 'Nanjing', '210001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (9, '369 Willow Rd', 'Wuhan', '430001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (10, '159 Aspen Blvd', 'Xi’an', '710001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (11, '357 Redwood Ln', 'Tianjin', '300001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (12, '753 Sequoia Dr', 'Suzhou', '215001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (13, '951 Fir St', 'Dalian', '116001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (14, '852 Cypress Ave', 'Qingdao', '266001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (15, '741 Palm Rd', 'Xiamen', '361001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (16, '630 Olive Blvd', 'Changsha', '410001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (17, '520 Cherry Ln', 'Zhengzhou', '450001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (18, '410 Peach Dr', 'Harbin', '150001', 'China', FALSE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (19, '300 Plum St', 'Kunming', '650001', 'China', TRUE); +INSERT INTO user_addresses (user_id, address_line, city, postal_code, country, is_default) VALUES (20, '200 Orange Ave', 'Fuzhou', '350001', 'China', TRUE); -INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES - (1, 1, 1, 999.99), -- iPhone - (1, 6, 3, 89.99), -- Books - (2, 6, 1, 89.99), - (3, 3, 1, 2499.99), - (4, 10, 2, 19.99), -- T-Shirts - (4, 11, 1, 49.99), -- Jeans - (5, 13, 1, 199.99), -- Dumbbell - (6, 16, 1, 129.99), -- Air Fryer - (6, 17, 1, 59.99), -- Blender - (6, 18, 2, 35.99), -- Lamps - (7, 16, 1, 79.99), - (8, 12, 1, 89.99), -- Shoes - (8, 14, 1, 29.99), -- Yoga Mat - (9, 12, 1, 89.99), - (10, 2, 1, 899.99), -- Galaxy - (10, 19, 1, 25.99), -- Bottle - (11, 17, 1, 59.99), - (12, 10, 5, 19.99), -- 5 T-Shirts - (12, 11, 2, 49.99), -- 2 Jeans - (13, 20, 1, 69.99), -- Backpack - (14, 7, 2, 12.99), -- 2x The Alchemist - (14, 8, 2, 14.99), -- 2x Atomic Habits - (15, 3, 1, 2499.99), - (16, 8, 3, 14.99), -- 3x Atomic Habits - (16, 9, 3, 16.99), -- 3x Dune - (17, 16, 1, 129.99), - (18, 18, 1, 35.99), -- Lamp - (18, 19, 1, 25.99), -- Bottle - (19, 13, 1, 199.99), - (20, 12, 1, 89.99); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (1, 1299.98, 'delivered', '2025-03-01 10:30:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (2, 89.99, 'shipped', '2025-03-02 14:15:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (3, 2499.99, 'paid', '2025-03-03 09:00:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (4, 109.98, 'pending', '2025-03-04 16:45:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (5, 199.99, 'delivered', '2025-02-28 11:20:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (6, 359.97, 'shipped', '2025-03-05 13:10:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (7, 79.99, 'delivered', '2025-03-01 17:50:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (8, 159.98, 'paid', '2025-03-06 08:30:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (9, 89.99, 'pending', '2025-03-07 12:00:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (10, 299.98, 'shipped', '2025-03-08 15:30:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (11, 59.99, 'delivered', '2025-03-02 10:00:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (12, 189.98, 'paid', '2025-03-09 14:20:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (13, 69.99, 'pending', '2025-03-10 09:45:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (14, 109.98, 'shipped', '2025-03-11 16:10:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (15, 2499.99, 'delivered', '2025-02-27 11:00:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (16, 149.98, 'paid', '2025-03-12 13:30:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (17, 129.99, 'pending', '2025-03-13 08:15:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (18, 39.98, 'shipped', '2025-03-14 17:00:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (19, 199.99, 'delivered', '2025-03-03 12:30:00'); +INSERT INTO orders (user_id, total_amount, status, order_date) VALUES (20, 89.99, 'paid', '2025-03-15 10:45:00'); -INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES - (1, 1299.98, 'Credit Card', 'completed', 'txn_001', '2025-03-01 10:35:00'), - (2, 89.99, 'Alipay', 'completed', 'txn_002', '2025-03-02 14:20:00'), - (3, 2499.99, 'WeChat Pay', 'completed', 'txn_003', '2025-03-03 09:05:00'), - (4, 109.98, 'Credit Card', 'pending', NULL, NULL), - (5, 199.99, 'Alipay', 'completed', 'txn_005', '2025-02-28 11:25:00'), - (6, 359.97, 'WeChat Pay', 'completed', 'txn_006', '2025-03-05 13:15:00'), - (7, 79.99, 'Credit Card', 'completed', 'txn_007', '2025-03-01 17:55:00'), - (8, 159.98, 'Alipay', 'completed', 'txn_008', '2025-03-06 08:35:00'), - (9, 89.99, 'WeChat Pay', 'pending', NULL, NULL), - (10, 299.98, 'Credit Card', 'completed', 'txn_010', '2025-03-08 15:35:00'), - (11, 59.99, 'Alipay', 'completed', 'txn_011', '2025-03-02 10:05:00'), - (12, 189.98, 'WeChat Pay', 'completed', 'txn_012', '2025-03-09 14:25:00'), - (13, 69.99, 'Credit Card', 'pending', NULL, NULL), - (14, 109.98, 'Alipay', 'completed', 'txn_014', '2025-03-11 16:15:00'), - (15, 2499.99, 'Credit Card', 'completed', 'txn_015', '2025-02-27 11:05:00'), - (16, 149.98, 'WeChat Pay', 'completed', 'txn_016', '2025-03-12 13:35:00'), - (17, 129.99, 'Alipay', 'pending', NULL, NULL), - (18, 39.98, 'Credit Card', 'completed', 'txn_018', '2025-03-14 17:05:00'), - (19, 199.99, 'WeChat Pay', 'completed', 'txn_019', '2025-03-03 12:35:00'), - (20, 89.99, 'Alipay', 'completed', 'txn_020', '2025-03-15 10:50:00'); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (1, 1, 1, 999.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (1, 6, 3, 89.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (2, 6, 1, 89.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (3, 3, 1, 2499.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (4, 10, 2, 19.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (4, 11, 1, 49.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (5, 13, 1, 199.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (6, 16, 1, 129.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (6, 17, 1, 59.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (6, 18, 2, 35.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (7, 16, 1, 79.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (8, 12, 1, 89.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (8, 14, 1, 29.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (9, 12, 1, 89.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (10, 2, 1, 899.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (10, 19, 1, 25.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (11, 17, 1, 59.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (12, 10, 5, 19.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (12, 11, 2, 49.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (13, 20, 1, 69.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (14, 7, 2, 12.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (14, 8, 2, 14.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (15, 3, 1, 2499.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (16, 8, 3, 14.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (16, 9, 3, 16.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (17, 16, 1, 129.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (18, 18, 1, 35.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (18, 19, 1, 25.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (19, 13, 1, 199.99); +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (20, 12, 1, 89.99); -INSERT INTO reviews (user_id, product_id, rating, comment) VALUES - (1, 1, 5, 'Amazing phone, worth every penny!'), - (2, 6, 4, 'Great book set for Harry Potter fans.'), - (3, 3, 5, 'Best laptop I have ever used.'), - (4, 10, 3, 'Comfortable but runs a bit small.'), - (5, 13, 4, 'Good quality dumbbells, solid build.'), - (6, 16, 5, 'Love my air fryer, cooks perfectly.'), - (7, 16, 4, 'Easy to use, cleans up nicely.'), - (8, 12, 5, 'Perfect for my morning runs.'), - (9, 12, 4, 'Good grip and cushioning.'), - (10, 2, 4, 'Great Android phone, battery life is good.'), - (11, 17, 3, 'Blender is okay, a bit noisy.'), - (12, 10, 5, 'Bought 5 for my team, all love them!'), - (13, 20, 4, 'Spacious and waterproof, great for travel.'), - (14, 7, 5, 'Life-changing book, highly recommend.'), - (15, 3, 5, 'Worth the investment for professionals.'), - (16, 8, 5, 'Helped me build better habits.'), - (17, 16, 4, 'Heats up fast, easy controls.'), - (18, 18, 3, 'Lamp is functional but design is plain.'), - (19, 13, 5, 'Perfect for home workouts.'), - (20, 12, 4, 'Comfortable for long walks too.'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (1, 1299.98, 'Credit Card', 'completed', 'txn_001', '2025-03-01 10:35:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (2, 89.99, 'Alipay', 'completed', 'txn_002', '2025-03-02 14:20:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (3, 2499.99, 'WeChat Pay', 'completed', 'txn_003', '2025-03-03 09:05:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (4, 109.98, 'Credit Card', 'pending', NULL, NULL); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (5, 199.99, 'Alipay', 'completed', 'txn_005', '2025-02-28 11:25:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (6, 359.97, 'WeChat Pay', 'completed', 'txn_006', '2025-03-05 13:15:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (7, 79.99, 'Credit Card', 'completed', 'txn_007', '2025-03-01 17:55:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (8, 159.98, 'Alipay', 'completed', 'txn_008', '2025-03-06 08:35:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (9, 89.99, 'WeChat Pay', 'pending', NULL, NULL); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (10, 299.98, 'Credit Card', 'completed', 'txn_010', '2025-03-08 15:35:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (11, 59.99, 'Alipay', 'completed', 'txn_011', '2025-03-02 10:05:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (12, 189.98, 'WeChat Pay', 'completed', 'txn_012', '2025-03-09 14:25:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (13, 69.99, 'Credit Card', 'pending', NULL, NULL); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (14, 109.98, 'Alipay', 'completed', 'txn_014', '2025-03-11 16:15:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (15, 2499.99, 'Credit Card', 'completed', 'txn_015', '2025-02-27 11:05:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (16, 149.98, 'WeChat Pay', 'completed', 'txn_016', '2025-03-12 13:35:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (17, 129.99, 'Alipay', 'pending', NULL, NULL); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (18, 39.98, 'Credit Card', 'completed', 'txn_018', '2025-03-14 17:05:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (19, 199.99, 'WeChat Pay', 'completed', 'txn_019', '2025-03-03 12:35:00'); +INSERT INTO payments (order_id, amount, payment_method, payment_status, transaction_id, paid_at) VALUES (20, 89.99, 'Alipay', 'completed', 'txn_020', '2025-03-15 10:50:00'); -INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES - ('WELCOME10', 10.00, '2025-01-01', '2025-12-31', TRUE), - ('SPRING20', 20.00, '2025-03-01', '2025-04-30', TRUE), - ('SUMMER25', 25.00, '2025-06-01', '2025-08-31', FALSE), - ('HOLIDAY30', 30.00, '2025-12-01', '2025-12-31', FALSE), - ('FLASH5', 5.00, '2025-03-15', '2025-03-16', TRUE), - ('VIP15', 15.00, '2025-01-01', '2025-12-31', TRUE), - ('NEWUSER10', 10.00, '2025-01-01', '2025-12-31', TRUE), - ('ELECTRO15', 15.00, '2025-03-01', '2025-03-31', TRUE), - ('BOOKS20', 20.00, '2025-03-01', '2025-03-31', TRUE), - ('FASHION10', 10.00, '2025-03-01', '2025-04-15', TRUE), - ('FITNESS25', 25.00, '2025-05-01', '2025-06-30', FALSE), - ('KITCHEN15', 15.00, '2025-03-01', '2025-04-30', TRUE), - ('BACKTOSCHOOL', 12.00, '2025-08-01', '2025-09-15', FALSE), - ('LOYALTY5', 5.00, '2025-01-01', '2025-12-31', TRUE), - ('FREESHIP', 0.00, '2025-03-01', '2025-03-31', TRUE); -- Free shipping coupon +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('WELCOME10', 10.00, '2025-01-01', '2025-12-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('SPRING20', 20.00, '2025-03-01', '2025-04-30', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('SUMMER25', 25.00, '2025-06-01', '2025-08-31', FALSE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('HOLIDAY30', 30.00, '2025-12-01', '2025-12-31', FALSE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('FLASH5', 5.00, '2025-03-15', '2025-03-16', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('VIP15', 15.00, '2025-01-01', '2025-12-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('NEWUSER10', 10.00, '2025-01-01', '2025-12-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('ELECTRO15', 15.00, '2025-03-01', '2025-03-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('BOOKS20', 20.00, '2025-03-01', '2025-03-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('FASHION10', 10.00, '2025-03-01', '2025-04-15', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('FITNESS25', 25.00, '2025-05-01', '2025-06-30', FALSE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('KITCHEN15', 15.00, '2025-03-01', '2025-04-30', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('BACKTOSCHOOL', 12.00, '2025-08-01', '2025-09-15', FALSE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('LOYALTY5', 5.00, '2025-01-01', '2025-12-31', TRUE); +INSERT INTO coupons (code, discount_percent, valid_from, valid_until, is_active) VALUES ('FREESHIP', 0.00, '2025-03-01', '2025-03-31', TRUE); -INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES - (1, 1, TRUE, '2025-03-01 10:30:00'), - (2, 1, TRUE, '2025-03-02 14:15:00'), - (3, 2, FALSE, NULL), - (4, 3, FALSE, NULL), - (5, 4, FALSE, NULL), - (6, 5, TRUE, '2025-03-05 13:10:00'), - (7, 6, TRUE, '2025-03-01 17:50:00'), - (8, 7, TRUE, '2025-03-06 08:30:00'), - (9, 8, FALSE, NULL), - (10, 9, TRUE, '2025-03-08 15:30:00'), - (11, 10, TRUE, '2025-03-02 10:00:00'), - (12, 11, FALSE, NULL), - (13, 12, FALSE, NULL), - (14, 13, FALSE, NULL), - (15, 14, TRUE, '2025-02-27 11:00:00'), - (16, 15, TRUE, '2025-03-12 13:30:00'), - (17, 1, TRUE, '2025-03-13 08:15:00'), - (18, 2, FALSE, NULL), - (19, 3, FALSE, NULL), - (20, 4, FALSE, NULL); \ No newline at end of file +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (1, 1, TRUE, '2025-03-01 10:30:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (2, 1, TRUE, '2025-03-02 14:15:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (3, 2, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (4, 3, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (5, 4, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (6, 5, TRUE, '2025-03-05 13:10:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (7, 6, TRUE, '2025-03-01 17:50:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (8, 7, TRUE, '2025-03-06 08:30:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (9, 8, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (10, 9, TRUE, '2025-03-08 15:30:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (11, 10, TRUE, '2025-03-02 10:00:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (12, 11, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (13, 12, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (14, 13, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (15, 14, TRUE, '2025-02-27 11:00:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (16, 15, TRUE, '2025-03-12 13:30:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (17, 1, TRUE, '2025-03-13 08:15:00'); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (18, 2, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (19, 3, FALSE, NULL); +INSERT INTO user_coupons (user_id, coupon_id, used, used_at) VALUES (20, 4, FALSE, NULL); \ No newline at end of file