Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ public final void enableComputeSqlHeuristicsOrExtractExecution(
*/
public final void initSqlHandler() {
sqlHandler.setConnection(getConnectionIfExist());
sqlHandler.setHarvestConnection(getHarvestConnectionIfExist());
sqlHandler.setSchema(getSqlDatabaseSchema());
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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.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{
DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection());
List<TableDto> tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto);
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)
);
}
@Test
public void testGenerateSelectSqls() throws Exception{
DbInfoDto dbInfoDto = DbInfoExtractor.extract(getConnection());
List<TableDto> tables = SqlDbHarvester.sortTablesByDependency(dbInfoDto);
Map<String, String> selects = SqlDbHarvester.generateSelectSqls(tables, 3);
List<String> 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<String, String> entry : selects.entrySet()) {
// QueryResult qr = SqlScriptRunner.execCommand(getConnection(), entry.getValue());
// }
}

@Override
public Connection getConnection() {
return connection;
}

@Override
public SutController getSutController() {
return new DatabaseFakeMySQLSutController(connection);
}
}
104 changes: 104 additions & 0 deletions client-java/controller/src/test/resources/db_schemas/order.sql
Original file line number Diff line number Diff line change
@@ -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)
);
Loading
Loading