diff --git a/openespi-common/pom.xml b/openespi-common/pom.xml
index 5243db13..06948dcf 100644
--- a/openespi-common/pom.xml
+++ b/openespi-common/pom.xml
@@ -411,7 +411,7 @@
com.mysql
mysql-connector-j
- 9.1.0
+ 8.4.0
test
@@ -517,31 +517,6 @@
-
-
- org.testcontainers
- junit-jupiter
- ${testcontainers.version}
- test
-
-
- org.testcontainers
- mysql
- ${testcontainers.version}
- test
-
-
- org.testcontainers
- postgresql
- ${testcontainers.version}
- test
-
-
- org.testcontainers
- testcontainers
- ${testcontainers.version}
- test
-
@@ -633,12 +608,12 @@
org.apache.maven.plugins
maven-failsafe-plugin
-
-
-
-
-
-
+
+
+ **/*IntegrationTest.java
+ **/*IT.java
+
+
diff --git a/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql b/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
index 7fcc2bc4..19ba3e16 100644
--- a/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
+++ b/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
@@ -33,8 +33,6 @@ CREATE INDEX idx_identified_object_related_links ON identified_object_related_li
CREATE TABLE application_information
(
id CHAR(36) PRIMARY KEY,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -47,10 +45,7 @@ CREATE TABLE application_information
self_link_type VARCHAR(255),
-- Application specific fields
- kind VARCHAR(255),
data_custodian_application_status VARCHAR(255),
- data_custodian_default_batch_resource TEXT,
- data_custodian_default_subscription_resource TEXT,
client_name VARCHAR(255),
client_id VARCHAR(255) NOT NULL UNIQUE,
client_secret VARCHAR(255),
@@ -74,10 +69,7 @@ CREATE TABLE application_information
authorization_server_registration_endpoint TEXT,
authorization_server_token_endpoint TEXT,
data_custodian_bulk_request_uri TEXT,
- data_custodian_third_party_selection_screen_uri TEXT,
data_custodian_resource_endpoint TEXT,
- third_party_data_custodian_selection_screen_uri TEXT,
- third_party_login_screen_uri TEXT,
third_party_scope_selection_screen_uri TEXT,
third_party_user_portal_screen_uri TEXT,
logo_uri TEXT,
@@ -89,7 +81,6 @@ CREATE TABLE application_information
token_endpoint_auth_method VARCHAR(50),
data_custodian_scope_selection_screen_uri TEXT,
data_custodian_id VARCHAR(64),
- third_party_application_name VARCHAR(64) NOT NULL DEFAULT 'Default Third Party Application Name',
response_types VARCHAR(255),
grant_types VARCHAR(255),
application_type VARCHAR(50),
@@ -150,9 +141,6 @@ CREATE INDEX idx_app_info_scopes ON application_information_scopes (application_
CREATE TABLE retail_customers
(
id CHAR(36) PRIMARY KEY,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -179,7 +167,6 @@ CREATE TABLE retail_customers
failed_login_attempts INTEGER DEFAULT 0
);
-CREATE INDEX idx_retail_customer_uuid ON retail_customers (uuid);
CREATE INDEX idx_retail_customer_username ON retail_customers (username);
CREATE INDEX idx_retail_customer_created ON retail_customers (created);
CREATE INDEX idx_retail_customer_updated ON retail_customers (updated);
@@ -198,9 +185,6 @@ CREATE INDEX idx_retail_customer_related_links ON retail_customer_related_links
CREATE TABLE service_delivery_points
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -219,7 +203,6 @@ CREATE TABLE service_delivery_points
sdp_customer_agreement VARCHAR(256)
);
-CREATE INDEX idx_sdp_uuid ON service_delivery_points (uuid);
CREATE INDEX idx_sdp_name ON service_delivery_points (sdp_name);
CREATE INDEX idx_sdp_tariff_profile ON service_delivery_points (sdp_tariff_profile);
CREATE INDEX idx_sdp_customer_agreement ON service_delivery_points (sdp_customer_agreement);
@@ -240,8 +223,6 @@ CREATE INDEX idx_sdp_related_links ON service_delivery_point_related_links (serv
CREATE TABLE authorizations
(
id CHAR(36) PRIMARY KEY ,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -308,9 +289,6 @@ CREATE INDEX idx_authorization_related_links ON authorization_related_links (aut
CREATE TABLE reading_types
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -345,7 +323,6 @@ CREATE TABLE reading_types
interharmonic_denominator BIGINT
);
-CREATE INDEX idx_reading_type_uuid ON reading_types (uuid);
CREATE INDEX idx_reading_type_kind ON reading_types (kind);
CREATE INDEX idx_reading_type_commodity ON reading_types (commodity);
CREATE INDEX idx_reading_type_uom ON reading_types (uom);
@@ -366,9 +343,6 @@ CREATE INDEX idx_reading_type_related_links ON reading_type_related_links (readi
CREATE TABLE subscriptions
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -394,7 +368,6 @@ CREATE TABLE subscriptions
FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE
);
-CREATE INDEX idx_subscription_uuid ON subscriptions (uuid);
CREATE INDEX idx_subscription_app_id ON subscriptions (application_information_id);
CREATE INDEX idx_subscription_customer_id ON subscriptions (retail_customer_id);
CREATE INDEX idx_subscription_last_update ON subscriptions (last_update);
@@ -415,9 +388,6 @@ CREATE INDEX idx_subscription_related_links ON subscription_related_links (subsc
CREATE TABLE batch_lists
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -433,7 +403,6 @@ CREATE TABLE batch_lists
resource_count INT DEFAULT 0
);
-CREATE INDEX idx_batch_list_uuid ON batch_lists (uuid);
CREATE INDEX idx_batch_list_created ON batch_lists (created);
CREATE INDEX idx_batch_list_resource_count ON batch_lists (resource_count);
CREATE INDEX idx_batch_list_updated ON batch_lists (updated);
diff --git a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql
index 6f0620d0..9ad5cae7 100644
--- a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql
+++ b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql
@@ -2,9 +2,6 @@
CREATE TABLE meter_readings
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -25,7 +22,6 @@ CREATE TABLE meter_readings
);
-- Indexes for meter_readings table
-CREATE INDEX idx_meter_reading_uuid ON meter_readings (uuid);
CREATE INDEX idx_meter_reading_usage_point_id ON meter_readings (usage_point_id);
CREATE INDEX idx_meter_reading_reading_type_id ON meter_readings (reading_type_id);
CREATE INDEX idx_meter_reading_created ON meter_readings (created);
@@ -46,9 +42,6 @@ CREATE INDEX idx_meter_reading_related_links ON meter_reading_related_links (met
CREATE TABLE interval_blocks
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -71,7 +64,6 @@ CREATE TABLE interval_blocks
);
-- Indexes for interval_blocks table
-CREATE INDEX idx_interval_block_uuid ON interval_blocks (uuid);
CREATE INDEX idx_interval_block_meter_reading_id ON interval_blocks (meter_reading_id);
CREATE INDEX idx_interval_block_start ON interval_blocks (interval_start);
CREATE INDEX idx_interval_block_created ON interval_blocks (created);
@@ -92,9 +84,6 @@ CREATE INDEX idx_interval_block_related_links ON interval_block_related_links (i
CREATE TABLE interval_readings
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -122,7 +111,6 @@ CREATE TABLE interval_readings
);
-- Indexes for interval_readings table
-CREATE INDEX idx_interval_reading_uuid ON interval_readings (uuid);
CREATE INDEX idx_interval_reading_interval_block_id ON interval_readings (interval_block_id);
CREATE INDEX idx_interval_reading_time_period_start ON interval_readings (time_period_start);
CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value);
@@ -145,9 +133,6 @@ CREATE INDEX idx_interval_reading_related_links ON interval_reading_related_link
CREATE TABLE reading_qualities
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -169,7 +154,6 @@ CREATE TABLE reading_qualities
);
-- Indexes for reading_qualities table
-CREATE INDEX idx_reading_quality_uuid ON reading_qualities (uuid);
CREATE INDEX idx_reading_quality_interval_reading_id ON reading_qualities (interval_reading_id);
CREATE INDEX idx_reading_quality_quality ON reading_qualities (quality);
CREATE INDEX idx_reading_quality_created ON reading_qualities (created);
@@ -190,9 +174,6 @@ CREATE INDEX idx_reading_quality_related_links ON reading_quality_related_links
CREATE TABLE usage_summaries
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -297,7 +278,6 @@ CREATE TABLE usage_summaries
);
-- Indexes for usage_summaries table
-CREATE INDEX idx_usage_summary_uuid ON usage_summaries (uuid);
CREATE INDEX idx_usage_summary_usage_point_id ON usage_summaries (usage_point_id);
CREATE INDEX idx_usage_summary_billing_period_start ON usage_summaries (billing_period_start);
CREATE INDEX idx_usage_summary_created ON usage_summaries (created);
@@ -341,9 +321,6 @@ ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_subscription
CREATE TABLE pnode_refs
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -368,7 +345,6 @@ CREATE TABLE pnode_refs
);
-- Indexes for pnode_refs table
-CREATE INDEX idx_pnode_ref_uuid ON pnode_refs (uuid);
CREATE INDEX idx_pnode_ref_apnode_type ON pnode_refs (apnode_type);
CREATE INDEX idx_pnode_ref_ref ON pnode_refs (ref);
CREATE INDEX idx_pnode_ref_usage_point_id ON pnode_refs (usage_point_id);
@@ -390,9 +366,6 @@ CREATE INDEX idx_pnode_ref_related_links ON pnode_ref_related_links (pnode_ref_i
CREATE TABLE aggregated_node_refs
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -419,7 +392,6 @@ CREATE TABLE aggregated_node_refs
);
-- Indexes for aggregated_node_refs table
-CREATE INDEX idx_aggregated_node_ref_uuid ON aggregated_node_refs (uuid);
CREATE INDEX idx_aggregated_node_ref_anode_type ON aggregated_node_refs (anode_type);
CREATE INDEX idx_aggregated_node_ref_ref ON aggregated_node_refs (ref);
CREATE INDEX idx_aggregated_node_ref_pnode_ref_id ON aggregated_node_refs (pnode_ref_id);
@@ -442,8 +414,6 @@ CREATE INDEX idx_aggregated_node_ref_related_links ON aggregated_node_ref_relate
CREATE TABLE customers
(
id CHAR(36) PRIMARY KEY ,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -510,9 +480,6 @@ CREATE INDEX idx_customer_updated ON customers (updated);
CREATE TABLE customer_agreements
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -546,7 +513,6 @@ CREATE TABLE customer_agreements
);
-- Indexes for customer_agreements table
-CREATE INDEX idx_customer_agreement_uuid ON customer_agreements (uuid);
CREATE INDEX idx_customer_agreement_sign_date ON customer_agreements (sign_date);
CREATE INDEX idx_customer_agreement_created ON customer_agreements (created);
CREATE INDEX idx_customer_agreement_updated ON customer_agreements (updated);
@@ -579,9 +545,6 @@ CREATE INDEX idx_customer_agreement_future_status ON customer_agreement_future_s
CREATE TABLE customer_accounts
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -617,7 +580,6 @@ CREATE TABLE customer_accounts
FOREIGN KEY (customer_id) REFERENCES customers (id) ON DELETE CASCADE
);
-CREATE INDEX idx_customer_account_uuid ON customer_accounts (uuid);
CREATE INDEX idx_customer_account_number ON customer_accounts (account_number);
CREATE INDEX idx_customer_account_kind ON customer_accounts (account_kind);
CREATE INDEX idx_customer_account_customer_id ON customer_accounts (customer_id);
@@ -642,9 +604,6 @@ CREATE INDEX idx_customer_account_notifications ON customer_account_notification
CREATE TABLE electric_power_quality_summaries
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -681,10 +640,9 @@ CREATE TABLE electric_power_quality_summaries
FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE
);
-CREATE INDEX idx_epqs_uuid ON electric_power_quality_summaries (uuid);
CREATE INDEX idx_epqs_usage_point_id ON electric_power_quality_summaries (usage_point_id);
-CREATE INDEX idx_epqs_summary_interval_start ON electric_power_quality_summaries (uuid);
-CREATE INDEX idx_epqs_created ON electric_power_quality_summaries (summary_interval_start);
+CREATE INDEX idx_epqs_summary_interval_start ON electric_power_quality_summaries (id);
+CREATE INDEX idx_epqs_created ON electric_power_quality_summaries (created);
CREATE INDEX idx_epqs_updated ON electric_power_quality_summaries (updated);
@@ -692,9 +650,6 @@ CREATE INDEX idx_epqs_updated ON electric_power_quality_summaries (updated);
CREATE TABLE end_devices
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -737,7 +692,6 @@ CREATE TABLE end_devices
amr_system VARCHAR(100)
);
-CREATE INDEX idx_end_device_uuid ON end_devices (uuid);
CREATE INDEX idx_end_device_type ON end_devices (type);
CREATE INDEX idx_end_device_serial_number ON end_devices (serial_number);
CREATE INDEX idx_end_device_status ON end_devices (status_value);
@@ -759,9 +713,6 @@ CREATE INDEX idx_end_device_related_links ON end_device_related_links (end_devic
CREATE TABLE line_items
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -785,7 +736,6 @@ CREATE TABLE line_items
FOREIGN KEY (usage_summary_id) REFERENCES usage_summaries (id) ON DELETE CASCADE
);
-CREATE INDEX idx_line_item_uuid ON line_items (uuid);
CREATE INDEX idx_line_item_usage_summary ON line_items (usage_summary_id);
CREATE INDEX idx_line_item_date_time ON line_items (date_time);
CREATE INDEX idx_line_item_amount ON line_items (amount);
@@ -817,9 +767,6 @@ CREATE INDEX idx_meters_form_number ON meters (form_number);
CREATE TABLE phone_numbers
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -843,11 +790,9 @@ CREATE TABLE phone_numbers
phone_type VARCHAR(20),
-- Polymorphic relationship fields
- parent_entity_uuid VARCHAR(36),
parent_entity_type VARCHAR(255)
);
-CREATE INDEX idx_phone_number_uuid ON phone_numbers (uuid);
CREATE INDEX idx_phone_number_itu_phone ON phone_numbers (itu_phone);
CREATE INDEX idx_phone_number_created ON phone_numbers (created);
CREATE INDEX idx_phone_number_updated ON phone_numbers (updated);
@@ -868,9 +813,6 @@ CREATE INDEX idx_phone_number_related_links ON phone_number_related_links (phone
CREATE TABLE program_date_id_mappings
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -887,7 +829,6 @@ CREATE TABLE program_date_id_mappings
program_id VARCHAR(100)
);
-CREATE INDEX idx_program_date_id_mapping_uuid ON program_date_id_mappings (uuid);
CREATE INDEX idx_program_date_id_mapping_program_date ON program_date_id_mappings (program_date);
CREATE INDEX idx_program_date_id_mapping_program_id ON program_date_id_mappings (program_id);
CREATE INDEX idx_program_date_id_mapping_created ON program_date_id_mappings (created);
@@ -908,9 +849,6 @@ CREATE INDEX idx_program_date_id_mapping_related_links ON program_date_id_mappin
CREATE TABLE service_locations
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -959,7 +897,6 @@ CREATE TABLE service_locations
outage_block VARCHAR(32)
);
-CREATE INDEX idx_service_location_uuid ON service_locations (uuid);
CREATE INDEX idx_service_location_access_method ON service_locations (access_method);
CREATE INDEX idx_service_location_needs_inspection ON service_locations (needs_inspection);
CREATE INDEX idx_service_location_created ON service_locations (created);
@@ -981,9 +918,6 @@ CREATE INDEX idx_service_location_related_links ON service_location_related_link
CREATE TABLE service_suppliers
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -1018,7 +952,6 @@ CREATE TABLE service_suppliers
supplier_radio VARCHAR(255)
);
-CREATE INDEX idx_service_supplier_uuid ON service_suppliers (uuid);
CREATE INDEX idx_service_supplier_kind ON service_suppliers (kind);
CREATE INDEX idx_service_supplier_issuer_id ON service_suppliers (issuer_identification_number);
CREATE INDEX idx_service_supplier_created ON service_suppliers (created);
@@ -1040,9 +973,6 @@ CREATE INDEX idx_service_supplier_related_links ON service_supplier_related_link
CREATE TABLE statements
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -1063,7 +993,6 @@ CREATE TABLE statements
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
-CREATE INDEX idx_statement_uuid ON statements (uuid);
CREATE INDEX idx_statement_issue_date_time ON statements (issue_date_time);
CREATE INDEX idx_statement_customer_id ON statements (customer_id);
CREATE INDEX idx_statement_statement_date ON statements (statement_date);
@@ -1085,9 +1014,6 @@ CREATE INDEX idx_statement_related_links ON statement_related_links (statement_i
CREATE TABLE statement_refs
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL,
@@ -1107,7 +1033,6 @@ CREATE TABLE statement_refs
FOREIGN KEY (statement_id) REFERENCES statements(id)
);
-CREATE INDEX idx_statement_ref_uuid ON statement_refs (uuid);
CREATE INDEX idx_statement_ref_statement_id ON statement_refs (statement_id);
CREATE INDEX idx_statement_ref_created ON statement_refs (created);
CREATE INDEX idx_statement_ref_updated ON statement_refs (updated);
diff --git a/openespi-common/src/main/resources/db/migration/V4__Drop_ApplicationInformation_Extension_Columns.sql b/openespi-common/src/main/resources/db/migration/V4__Drop_ApplicationInformation_Extension_Columns.sql
deleted file mode 100644
index 5f3d5143..00000000
--- a/openespi-common/src/main/resources/db/migration/V4__Drop_ApplicationInformation_Extension_Columns.sql
+++ /dev/null
@@ -1,42 +0,0 @@
--- ==========================================================================================================
--- V4__Drop_ApplicationInformation_Extension_Columns.sql
---
--- Purpose: Remove extension columns from application_information table that are not in ESPI 4.0 XSD schema.
--- This migration aligns the database schema with the espi.xsd ApplicationInformation definition.
---
--- BREAKING CHANGE WARNING:
--- This migration PERMANENTLY deletes columns and data. Applications using these fields will break.
--- Extension fields removed:
--- 1. kind
--- 2. data_custodian_default_batch_resource
--- 3. data_custodian_default_subscription_resource
--- 4. data_custodian_third_party_selection_screen_uri
--- 5. third_party_data_custodian_selection_screen_uri
--- 6. third_party_login_screen_uri
--- 7. third_party_application_name
---
--- Related: ApplicationInformationEntity.java and ApplicationInformationDto.java have been updated to
--- match XSD field sequence and remove extension fields.
---
--- Author: Claude Code (feature/fix-ApplicationInformation-structure)
--- Date: 2025-12-17
--- ==========================================================================================================
-
--- Drop extension columns from application_information table
--- Each column is dropped in a separate ALTER TABLE statement for H2 compatibility
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS kind;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS data_custodian_default_batch_resource;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS data_custodian_default_subscription_resource;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS data_custodian_third_party_selection_screen_uri;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS third_party_data_custodian_selection_screen_uri;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS third_party_login_screen_uri;
-
-ALTER TABLE application_information DROP COLUMN IF EXISTS third_party_application_name;
-
--- Note: All remaining columns match ESPI 4.0 XSD schema ApplicationInformation sequence (espi.xsd lines 62-246)
\ No newline at end of file
diff --git a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
index ca54edcf..89c69264 100644
--- a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
@@ -34,9 +34,6 @@
CREATE TABLE time_configurations
(
id UUID PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
@@ -56,7 +53,6 @@ CREATE TABLE time_configurations
);
-- Create indexes for time_configurations table
-CREATE INDEX idx_time_config_uuid ON time_configurations (uuid);
CREATE INDEX idx_time_config_created ON time_configurations (created);
CREATE INDEX idx_time_config_updated ON time_configurations (updated);
@@ -75,9 +71,6 @@ CREATE INDEX idx_time_config_related_links ON time_configuration_related_links (
CREATE TABLE usage_points
(
id UUID PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
@@ -108,7 +101,6 @@ CREATE TABLE usage_points
);
-- Create indexes for usage_points table
-CREATE INDEX idx_usage_point_uuid ON usage_points (uuid);
CREATE INDEX idx_usage_point_kind ON usage_points (kind);
CREATE INDEX idx_usage_point_status ON usage_points (status);
CREATE INDEX idx_usage_point_customer_id ON usage_points (retail_customer_id);
diff --git a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
index bd2fbd4b..10441e03 100644
--- a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
@@ -34,9 +34,6 @@
CREATE TABLE time_configurations
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
@@ -54,7 +51,6 @@ CREATE TABLE time_configurations
dst_start_rule BLOB,
tz_offset BIGINT,
- INDEX idx_time_config_uuid (uuid),
INDEX idx_time_config_created (created),
INDEX idx_time_config_updated (updated)
);
@@ -72,9 +68,6 @@ CREATE TABLE time_configuration_related_links
CREATE TABLE usage_points
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
@@ -132,7 +125,6 @@ CREATE TABLE usage_points
FOREIGN KEY (service_delivery_point_id) REFERENCES service_delivery_points (id) ON DELETE SET NULL,
FOREIGN KEY (local_time_parameters_id) REFERENCES time_configurations (id) ON DELETE SET NULL,
- INDEX idx_usage_point_uuid (uuid),
INDEX idx_usage_point_kind (kind),
INDEX idx_usage_point_status (status),
INDEX idx_usage_point_customer_id (retail_customer_id),
diff --git a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
index f7f40bde..9670192b 100644
--- a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
@@ -34,9 +34,6 @@
CREATE TABLE time_configurations
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -55,7 +52,6 @@ CREATE TABLE time_configurations
tz_offset BIGINT
);
-CREATE INDEX idx_time_config_uuid ON time_configurations (uuid);
CREATE INDEX idx_time_config_created ON time_configurations (created);
CREATE INDEX idx_time_config_updated ON time_configurations (updated);
@@ -73,9 +69,6 @@ CREATE INDEX idx_time_config_related_links ON time_configuration_related_links (
CREATE TABLE usage_points
(
id CHAR(36) PRIMARY KEY ,
- uuid VARCHAR(36) NOT NULL UNIQUE,
- uuid_msb BIGINT,
- uuid_lsb BIGINT,
description VARCHAR(255),
created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -134,7 +127,6 @@ CREATE TABLE usage_points
FOREIGN KEY (local_time_parameters_id) REFERENCES time_configurations (id) ON DELETE SET NULL
);
-CREATE INDEX idx_usage_point_uuid ON usage_points (uuid);
CREATE INDEX idx_usage_point_kind ON usage_points (kind);
CREATE INDEX idx_usage_point_status ON usage_points (status);
CREATE INDEX idx_usage_point_customer_id ON usage_points (retail_customer_id);
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipMySQLIntegrationTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipMySQLIntegrationTest.java
new file mode 100644
index 00000000..3e518532
--- /dev/null
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipMySQLIntegrationTest.java
@@ -0,0 +1,276 @@
+/*
+ *
+ * Copyright (c) 2025 Green Button Alliance, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.greenbuttonalliance.espi.common.repositories.integration;
+
+import org.greenbuttonalliance.espi.common.domain.customer.entity.CustomerEntity;
+import org.greenbuttonalliance.espi.common.domain.customer.entity.StatementEntity;
+import org.greenbuttonalliance.espi.common.domain.usage.*;
+import org.greenbuttonalliance.espi.common.repositories.customer.CustomerRepository;
+import org.greenbuttonalliance.espi.common.repositories.customer.StatementRepository;
+import org.greenbuttonalliance.espi.common.repositories.usage.*;
+import org.greenbuttonalliance.espi.common.test.BaseTestContainersTest;
+import org.greenbuttonalliance.espi.common.test.TestDataBuilders;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.transaction.annotation.Transactional;
+import org.testcontainers.junit.jupiter.Container;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * Complex relationship integration tests for JPA entities using MySQL TestContainer.
+ *
+ * Tests basic relationship operations and entity hierarchies using
+ * available repositories and methods with a real MySQL database.
+ */
+@DisplayName("Complex Relationship Integration Tests - MySQL")
+@ActiveProfiles({"test", "test-mysql"})
+class ComplexRelationshipMySQLIntegrationTest extends BaseTestContainersTest {
+
+ @Container
+ private static final org.testcontainers.containers.MySQLContainer> mysql = mysqlContainer;
+
+ static {
+ mysql.start();
+ }
+
+ @DynamicPropertySource
+ static void configureMySQLProperties(DynamicPropertyRegistry registry) {
+ registry.add("spring.datasource.url", mysql::getJdbcUrl);
+ registry.add("spring.datasource.username", mysql::getUsername);
+ registry.add("spring.datasource.password", mysql::getPassword);
+ registry.add("spring.datasource.driver-class-name", () -> "com.mysql.cj.jdbc.Driver");
+ }
+
+ @Autowired
+ private CustomerRepository customerRepository;
+
+ @Autowired
+ private StatementRepository statementRepository;
+
+ @Autowired
+ private UsagePointRepository usagePointRepository;
+
+ @Autowired
+ private MeterReadingRepository meterReadingRepository;
+
+ @Autowired
+ private IntervalBlockRepository intervalBlockRepository;
+
+ @Autowired
+ private RetailCustomerRepository retailCustomerRepository;
+
+ @Nested
+ @DisplayName("Basic Relationship Operations")
+ class BasicRelationshipTest {
+
+ @Test
+ @DisplayName("Should handle Customer → Statement relationships")
+ void shouldHandleCustomerStatementRelationships() {
+ // Arrange
+ CustomerEntity customer = TestDataBuilders.createValidCustomer();
+ customer.setCustomerName("MySQL Relationship Test Customer");
+ CustomerEntity savedCustomer = customerRepository.save(customer);
+
+ StatementEntity statement = TestDataBuilders.createValidStatement();
+ statement.setCustomer(savedCustomer);
+ statement.setDescription("MySQL Relationship Test Statement");
+ StatementEntity savedStatement = statementRepository.save(statement);
+
+ flushAndClear();
+
+ // Act - Retrieve customer and statement
+ Optional retrievedCustomer = customerRepository.findById(savedCustomer.getId());
+ Optional retrievedStatement = statementRepository.findById(savedStatement.getId());
+
+ // Assert
+ assertThat(retrievedCustomer).isPresent();
+ assertThat(retrievedStatement).isPresent();
+ assertThat(retrievedStatement.get().getCustomer()).isNotNull();
+ assertThat(retrievedStatement.get().getCustomer().getId()).isEqualTo(savedCustomer.getId());
+ }
+
+ @Test
+ @DisplayName("Should handle UsagePoint → MeterReading → IntervalBlock hierarchy")
+ void shouldHandleUsagePointHierarchy() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("MySQL Hierarchy Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReading();
+ meterReading.setUsagePoint(savedUsagePoint);
+ meterReading.setDescription("MySQL Hierarchy Test Meter Reading");
+ MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
+
+ IntervalBlockEntity intervalBlock = TestDataBuilders.createValidIntervalBlock();
+ intervalBlock.setMeterReading(savedMeterReading);
+ intervalBlock.setDescription("MySQL Hierarchy Test Interval Block");
+ IntervalBlockEntity savedIntervalBlock = intervalBlockRepository.save(intervalBlock);
+
+ flushAndClear();
+
+ // Act - Retrieve the hierarchy
+ Optional retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId());
+ Optional retrievedMeterReading = meterReadingRepository.findById(savedMeterReading.getId());
+ Optional retrievedIntervalBlock = intervalBlockRepository.findById(savedIntervalBlock.getId());
+
+ // Assert
+ assertThat(retrievedUsagePoint).isPresent();
+ assertThat(retrievedMeterReading).isPresent();
+ assertThat(retrievedIntervalBlock).isPresent();
+
+ assertThat(retrievedMeterReading.get().getUsagePoint()).isNotNull();
+ assertThat(retrievedMeterReading.get().getUsagePoint().getId()).isEqualTo(savedUsagePoint.getId());
+
+ assertThat(retrievedIntervalBlock.get().getMeterReading()).isNotNull();
+ assertThat(retrievedIntervalBlock.get().getMeterReading().getId()).isEqualTo(savedMeterReading.getId());
+ }
+
+ @Test
+ @DisplayName("Should handle RetailCustomer → UsagePoint relationships")
+ void shouldHandleRetailCustomerUsagePointRelationships() {
+ // Arrange
+ RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
+ retailCustomer.setUsername("mysql.test@example.com");
+ RetailCustomerEntity savedRetailCustomer = retailCustomerRepository.save(retailCustomer);
+
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setRetailCustomer(savedRetailCustomer);
+ usagePoint.setDescription("MySQL Relationship Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ flushAndClear();
+
+ // Act - Retrieve retail customer and usage point
+ Optional retrievedCustomer = retailCustomerRepository.findById(savedRetailCustomer.getId());
+ Optional retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId());
+
+ // Assert
+ assertThat(retrievedCustomer).isPresent();
+ assertThat(retrievedUsagePoint).isPresent();
+ assertThat(retrievedUsagePoint.get().getRetailCustomer()).isNotNull();
+ assertThat(retrievedUsagePoint.get().getRetailCustomer().getUsername()).isEqualTo("mysql.test@example.com");
+ }
+ }
+
+ @Nested
+ @DisplayName("Transaction Boundary Scenarios")
+ class TransactionBoundaryTest {
+
+ @Test
+ @DisplayName("Should maintain data consistency across transaction boundaries")
+ @Transactional
+ void shouldMaintainDataConsistency() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("MySQL Transaction Test");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReading();
+ meterReading.setUsagePoint(savedUsagePoint);
+ meterReading.setDescription("MySQL Transaction Test Reading");
+ MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
+
+ // Act - Modify within same transaction
+ savedUsagePoint.setDescription("MySQL Modified in Transaction");
+ savedMeterReading.setDescription("MySQL Modified Reading in Transaction");
+
+ usagePointRepository.save(savedUsagePoint);
+ meterReadingRepository.save(savedMeterReading);
+
+ // Assert - Changes should be visible within transaction
+ UsagePointEntity retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId()).orElse(null);
+ assertThat(retrievedUsagePoint).isNotNull();
+ assertThat(retrievedUsagePoint.getDescription()).isEqualTo("MySQL Modified in Transaction");
+
+ MeterReadingEntity retrievedMeterReading = meterReadingRepository.findById(savedMeterReading.getId()).orElse(null);
+ assertThat(retrievedMeterReading).isNotNull();
+ assertThat(retrievedMeterReading.getDescription()).isEqualTo("MySQL Modified Reading in Transaction");
+ }
+ }
+
+ @Nested
+ @DisplayName("Bulk Operation Integrity")
+ class BulkOperationTest {
+
+ @Test
+ @DisplayName("Should handle bulk save operations correctly")
+ void shouldHandleBulkSaveOperations() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("MySQL Bulk Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ List meterReadings = TestDataBuilders.createValidEntities(5,
+ () -> {
+ MeterReadingEntity reading = TestDataBuilders.createValidMeterReading();
+ reading.setUsagePoint(savedUsagePoint);
+ return reading;
+ });
+
+ // Act - Bulk save
+ List savedReadings = meterReadingRepository.saveAll(meterReadings);
+ flushAndClear();
+
+ // Assert
+ assertThat(savedReadings).hasSize(5);
+ assertThat(savedReadings).allMatch(reading -> reading.getId() != null);
+
+ // Verify all readings were saved
+ long count = meterReadingRepository.count();
+ assertThat(count).isGreaterThanOrEqualTo(5);
+ }
+
+ @Test
+ @DisplayName("Should handle bulk delete operations correctly")
+ void shouldHandleBulkDeleteOperations() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ List meterReadings = TestDataBuilders.createValidEntities(3,
+ () -> {
+ MeterReadingEntity reading = TestDataBuilders.createValidMeterReading();
+ reading.setUsagePoint(savedUsagePoint);
+ return reading;
+ });
+
+ List savedReadings = meterReadingRepository.saveAll(meterReadings);
+ long initialCount = meterReadingRepository.count();
+ flushAndClear();
+
+ // Act - Bulk delete
+ meterReadingRepository.deleteAll(savedReadings);
+ flushAndClear();
+
+ // Assert
+ long finalCount = meterReadingRepository.count();
+ assertThat(finalCount).isEqualTo(initialCount - 3);
+ }
+ }
+}
\ No newline at end of file
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipPostgreSQLIntegrationTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipPostgreSQLIntegrationTest.java
new file mode 100644
index 00000000..84d79c69
--- /dev/null
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/integration/ComplexRelationshipPostgreSQLIntegrationTest.java
@@ -0,0 +1,276 @@
+/*
+ *
+ * Copyright (c) 2025 Green Button Alliance, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.greenbuttonalliance.espi.common.repositories.integration;
+
+import org.greenbuttonalliance.espi.common.domain.customer.entity.CustomerEntity;
+import org.greenbuttonalliance.espi.common.domain.customer.entity.StatementEntity;
+import org.greenbuttonalliance.espi.common.domain.usage.*;
+import org.greenbuttonalliance.espi.common.repositories.customer.CustomerRepository;
+import org.greenbuttonalliance.espi.common.repositories.customer.StatementRepository;
+import org.greenbuttonalliance.espi.common.repositories.usage.*;
+import org.greenbuttonalliance.espi.common.test.BaseTestContainersTest;
+import org.greenbuttonalliance.espi.common.test.TestDataBuilders;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.transaction.annotation.Transactional;
+import org.testcontainers.junit.jupiter.Container;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * Complex relationship integration tests for JPA entities using PostgreSQL TestContainer.
+ *
+ * Tests basic relationship operations and entity hierarchies using
+ * available repositories and methods with a real PostgreSQL database.
+ */
+@DisplayName("Complex Relationship Integration Tests - PostgreSQL")
+@ActiveProfiles({"test", "test-postgresql"})
+class ComplexRelationshipPostgreSQLIntegrationTest extends BaseTestContainersTest {
+
+ @Container
+ private static final org.testcontainers.containers.PostgreSQLContainer> postgres = postgresqlContainer;
+
+ static {
+ postgres.start();
+ }
+
+ @DynamicPropertySource
+ static void configurePostgreSQLProperties(DynamicPropertyRegistry registry) {
+ registry.add("spring.datasource.url", postgres::getJdbcUrl);
+ registry.add("spring.datasource.username", postgres::getUsername);
+ registry.add("spring.datasource.password", postgres::getPassword);
+ registry.add("spring.datasource.driver-class-name", () -> "org.postgresql.Driver");
+ }
+
+ @Autowired
+ private CustomerRepository customerRepository;
+
+ @Autowired
+ private StatementRepository statementRepository;
+
+ @Autowired
+ private UsagePointRepository usagePointRepository;
+
+ @Autowired
+ private MeterReadingRepository meterReadingRepository;
+
+ @Autowired
+ private IntervalBlockRepository intervalBlockRepository;
+
+ @Autowired
+ private RetailCustomerRepository retailCustomerRepository;
+
+ @Nested
+ @DisplayName("Basic Relationship Operations")
+ class BasicRelationshipTest {
+
+ @Test
+ @DisplayName("Should handle Customer → Statement relationships")
+ void shouldHandleCustomerStatementRelationships() {
+ // Arrange
+ CustomerEntity customer = TestDataBuilders.createValidCustomer();
+ customer.setCustomerName("PostgreSQL Relationship Test Customer");
+ CustomerEntity savedCustomer = customerRepository.save(customer);
+
+ StatementEntity statement = TestDataBuilders.createValidStatement();
+ statement.setCustomer(savedCustomer);
+ statement.setDescription("PostgreSQL Relationship Test Statement");
+ StatementEntity savedStatement = statementRepository.save(statement);
+
+ flushAndClear();
+
+ // Act - Retrieve customer and statement
+ Optional retrievedCustomer = customerRepository.findById(savedCustomer.getId());
+ Optional retrievedStatement = statementRepository.findById(savedStatement.getId());
+
+ // Assert
+ assertThat(retrievedCustomer).isPresent();
+ assertThat(retrievedStatement).isPresent();
+ assertThat(retrievedStatement.get().getCustomer()).isNotNull();
+ assertThat(retrievedStatement.get().getCustomer().getId()).isEqualTo(savedCustomer.getId());
+ }
+
+ @Test
+ @DisplayName("Should handle UsagePoint → MeterReading → IntervalBlock hierarchy")
+ void shouldHandleUsagePointHierarchy() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("PostgreSQL Hierarchy Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReading();
+ meterReading.setUsagePoint(savedUsagePoint);
+ meterReading.setDescription("PostgreSQL Hierarchy Test Meter Reading");
+ MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
+
+ IntervalBlockEntity intervalBlock = TestDataBuilders.createValidIntervalBlock();
+ intervalBlock.setMeterReading(savedMeterReading);
+ intervalBlock.setDescription("PostgreSQL Hierarchy Test Interval Block");
+ IntervalBlockEntity savedIntervalBlock = intervalBlockRepository.save(intervalBlock);
+
+ flushAndClear();
+
+ // Act - Retrieve the hierarchy
+ Optional retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId());
+ Optional retrievedMeterReading = meterReadingRepository.findById(savedMeterReading.getId());
+ Optional retrievedIntervalBlock = intervalBlockRepository.findById(savedIntervalBlock.getId());
+
+ // Assert
+ assertThat(retrievedUsagePoint).isPresent();
+ assertThat(retrievedMeterReading).isPresent();
+ assertThat(retrievedIntervalBlock).isPresent();
+
+ assertThat(retrievedMeterReading.get().getUsagePoint()).isNotNull();
+ assertThat(retrievedMeterReading.get().getUsagePoint().getId()).isEqualTo(savedUsagePoint.getId());
+
+ assertThat(retrievedIntervalBlock.get().getMeterReading()).isNotNull();
+ assertThat(retrievedIntervalBlock.get().getMeterReading().getId()).isEqualTo(savedMeterReading.getId());
+ }
+
+ @Test
+ @DisplayName("Should handle RetailCustomer → UsagePoint relationships")
+ void shouldHandleRetailCustomerUsagePointRelationships() {
+ // Arrange
+ RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
+ retailCustomer.setUsername("postgresql.test@example.com");
+ RetailCustomerEntity savedRetailCustomer = retailCustomerRepository.save(retailCustomer);
+
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setRetailCustomer(savedRetailCustomer);
+ usagePoint.setDescription("PostgreSQL Relationship Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ flushAndClear();
+
+ // Act - Retrieve retail customer and usage point
+ Optional retrievedCustomer = retailCustomerRepository.findById(savedRetailCustomer.getId());
+ Optional retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId());
+
+ // Assert
+ assertThat(retrievedCustomer).isPresent();
+ assertThat(retrievedUsagePoint).isPresent();
+ assertThat(retrievedUsagePoint.get().getRetailCustomer()).isNotNull();
+ assertThat(retrievedUsagePoint.get().getRetailCustomer().getUsername()).isEqualTo("postgresql.test@example.com");
+ }
+ }
+
+ @Nested
+ @DisplayName("Transaction Boundary Scenarios")
+ class TransactionBoundaryTest {
+
+ @Test
+ @DisplayName("Should maintain data consistency across transaction boundaries")
+ @Transactional
+ void shouldMaintainDataConsistency() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("PostgreSQL Transaction Test");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReading();
+ meterReading.setUsagePoint(savedUsagePoint);
+ meterReading.setDescription("PostgreSQL Transaction Test Reading");
+ MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
+
+ // Act - Modify within same transaction
+ savedUsagePoint.setDescription("PostgreSQL Modified in Transaction");
+ savedMeterReading.setDescription("PostgreSQL Modified Reading in Transaction");
+
+ usagePointRepository.save(savedUsagePoint);
+ meterReadingRepository.save(savedMeterReading);
+
+ // Assert - Changes should be visible within transaction
+ UsagePointEntity retrievedUsagePoint = usagePointRepository.findById(savedUsagePoint.getId()).orElse(null);
+ assertThat(retrievedUsagePoint).isNotNull();
+ assertThat(retrievedUsagePoint.getDescription()).isEqualTo("PostgreSQL Modified in Transaction");
+
+ MeterReadingEntity retrievedMeterReading = meterReadingRepository.findById(savedMeterReading.getId()).orElse(null);
+ assertThat(retrievedMeterReading).isNotNull();
+ assertThat(retrievedMeterReading.getDescription()).isEqualTo("PostgreSQL Modified Reading in Transaction");
+ }
+ }
+
+ @Nested
+ @DisplayName("Bulk Operation Integrity")
+ class BulkOperationTest {
+
+ @Test
+ @DisplayName("Should handle bulk save operations correctly")
+ void shouldHandleBulkSaveOperations() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ usagePoint.setDescription("PostgreSQL Bulk Test Usage Point");
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ List meterReadings = TestDataBuilders.createValidEntities(5,
+ () -> {
+ MeterReadingEntity reading = TestDataBuilders.createValidMeterReading();
+ reading.setUsagePoint(savedUsagePoint);
+ return reading;
+ });
+
+ // Act - Bulk save
+ List savedReadings = meterReadingRepository.saveAll(meterReadings);
+ flushAndClear();
+
+ // Assert
+ assertThat(savedReadings).hasSize(5);
+ assertThat(savedReadings).allMatch(reading -> reading.getId() != null);
+
+ // Verify all readings were saved
+ long count = meterReadingRepository.count();
+ assertThat(count).isGreaterThanOrEqualTo(5);
+ }
+
+ @Test
+ @DisplayName("Should handle bulk delete operations correctly")
+ void shouldHandleBulkDeleteOperations() {
+ // Arrange
+ UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
+ UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
+
+ List meterReadings = TestDataBuilders.createValidEntities(3,
+ () -> {
+ MeterReadingEntity reading = TestDataBuilders.createValidMeterReading();
+ reading.setUsagePoint(savedUsagePoint);
+ return reading;
+ });
+
+ List savedReadings = meterReadingRepository.saveAll(meterReadings);
+ long initialCount = meterReadingRepository.count();
+ flushAndClear();
+
+ // Act - Bulk delete
+ meterReadingRepository.deleteAll(savedReadings);
+ flushAndClear();
+
+ // Assert
+ long finalCount = meterReadingRepository.count();
+ assertThat(finalCount).isEqualTo(initialCount - 3);
+ }
+ }
+}
\ No newline at end of file
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/BaseTestContainersTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/BaseTestContainersTest.java
new file mode 100644
index 00000000..dfee4fd6
--- /dev/null
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/BaseTestContainersTest.java
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright (c) 2025 Green Button Alliance, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.greenbuttonalliance.espi.common.test;
+
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testcontainers.containers.MySQLContainer;
+import org.testcontainers.containers.PostgreSQLContainer;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+import jakarta.validation.Validator;
+
+/**
+ * Base class for TestContainers integration tests providing MySQL and PostgreSQL container setup.
+ *
+ * This abstract class provides:
+ * - TestContainers setup for MySQL and PostgreSQL
+ * - Dynamic datasource configuration from containers
+ * - DataJpaTest configuration with real databases
+ * - TestEntityManager for direct entity operations
+ * - Validator for constraint testing
+ *
+ * Subclasses should use either {@link #mysqlContainer} or {@link #postgresqlContainer}
+ * and configure the datasource properties accordingly.
+ */
+@DataJpaTest
+@Testcontainers
+@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
+@ContextConfiguration(classes = org.greenbuttonalliance.espi.common.TestApplication.class)
+@ActiveProfiles("test")
+public abstract class BaseTestContainersTest {
+
+ /**
+ * MySQL 8.0 container for integration testing.
+ * Reusable across tests for better performance.
+ */
+ protected static final MySQLContainer> mysqlContainer = new MySQLContainer<>("mysql:8.0")
+ .withDatabaseName("openespi_test")
+ .withUsername("test")
+ .withPassword("test")
+ .withReuse(true);
+
+ /**
+ * PostgreSQL 15 container for integration testing.
+ * Reusable across tests for better performance.
+ */
+ protected static final PostgreSQLContainer> postgresqlContainer = new PostgreSQLContainer<>("postgres:15-alpine")
+ .withDatabaseName("openespi_test")
+ .withUsername("test")
+ .withPassword("test")
+ .withReuse(true);
+
+ /**
+ * TestEntityManager for direct entity operations in tests.
+ */
+ @Autowired
+ protected TestEntityManager entityManager;
+
+ /**
+ * Bean validator for testing validation constraints.
+ */
+ protected Validator validator = jakarta.validation.Validation.buildDefaultValidatorFactory().getValidator();
+
+ /**
+ * Flushes and clears the entity manager to ensure database synchronization.
+ */
+ protected void flushAndClear() {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ /**
+ * Persists an entity and flushes to ensure it's saved to the database.
+ *
+ * @param entity Entity to persist
+ * @param Entity type
+ * @return Persisted entity
+ */
+ protected T persistAndFlush(T entity) {
+ T persisted = entityManager.persistAndFlush(entity);
+ entityManager.clear();
+ return persisted;
+ }
+
+ /**
+ * Merges a detached entity and flushes to ensure updates are persisted.
+ * Use this for updating entities between operations where the context was cleared.
+ *
+ * @param entity Detached or managed entity with modifications
+ * @param Entity type
+ * @return Managed, updated entity
+ */
+ protected T mergeAndFlush(T entity) {
+ T managed = entityManager.merge(entity);
+ entityManager.flush();
+ entityManager.clear();
+ return managed;
+ }
+}
\ No newline at end of file
diff --git a/openespi-common/src/test/resources/application-test-mysql.yml b/openespi-common/src/test/resources/application-test-mysql.yml
index 3c645197..de5d8135 100644
--- a/openespi-common/src/test/resources/application-test-mysql.yml
+++ b/openespi-common/src/test/resources/application-test-mysql.yml
@@ -1,71 +1,24 @@
-# Test Configuration for MySQL compatibility
spring:
- datasource:
-# url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
-# username: sa
-# password:
-# driver-class-name: org.h2.Driver
- driver-class-name: com.mysql.cj.jdbc.Driver
- hikari:
- maximum-pool-size: 20
- minimum-idle: 5
- idle-timeout: 300000
- max-lifetime: 1200000
- connection-timeout: 20000
- validation-timeout: 5000
- leak-detection-threshold: 60000
jpa:
+ database-platform: org.hibernate.dialect.MySQLDialect
hibernate:
- ddl-auto: validate
- show-sql: false
+ ddl-auto: none
properties:
+ jakarta:
+ persistence:
+ schema-generation:
+ database:
+ action: none
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
- generate_statistics: false
- show_sql: false
format_sql: true
- use_sql_comments: true
- jdbc:
- batch_size: 25
- connection:
- provider_disables_autocommit: true
- cache:
- use_second_level_cache: false
- use_query_cache: false
- show-sql: false
- open-in-view: false
- database: mysql
-
+ show-sql: true
+
flyway:
enabled: true
- locations:
- - classpath:db/migration
- - classpath:db/vendor/mysql
- baseline-on-migrate: true
- baseline-version: 1
- validate-on-migrate: true
- clean-disabled: true
-
- security:
- oauth2:
- resourceserver:
- jwt:
- issuer-uri: http://localhost:8080
- jwk-set-uri: http://localhost:8080/.well-known/jwks.json
+ locations: classpath:db/migration,classpath:db/vendor/mysql
logging:
level:
- org.greenbuttonalliance.espi: DEBUG
- org.springframework.security: WARN
- # org.hibernate: DEBUG
- # org.hibernate.tool.schema: DEBUG
- # org.flywaydb: DEBUG
- # org.springframework.orm.jpa: DEBUG
- #org.springframework.boot.autoconfigure: DEBUG
-
-espi:
- datacustodian:
- base-url: http://localhost:8081/DataCustodian
- authorization-server:
- issuer-uri: http://localhost:8080
- jwk-set-uri: http://localhost:8080/.well-known/jwks.json
\ No newline at end of file
+ org.flywaydb: DEBUG
+ org.hibernate.SQL: DEBUG
diff --git a/openespi-common/src/test/resources/application-test-postgresql.yml b/openespi-common/src/test/resources/application-test-postgresql.yml
new file mode 100644
index 00000000..5eb01972
--- /dev/null
+++ b/openespi-common/src/test/resources/application-test-postgresql.yml
@@ -0,0 +1,24 @@
+spring:
+ jpa:
+ database-platform: org.hibernate.dialect.PostgreSQLDialect
+ hibernate:
+ ddl-auto: none
+ properties:
+ jakarta:
+ persistence:
+ schema-generation:
+ database:
+ action: none
+ hibernate:
+ dialect: org.hibernate.dialect.PostgreSQLDialect
+ format_sql: true
+ show-sql: true
+
+ flyway:
+ enabled: true
+ locations: classpath:db/migration,classpath:db/vendor/postgres
+
+logging:
+ level:
+ org.flywaydb: DEBUG
+ org.hibernate.SQL: DEBUG