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