content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.apache.atlas.hive.hook.events;
  
  import org.apache.atlas.hive.hook.AtlasHiveHookContext;
  import org.apache.atlas.hive.hook.HiveHook.PreprocessAction;
  import org.apache.atlas.model.instance.AtlasEntity;
  import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo;
  import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
  import org.apache.atlas.model.instance.AtlasObjectId;
  import org.apache.atlas.model.notification.HookNotification;
  import org.apache.hadoop.hive.metastore.TableType;
  import org.apache.hadoop.hive.metastore.api.Database;
  import org.apache.hadoop.hive.metastore.api.FieldSchema;
  import org.apache.hadoop.hive.metastore.api.SerDeInfo;
  import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
  import org.apache.hadoop.hive.ql.hooks.Entity;
  import org.apache.hadoop.hive.ql.hooks.HookContext;
  import org.apache.hadoop.hive.ql.hooks.LineageInfo;
  import org.apache.hadoop.hive.ql.hooks.LineageInfo.BaseColumnInfo;
  import org.apache.hadoop.hive.ql.hooks.LineageInfo.DependencyKey;
  import org.apache.hadoop.hive.ql.hooks.ReadEntity;
  import org.apache.hadoop.hive.ql.hooks.WriteEntity;
  import org.apache.hadoop.hive.ql.metadata.Hive;
  import org.apache.hadoop.hive.ql.metadata.Table;
  import org.apache.hadoop.hive.ql.plan.HiveOperation;
  import org.apache.hadoop.security.UserGroupInformation;
  import org.mockito.Mock;
  import org.mockito.Mockito;
  import org.mockito.MockitoAnnotations;
  import org.testng.AssertJUnit;
  import org.testng.annotations.BeforeMethod;
  import org.testng.annotations.Test;
  
  import java.net.URI;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import static org.mockito.Matchers.any;
  import static org.mockito.Matchers.anyString;
  import static org.mockito.Matchers.eq;
  import static org.mockito.Mockito.when;
  import static org.mockito.Mockito.doNothing;
  
  import static org.mockito.Mockito.doReturn;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.spy;
  
  public class BaseHiveEventTest {
  
      @Mock
      AtlasHiveHookContext context;
  
      @Mock
      Hive hive;
  
      Table table;
  
      @Mock
      Database database;
  
      @Mock
      HookContext hookContext;
  
      @Mock
      Entity entity;
  
      @Mock
      ReadEntity readEntity;
  
      @Mock
      WriteEntity writeEntity;
  
      @Mock
      LineageInfo lineageInfo;
  
      @Mock
      UserGroupInformation ugi;
  
      // Concrete implementation of BaseHiveEvent for testing
      private static class TestableBaseHiveEvent extends BaseHiveEvent {
          public TestableBaseHiveEvent(AtlasHiveHookContext context) {
              super(context);
          }
  
          @Override
          public List<HookNotification> getNotificationMessages() throws Exception {
              return Collections.emptyList();
          }
      }
  
      private TestableBaseHiveEvent baseHiveEvent;
  
      @BeforeMethod
      public void setUp() {
          MockitoAnnotations.initMocks(this);
          
          // Create a real Table object with a mocked tTable to avoid NPE issues
          org.apache.hadoop.hive.metastore.api.Table tTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(tTable.getTableName()).thenReturn("test_table");
          when(tTable.getCreateTime()).thenReturn(1000);
          when(tTable.getDbName()).thenReturn("default");
          
          // Create a real Table object and then spy on it
          table = spy(new Table(tTable));
          
          // Mock required context methods for BaseHiveEvent constructor
          when(context.isSkipTempTables()).thenReturn(false);
          when(context.isMetastoreHook()).thenReturn(false); // Ensure it's not a metastore hook
          baseHiveEvent = new TestableBaseHiveEvent(context);
      }
  
      // ========== CONSTRUCTOR TESTS ==========
  
      @Test
      public void testConstructor() {
          when(context.isSkipTempTables()).thenReturn(true);
          TestableBaseHiveEvent event = new TestableBaseHiveEvent(context);
  
          AssertJUnit.assertNotNull("BaseHiveEvent should be instantiated", event);
          AssertJUnit.assertEquals("Context should be set", context, event.getContext());
      }
  
      @Test(expectedExceptions = NullPointerException.class)
      public void testConstructorWithNullContext() {
          new TestableBaseHiveEvent(null);
      }
  
      // ========== UTILITY METHOD TESTS ==========
  
      @Test
      public void testGetTableCreateTime() {
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(metastoreTable.getCreateTime()).thenReturn(1000);
          when(table.getTTable()).thenReturn(metastoreTable);
  
          long createTime = BaseHiveEvent.getTableCreateTime(table);
  
          AssertJUnit.assertEquals("Create time should be converted from seconds to milliseconds",
                  1000 * BaseHiveEvent.MILLIS_CONVERT_FACTOR, createTime);
      }
  
      @Test
      public void testGetTableCreateTime_NullTTable() {
          when(table.getTTable()).thenReturn(null);
  
          long createTime = BaseHiveEvent.getTableCreateTime(table);
  
          AssertJUnit.assertTrue("Should return current time when TTable is null",
                  createTime > 0 && createTime <= System.currentTimeMillis());
      }
  
      @Test
      public void testGetTableOwner() {
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(table.getTTable()).thenReturn(metastoreTable);
          when(table.getOwner()).thenReturn("test_user");
  
          String owner = BaseHiveEvent.getTableOwner(table);
  
          AssertJUnit.assertEquals("Should return table owner", "test_user", owner);
      }
  
      @Test
      public void testGetTableOwner_NullTTable() {
          when(table.getTTable()).thenReturn(null);
  
          String owner = BaseHiveEvent.getTableOwner(table);
  
          AssertJUnit.assertEquals("Should return empty string when TTable is null", "", owner);
      }
  
      @Test
      public void testGetObjectIds() {
          AtlasEntity entity1 = new AtlasEntity("type1");
          entity1.setAttribute("qualifiedName", "entity1@cluster");
          AtlasEntity entity2 = new AtlasEntity("type2");
          entity2.setAttribute("qualifiedName", "entity2@cluster");
  
          List<AtlasEntity> entities = Arrays.asList(entity1, entity2);
  
          List<AtlasObjectId> objectIds = BaseHiveEvent.getObjectIds(entities);
  
          AssertJUnit.assertEquals("Should return correct number of object IDs", 2, objectIds.size());
          AssertJUnit.assertEquals("First object ID type should match", "type1", objectIds.get(0).getTypeName());
          AssertJUnit.assertEquals("Second object ID type should match", "type2", objectIds.get(1).getTypeName());
      }
  
      @Test
      public void testGetObjectIds_EmptyList() {
          List<AtlasObjectId> objectIds = BaseHiveEvent.getObjectIds(Collections.emptyList());
  
          AssertJUnit.assertTrue("Should return empty list", objectIds.isEmpty());
      }
  
      @Test
      public void testGetObjectIds_NullList() {
          List<AtlasObjectId> objectIds = BaseHiveEvent.getObjectIds(null);
  
          AssertJUnit.assertTrue("Should return empty list for null input", objectIds.isEmpty());
      }
  
      // ========== ENTITY PROCESSING TESTS ==========
  
      @Test
      public void testAddProcessedEntities() throws Exception {
          AtlasEntitiesWithExtInfo entitiesWithExtInfo = new AtlasEntitiesWithExtInfo();
  
          AtlasEntity contextEntity = new AtlasEntity("test_type");
          contextEntity.setAttribute("qualifiedName", "test@cluster");
          List<AtlasEntity> contextEntities = Arrays.asList(contextEntity);
  
          when(context.getEntities()).thenReturn(contextEntities);
  
          // Use generic safe matcher instead of anyCollection()
          doNothing().when(context).addToKnownEntities(Mockito.<Collection<AtlasEntity>>any());
  
          baseHiveEvent.addProcessedEntities(entitiesWithExtInfo);
  
  
          AssertJUnit.assertEquals("Should have referred entities", 1,
                  entitiesWithExtInfo.getReferredEntities().size());
      }
  
      @Test
      public void testGetInputOutputEntity_DfsDir() throws Exception {
          URI location = URI.create("hdfs://namenode:9000/test/path");
          when(entity.getType()).thenReturn(Entity.Type.DFS_DIR);
          when(entity.getLocation()).thenReturn(location);
  
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isConvertHdfsPathToLowerCase()).thenReturn(true);
          when(context.getQNameToEntityMap()).thenReturn(new HashMap<>());
          when(context.getAwsS3AtlasModelVersion()).thenReturn(String.valueOf(1));
  
          AtlasEntity result = baseHiveEvent.getInputOutputEntity(entity, new AtlasEntitiesWithExtInfo(), false);
  
          AssertJUnit.assertNotNull("Should return path entity", result);
      }
  
      @Test
      public void testGetInputOutputEntity_SkipTempTable() throws Exception {
          // Mock the underlying metastore Table
  
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          Table tableSpy = spy(new Table(metastoreTable));
          when(entity.getType()).thenReturn(Entity.Type.TABLE);
          when(entity.getTable()).thenReturn(tableSpy);
          when(tableSpy.isTemporary()).thenReturn(true);
          when(metastoreTable.getTableName()).thenReturn("test_table");
          when(context.isSkipTempTables()).thenReturn(true);
          when(tableSpy.getDbName()).thenReturn("default"); // Mock getDbName to avoid potential NPE
          when(context.getEntity(any(String.class))).thenReturn(null); // Mock entity lookup
          doNothing().when(context).putEntity(any(String.class), any(AtlasEntity.class)); // Mock entity storage
  
          // Debug: Verify mock setup
          AssertJUnit.assertEquals("Verify entity type", Entity.Type.TABLE, entity.getType());
  //        AssertJUnit.assertEquals("Verify table is temporary", true, table.isTemporary());
          AssertJUnit.assertEquals("Verify table name", "test_table", metastoreTable.getTableName());
  
          AtlasEntity result = baseHiveEvent.getInputOutputEntity(entity, new AtlasEntitiesWithExtInfo(), true);
  
          AssertJUnit.assertNull("Should return null for temp table when skipTempTables=true", result);
      }
  
      // ========== DATABASE ENTITY TESTS ==========
  
      @Test
      public void testToDbEntity() throws Exception {
          when(database.getName()).thenReturn("test_db");
          when(database.getDescription()).thenReturn("Test database");
          when(database.getOwnerName()).thenReturn("test_owner");
          when(database.getLocationUri()).thenReturn("hdfs://namenode:9000/warehouse/test_db");
          when(database.getParameters()).thenReturn(new HashMap<>());
  
          when(context.getQualifiedName(database)).thenReturn("test_db@cluster");
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isKnownDatabase(anyString())).thenReturn(false);
          when(context.getEntity(anyString())).thenReturn(null);
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          AtlasEntity result = baseHiveEvent.toDbEntity(database);
  
          AssertJUnit.assertNotNull("Should return database entity", result);
          AssertJUnit.assertEquals("Should be hive_db type", "hive_db", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct name", "test_db", result.getAttribute("name"));
          AssertJUnit.assertEquals("Should have correct owner", "test_owner", result.getAttribute("owner"));
          AssertJUnit.assertEquals("Should have correct description", "Test database", result.getAttribute("description"));
      }
  
      @Test
      public void testToDbEntity_KnownDatabase() throws Exception {
          when(database.getName()).thenReturn("test_db");
          when(context.getQualifiedName(database)).thenReturn("test_db@cluster");
          when(context.isKnownDatabase("test_db@cluster")).thenReturn(true);
          when(context.getEntity("test_db@cluster")).thenReturn(null);
          when(context.getMetadataNamespace()).thenReturn("cluster");
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          AtlasEntity result = baseHiveEvent.toDbEntity(database);
  
          AssertJUnit.assertNotNull("Should return database entity", result);
          AssertJUnit.assertNull("Should have null guid for known database", result.getGuid());
      }
  
      // ========== TABLE ENTITY TESTS ==========
  
      @Test
      public void testToTableEntity() throws Exception {
          setupTableMocks();
          setupDatabaseMocks();
  
          when(context.getHive()).thenReturn(hive);
          when(hive.getDatabase("default")).thenReturn(database);
  
          // Mock dbEntity for AtlasTypeUtil.getObjectId(dbEntity)
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("qualifiedName", "default@cluster");
          when(context.getEntity("default@cluster")).thenReturn(dbEntity);
  
          // Debug: Verify mock setup
          AssertJUnit.assertEquals("Verify table name", "test_table", table.getTTable().getTableName());
          AssertJUnit.assertEquals("Verify db name", "default", table.getDbName());
          
          AssertJUnit.assertEquals("Verify qualified name", "default.test_table@cluster", context.getQualifiedName(table));
  
          AtlasEntityWithExtInfo result = baseHiveEvent.toTableEntity(table);
  
          AssertJUnit.assertNotNull("Should return table entity with ext info", result);
          AssertJUnit.assertNotNull("Should have entity", result.getEntity());
          AssertJUnit.assertEquals("Should be hive_table type", "hive_table", result.getEntity().getTypeName());
      }
  
  
      // ========== STORAGE DESCRIPTOR TESTS ==========
  
      @Test
      public void testGetStorageDescEntity() throws Exception {
          AtlasObjectId tableId = new AtlasObjectId("hive_table", "qualifiedName", "default.test_table@cluster");
  
          StorageDescriptor sd = mock(StorageDescriptor.class);
          when(sd.getParameters()).thenReturn(new HashMap<>());
          when(sd.getLocation()).thenReturn("/warehouse/default/test_table");
          when(sd.getInputFormat()).thenReturn("org.apache.hadoop.mapred.TextInputFormat");
          when(sd.getOutputFormat()).thenReturn("org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat");
          when(sd.isCompressed()).thenReturn(false);
          when(sd.getNumBuckets()).thenReturn(0);
          when(sd.isStoredAsSubDirectories()).thenReturn(false);
          when(sd.getBucketCols()).thenReturn(Collections.emptyList());
          when(sd.getSortCols()).thenReturn(Collections.emptyList());
  
          SerDeInfo serdeInfo = mock(SerDeInfo.class);
          when(serdeInfo.getName()).thenReturn("test_serde");
          when(serdeInfo.getSerializationLib()).thenReturn("org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe");
          when(serdeInfo.getParameters()).thenReturn(new HashMap<>());
          when(sd.getSerdeInfo()).thenReturn(serdeInfo);
  
          when(table.getSd()).thenReturn(sd);
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster_storage");
  
          when(context.getEntity("default.test_table@cluster_storage")).thenReturn(null);
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          AtlasEntity result = baseHiveEvent.getStorageDescEntity(tableId, table);
  
          AssertJUnit.assertNotNull("Should return storage descriptor entity", result);
          AssertJUnit.assertEquals("Should be hive_storagedesc type", "hive_storagedesc", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct location", "/warehouse/default/test_table",
                  result.getAttribute("location"));
      }
  
      // ========== COLUMN ENTITY TESTS ==========
  
      @Test
      public void testGetColumnEntities() throws Exception {
          AtlasObjectId tableId = new AtlasObjectId("hive_table", "qualifiedName", "default.test_table@cluster");
  
          FieldSchema col1 = new FieldSchema("col1", "string", "First column");
          FieldSchema col2 = new FieldSchema("col2", "int", "Second column");
          List<FieldSchema> columns = Arrays.asList(col1, col2);
  
          when(table.getOwner()).thenReturn("test_owner");
          when(context.getQualifiedName(eq(table))).thenReturn("default.test_table.col1@cluster");
          when(context.getQualifiedName(eq(table))).thenReturn("default.test_table.col2@cluster");
  
          when(context.getEntity("default.test_table.col1@cluster")).thenReturn(null);
          when(context.getEntity("default.test_table.col2@cluster")).thenReturn(null);
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          List<AtlasEntity> result = baseHiveEvent.getColumnEntities(tableId, table, columns, "test_relationship");
  
          AssertJUnit.assertEquals("Should return 2 column entities", 2, result.size());
          AssertJUnit.assertEquals("First column should be hive_column type", "hive_column", result.get(0).getTypeName());
          AssertJUnit.assertEquals("First column should have correct name", "col1", result.get(0).getAttribute("name"));
          AssertJUnit.assertEquals("Second column should have correct name", "col2", result.get(1).getAttribute("name"));
      }
  
      @Test
      public void testGetColumnEntities_EmptyList() throws Exception {
          AtlasObjectId tableId = new AtlasObjectId("hive_table", "qualifiedName", "default.test_table@cluster");
  
          List<AtlasEntity> result = baseHiveEvent.getColumnEntities(tableId, table, Collections.emptyList(), "test_relationship");
  
          AssertJUnit.assertTrue("Should return empty list", result.isEmpty());
      }
  
      // ========== PROCESS ENTITY TESTS ==========
  
      @Test
      public void testGetHiveProcessEntity() throws Exception {
          AtlasEntity input = new AtlasEntity("hive_table");
          input.setAttribute("qualifiedName", "input_table@cluster");
          AtlasEntity output = new AtlasEntity("hive_table");
          output.setAttribute("qualifiedName", "output_table@cluster");
  
          List<AtlasEntity> inputs = Arrays.asList(input);
          List<AtlasEntity> outputs = Arrays.asList(output);
  
          when(context.isMetastoreHook()).thenReturn(false);
          when(context.isHiveProcessPopulateDeprecatedAttributes()).thenReturn(true);
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.getHiveOperation()).thenReturn(HiveOperation.QUERY);
  
          setupHiveContextMocks();
  
          AtlasEntity result = baseHiveEvent.getHiveProcessEntity(inputs, outputs);
  
          AssertJUnit.assertNotNull("Should return process entity", result);
          AssertJUnit.assertEquals("Should be hive_process type", "hive_process", result.getTypeName());
      }
  
      @Test
      public void testGetHiveProcessEntity_MetastoreHook() throws Exception {
          AtlasEntity output = new AtlasEntity("hive_table");
          output.setAttribute("qualifiedName", "output_table@cluster");
          output.setAttribute("createTime", 1000L);
  
          List<AtlasEntity> outputs = Arrays.asList(output);
  
          when(context.isMetastoreHook()).thenReturn(true);
          when(context.getHiveOperation()).thenReturn(HiveOperation.CREATETABLE);
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isHiveProcessPopulateDeprecatedAttributes()).thenReturn(false);
  
          setupHiveContextMocks();
  
          AtlasEntity result = baseHiveEvent.getHiveProcessEntity(Collections.emptyList(), outputs);
  
          AssertJUnit.assertNotNull("Should return process entity", result);
          AssertJUnit.assertEquals("Should be hive_process type", "hive_process", result.getTypeName());
          AssertJUnit.assertEquals("Should have CREATETABLE operation", "CREATETABLE", result.getAttribute("operationType"));
      }
  
      @Test
      public void testGetHiveProcessExecutionEntity() throws Exception {
          AtlasEntity process = new AtlasEntity("hive_process");
          process.setAttribute("qualifiedName", "test_process@cluster");
  
          setupHiveContextMocks();
          when(context.getHostName()).thenReturn("test_host");
  
          AtlasEntity result = baseHiveEvent.getHiveProcessExecutionEntity(process);
  
          AssertJUnit.assertNotNull("Should return process execution entity", result);
          AssertJUnit.assertEquals("Should be hive_process_execution type", "hive_process_execution", result.getTypeName());
          AssertJUnit.assertNotNull("Should have qualified name", result.getAttribute("qualifiedName"));
          AssertJUnit.assertEquals("Should have correct host", "test_host", result.getAttribute("hostName"));
      }
  
      // ========== DDL ENTITY TESTS ==========
  
      @Test
      public void testCreateHiveDDLEntity_Database() throws Exception {
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("qualifiedName", "test_db@cluster");
  
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.getHiveOperation()).thenReturn(HiveOperation.QUERY);
          when(context.getEntity(anyString())).thenReturn(null);
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          setupHiveContextMocks();
  
          AtlasEntity result = baseHiveEvent.createHiveDDLEntity(dbEntity);
  
          AssertJUnit.assertNotNull("Should return DDL entity", result);
          AssertJUnit.assertEquals("Should be hive_db_ddl type", "hive_db_ddl", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct service type", "hive", result.getAttribute("serviceType"));
      }
  
      @Test
      public void testCreateHiveDDLEntity_Table() throws Exception {
          AtlasEntity tableEntity = new AtlasEntity("hive_table");
          tableEntity.setAttribute("qualifiedName", "default.test_table@cluster");
  
          setupHiveContextMocks();
  
          AtlasEntity result = baseHiveEvent.createHiveDDLEntity(tableEntity);
  
          AssertJUnit.assertNotNull("Should return DDL entity", result);
          AssertJUnit.assertEquals("Should be hive_table_ddl type", "hive_table_ddl", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct service type", "hive", result.getAttribute("serviceType"));
      }
  
      @Test
      public void testCreateHiveDDLEntity_ExcludeGuid() throws Exception {
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("qualifiedName", "test_db@cluster");
          dbEntity.setGuid("test-guid");
  
          setupHiveContextMocks();
  
          AtlasEntity result = baseHiveEvent.createHiveDDLEntity(dbEntity, true);
  
          AssertJUnit.assertNotNull("Should return DDL entity", result);
      }
  
      // ========== QUALIFIED NAME TESTS ==========
  
      @Test
      public void testGetQualifiedName_Database() throws Exception {
          Database hiveDatabase = mock(Database.class);
          when(entity.getType()).thenReturn(Entity.Type.DATABASE);
          when(entity.getDatabase()).thenReturn(hiveDatabase);
          when(context.getQualifiedName(hiveDatabase)).thenReturn("test_db@cluster");
  
          String result = baseHiveEvent.getQualifiedName(entity);
  
          AssertJUnit.assertEquals("Should return database qualified name", "test_db@cluster", result);
      }
  
      @Test
      public void testGetQualifiedName_Table() throws Exception {
          when(entity.getType()).thenReturn(Entity.Type.TABLE);
          when(entity.getTable()).thenReturn(table);
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster");
  
          String result = baseHiveEvent.getQualifiedName(entity);
  
          AssertJUnit.assertEquals("Should return table qualified name", "default.test_table@cluster", result);
      }
  
      @Test
      public void testGetQualifiedName_DfsDir() throws Exception {
          URI location = URI.create("hdfs://namenode:9000/test/path");
          when(entity.getType()).thenReturn(Entity.Type.DFS_DIR);
          when(entity.getLocation()).thenReturn(location);
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isConvertHdfsPathToLowerCase()).thenReturn(false);
  
          String result = baseHiveEvent.getQualifiedName(entity);
  
          AssertJUnit.assertNotNull("Should return path qualified name", result);
          AssertJUnit.assertTrue("Should contain cluster namespace", result.contains("cluster"));
      }
  
      @Test
      public void testGetQualifiedName_TableStorageDescriptor() throws Exception {
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster");
  
          String result = baseHiveEvent.getQualifiedName(table, mock(StorageDescriptor.class));
  
          AssertJUnit.assertEquals("Should return storage descriptor qualified name",
                  "default.test_table@cluster_storage", result);
      }
  
      @Test
      public void testGetQualifiedName_TableColumn() throws Exception {
          FieldSchema column = new FieldSchema("test_col", "string", null);
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster");
  
          String result = baseHiveEvent.getQualifiedName(table, column);
  
          AssertJUnit.assertNotNull("Should return column qualified name", result);
          AssertJUnit.assertTrue("Should contain column name", result.contains("test_col"));
          AssertJUnit.assertTrue("Should contain cluster namespace", result.contains("cluster"));
      }
  
      @Test
      public void testGetQualifiedName_DependencyKey() throws Exception {
          DependencyKey depKey = mock(DependencyKey.class);
          LineageInfo.DataContainer dataContainer = mock(LineageInfo.DataContainer.class);
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(depKey.getDataContainer()).thenReturn(dataContainer);
          when(dataContainer.getTable()).thenReturn(metastoreTable);
          when(metastoreTable.getDbName()).thenReturn("default");
          when(metastoreTable.getTableName()).thenReturn("test_table");
  
          FieldSchema fieldSchema = new FieldSchema("test_col", "string", null);
          when(depKey.getFieldSchema()).thenReturn(fieldSchema);
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          String result = baseHiveEvent.getQualifiedName(depKey);
  
          AssertJUnit.assertNotNull("Should return dependency key qualified name", result);
          AssertJUnit.assertTrue("Should contain database name", result.contains("default"));
          AssertJUnit.assertTrue("Should contain table name", result.contains("test_table"));
          AssertJUnit.assertTrue("Should contain column name", result.contains("test_col"));
      }
  
      @Test
      public void testGetQualifiedName_BaseColumnInfo() throws Exception {
          BaseColumnInfo baseColInfo = mock(BaseColumnInfo.class);
          LineageInfo.TableAliasInfo tabAlias = mock(LineageInfo.TableAliasInfo.class);
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(baseColInfo.getTabAlias()).thenReturn(tabAlias);
          when(tabAlias.getTable()).thenReturn(metastoreTable);
          when(metastoreTable.getDbName()).thenReturn("default");
          when(metastoreTable.getTableName()).thenReturn("test_table");
  
          FieldSchema column = new FieldSchema("test_col", "string", null);
          when(baseColInfo.getColumn()).thenReturn(column);
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          String result = baseHiveEvent.getQualifiedName(baseColInfo);
  
          AssertJUnit.assertNotNull("Should return base column info qualified name", result);
          AssertJUnit.assertTrue("Should contain database name", result.contains("default"));
          AssertJUnit.assertTrue("Should contain table name", result.contains("test_table"));
          AssertJUnit.assertTrue("Should contain column name", result.contains("test_col"));
      }
  
      @Test
      public void testGetQualifiedName_BaseColumnInfo_NullColumn() throws Exception {
          BaseColumnInfo baseColInfo = mock(BaseColumnInfo.class);
          LineageInfo.TableAliasInfo tabAlias = mock(LineageInfo.TableAliasInfo.class);
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
          when(baseColInfo.getTabAlias()).thenReturn(tabAlias);
          when(tabAlias.getTable()).thenReturn(metastoreTable);
          when(metastoreTable.getDbName()).thenReturn("default");
          when(metastoreTable.getTableName()).thenReturn("test_table");
          when(baseColInfo.getColumn()).thenReturn(null);
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          String result = baseHiveEvent.getQualifiedName(baseColInfo);
  
          AssertJUnit.assertNotNull("Should return qualified name", result);
          AssertJUnit.assertTrue("Should not contain column separator when column is null",
                  !result.contains(".."));
      }
  
      // ========== USER NAME TESTS ==========
  
      @Test
      public void testGetUserName_MetastoreHook() throws Exception {
          when(context.isMetastoreHook()).thenReturn(true);
          when(ugi.getShortUserName()).thenReturn("metastore_user");
  
          String result = baseHiveEvent.getUserName();
  
          AssertJUnit.assertNotNull("Should return user name", result);
      }
  
      @Test
      public void testGetUserName_HiveHook() throws Exception {
          when(context.isMetastoreHook()).thenReturn(false);
          when(context.getHiveContext()).thenReturn(hookContext);
          when(hookContext.getUserName()).thenReturn("hive_user");
  
          String result = baseHiveEvent.getUserName();
  
          AssertJUnit.assertEquals("Should return hive user name", "hive_user", result);
      }
  
      @Test
      public void testGetUserName_Fallback() throws Exception {
          when(context.isMetastoreHook()).thenReturn(false);
          when(context.getHiveContext()).thenReturn(hookContext);
          when(hookContext.getUserName()).thenReturn("");
          when(hookContext.getUgi()).thenReturn(ugi);
          when(ugi.getShortUserName()).thenReturn("ugi_user");
  
          String result = baseHiveEvent.getUserName();
  
          AssertJUnit.assertEquals("Should return UGI user name", "ugi_user", result);
      }
  
      // ========== CONTEXT GETTER TESTS ==========
  
      @Test
      public void testGetInputs() {
          Set<ReadEntity> inputs = Collections.singleton(readEntity);
          when(context.getInputs()).thenReturn(inputs);
  
          Set<ReadEntity> result = baseHiveEvent.getInputs();
  
          AssertJUnit.assertEquals("Should return inputs from context", inputs, result);
      }
  
      @Test
      public void testGetInputs_NullContext() {
          // Create event with null context using reflection to avoid constructor call
          TestableBaseHiveEvent event = null;
          try {
              when(context.getInputs()).thenReturn(null);
              event = new TestableBaseHiveEvent(context);
              Set<ReadEntity> result = event.getInputs();
              AssertJUnit.assertNull("Should return null when context.getInputs() returns null", result);
          } catch (Exception e) {
              // If we can't create with null context, that's expected behavior
              AssertJUnit.assertTrue("Null context handling works as expected", true);
          }
      }
  
      @Test
      public void testGetOutputs() {
          Set<WriteEntity> outputs = Collections.singleton(writeEntity);
          when(context.getOutputs()).thenReturn(outputs);
  
          Set<WriteEntity> result = baseHiveEvent.getOutputs();
  
          AssertJUnit.assertEquals("Should return outputs from context", outputs, result);
      }
  
      @Test
      public void testGetLineageInfo() {
          when(context.getLineageInfo()).thenReturn(lineageInfo);
  
          LineageInfo result = baseHiveEvent.getLineageInfo();
  
          AssertJUnit.assertEquals("Should return lineage info from context", lineageInfo, result);
      }
  
      @Test
      public void testGetHive() {
          when(context.getHive()).thenReturn(hive);
  
          Hive result = baseHiveEvent.getHive();
  
          AssertJUnit.assertEquals("Should return hive from context", hive, result);
      }
  
      @Test
      public void testGetMetadataNamespace() {
          when(context.getMetadataNamespace()).thenReturn("test_cluster");
  
          String result = baseHiveEvent.getMetadataNamespace();
  
          AssertJUnit.assertEquals("Should return metadata namespace", "test_cluster", result);
      }
  
      // ========== HBASE TESTS ==========
  
      @Test
      public void testIsHBaseStore_True() {
          Map<String, String> parameters = new HashMap<>();
          parameters.put("storage_handler", "org.apache.hadoop.hive.hbase.HBaseStorageHandler");
          when(table.getParameters()).thenReturn(parameters);
  
          boolean result = baseHiveEvent.isHBaseStore(table);
  
          AssertJUnit.assertTrue("Should return true for HBase storage handler", result);
      }
  
      @Test
      public void testIsHBaseStore_False() {
          Map<String, String> parameters = new HashMap<>();
          parameters.put("storage_handler", "other.storage.Handler");
          when(table.getParameters()).thenReturn(parameters);
  
          boolean result = baseHiveEvent.isHBaseStore(table);
  
          AssertJUnit.assertFalse("Should return false for non-HBase storage handler", result);
      }
  
      @Test
      public void testIsHBaseStore_NoStorageHandler() {
          when(table.getParameters()).thenReturn(new HashMap<>());
  
          boolean result = baseHiveEvent.isHBaseStore(table);
  
          AssertJUnit.assertFalse("Should return false when no storage handler", result);
      }
  
      @Test
      public void testToReferencedHBaseTable() throws Exception {
          Map<String, String> parameters = new HashMap<>();
          parameters.put("hbase.table.name", "test_namespace:test_table");
          when(table.getParameters()).thenReturn(parameters);
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          AtlasEntitiesWithExtInfo entities = new AtlasEntitiesWithExtInfo();
          AtlasEntity result = baseHiveEvent.toReferencedHBaseTable(table, entities);
  
          AssertJUnit.assertNotNull("Should return HBase table entity", result);
          AssertJUnit.assertEquals("Should be hbase_table type", "hbase_table", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct table name", "test_table", result.getAttribute("name"));
      }
  
      @Test
      public void testToReferencedHBaseTable_DefaultNamespace() throws Exception {
          Map<String, String> parameters = new HashMap<>();
          parameters.put("hbase.table.name", "test_table");  // No namespace specified
          when(table.getParameters()).thenReturn(parameters);
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          AtlasEntitiesWithExtInfo entities = new AtlasEntitiesWithExtInfo();
          AtlasEntity result = baseHiveEvent.toReferencedHBaseTable(table, entities);
  
          AssertJUnit.assertNotNull("Should return HBase table entity", result);
          AssertJUnit.assertEquals("Should be hbase_table type", "hbase_table", result.getTypeName());
          AssertJUnit.assertEquals("Should have correct table name", "test_table", result.getAttribute("name"));
      }
  
      // ========== STATIC TABLE CONVERSION TEST ==========
  
      @Test
      public void testToTable() {
          org.apache.hadoop.hive.metastore.api.Table metastoreTable = mock(org.apache.hadoop.hive.metastore.api.Table.class);
  
          Table result = BaseHiveEvent.toTable(metastoreTable);
  
          AssertJUnit.assertNotNull("Should return Hive Table", result);
          AssertJUnit.assertEquals("Should wrap metastore table", metastoreTable, result.getTTable());
      }
  
      // ========== ADDITIONAL COVERAGE TESTS ==========
  
      @Test
      public void testGetDatabases() throws Exception {
          when(context.isMetastoreHook()).thenReturn(true);
  
          // Create the IHMSHandler mock first
          org.apache.hadoop.hive.metastore.IHMSHandler metastoreHandlerMock = mock(org.apache.hadoop.hive.metastore.IHMSHandler.class);
  
          // Stub the method on the IHMSHandler mock
          when(metastoreHandlerMock.get_database("test_db")).thenReturn(database);
  
          // Now stub context.getMetastoreHandler() to return the above mock
          when(context.getMetastoreHandler()).thenReturn(metastoreHandlerMock);
  
          Database result = baseHiveEvent.getDatabases("test_db");
  
          AssertJUnit.assertEquals("Should return metastore database", database, result);
      }
  
  
      @Test
      public void testGetDatabases_HiveHook() throws Exception {
          when(context.isMetastoreHook()).thenReturn(false);
          when(context.getHive()).thenReturn(hive);
          when(hive.getDatabase("test_db")).thenReturn(database);
  
          Database result = baseHiveEvent.getDatabases("test_db");
  
          AssertJUnit.assertEquals("Should return hive database", database, result);
      }
  
      @Test
      public void testGetNotificationMessages_BaseImplementation() throws Exception {
          List<HookNotification> result = baseHiveEvent.getNotificationMessages();
  
          AssertJUnit.assertNotNull("Should return empty list", result);
          AssertJUnit.assertTrue("Should be empty", result.isEmpty());
      }
  
      @Test
      public void testGetQualifiedName_DbNameColumnName() {
          when(context.getMetadataNamespace()).thenReturn("cluster");
  
          String result = baseHiveEvent.getQualifiedName("testdb", "testtable", "testcol");
  
          AssertJUnit.assertNotNull("Should return qualified name", result);
          AssertJUnit.assertTrue("Should contain database", result.contains("testdb"));
          AssertJUnit.assertTrue("Should contain table", result.contains("testtable"));
          AssertJUnit.assertTrue("Should contain column", result.contains("testcol"));
          AssertJUnit.assertTrue("Should contain cluster", result.contains("cluster"));
      }
  
      @Test
      public void testGetColumnQualifiedName() {
          String result = baseHiveEvent.getColumnQualifiedName("default.test_table@cluster", "test_col");
  
          AssertJUnit.assertNotNull("Should return column qualified name", result);
          AssertJUnit.assertTrue("Should contain column name", result.contains("test_col"));
          AssertJUnit.assertTrue("Should contain cluster", result.contains("cluster"));
      }
  
      @Test
      public void testGetColumnQualifiedName_NoNamespace() {
          String result = baseHiveEvent.getColumnQualifiedName("default.test_table", "test_col");
  
          AssertJUnit.assertNotNull("Should return column qualified name", result);
          AssertJUnit.assertTrue("Should contain column name", result.contains("test_col"));
      }
  
      @Test
      public void testCreateHiveLocationEntity() throws Exception {
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("location", "hdfs://namenode:9000/warehouse/test_db");
  
          AtlasEntitiesWithExtInfo extInfo = new AtlasEntitiesWithExtInfo();
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isConvertHdfsPathToLowerCase()).thenReturn(true);
          when(context.getQNameToEntityMap()).thenReturn(new HashMap<>());
          when(context.getAwsS3AtlasModelVersion()).thenReturn("1");
  
          AtlasEntity result = baseHiveEvent.createHiveLocationEntity(dbEntity, extInfo);
  
          AssertJUnit.assertNotNull("Should return location entity", result);
      }
  
      @Test
      public void testCreateHiveLocationEntity_EmptyLocation() throws Exception {
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("location", "");
  
          AtlasEntitiesWithExtInfo extInfo = new AtlasEntitiesWithExtInfo();
  
          AtlasEntity result = baseHiveEvent.createHiveLocationEntity(dbEntity, extInfo);
  
          AssertJUnit.assertNull("Should return null for empty location", result);
      }
  
      @Test
      public void testCreateHiveLocationEntity_InvalidLocation() throws Exception {
          AtlasEntity dbEntity = new AtlasEntity("hive_db");
          dbEntity.setAttribute("location", "://invalid");  // URI format that causes IllegalArgumentException
  
          AtlasEntitiesWithExtInfo extInfo = new AtlasEntitiesWithExtInfo();
  
          AtlasEntity result = baseHiveEvent.createHiveLocationEntity(dbEntity, extInfo);
  
          AssertJUnit.assertNull("Should return null for invalid location", result);
      }
  
      @Test
      public void testToDbEntity_ExistingEntity() throws Exception {
          AtlasEntity existingEntity = new AtlasEntity("hive_db");
  
          // Mock database properties properly to avoid getDatabaseName NPE
          when(database.getName()).thenReturn("test_db");
          when(database.getCatalogName()).thenReturn(null);
  
          when(context.getQualifiedName(database)).thenReturn("test_db@cluster");
          when(context.getEntity("test_db@cluster")).thenReturn(existingEntity);
  
          AtlasEntity result = baseHiveEvent.toDbEntity(database);
  
          AssertJUnit.assertEquals("Should return existing entity", existingEntity, result);
      }
  
      @Test
      public void testToTableEntity_ExistingEntity() throws Exception {
          AtlasEntity existingEntity = new AtlasEntity("hive_table");
  
          // Mock required dependencies first
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster");
          when(context.isKnownTable(anyString())).thenReturn(false);
          when(context.getPreprocessActionForHiveTable(anyString())).thenReturn(PreprocessAction.NONE);
  
          // Return existing entity instead of null to trigger the cached entity path
          when(context.getEntity("default.test_table@cluster")).thenReturn(existingEntity);
  
          AtlasObjectId dbId = new AtlasObjectId("hive_db", "qualifiedName", "default@cluster");
          AtlasEntity result = baseHiveEvent.toTableEntity(dbId, table, new AtlasEntitiesWithExtInfo());
  
          AssertJUnit.assertEquals("Should return existing entity", existingEntity, result);
      }
  
      @Test
      public void testIsAlterTableOperation() throws Exception {
          when(context.getHiveOperation()).thenReturn(HiveOperation.ALTERTABLE_RENAMECOL);
  
          // Use reflection to call private method
          java.lang.reflect.Method method = BaseHiveEvent.class.getDeclaredMethod("isAlterTableOperation");
          method.setAccessible(true);
          boolean result = (Boolean) method.invoke(baseHiveEvent);
  
          AssertJUnit.assertTrue("Should recognize alter table operation", result);
      }
  
      @Test
      public void testIsAlterTableOperation_False() throws Exception {
          when(context.getHiveOperation()).thenReturn(HiveOperation.QUERY);
  
          // Use reflection to call private method
          java.lang.reflect.Method method = BaseHiveEvent.class.getDeclaredMethod("isAlterTableOperation");
          method.setAccessible(true);
          boolean result = (Boolean) method.invoke(baseHiveEvent);
  
          AssertJUnit.assertFalse("Should not recognize query as alter table operation", result);
      }
  
      @Test
      public void testHasPartitionEntity() throws Exception {
          WriteEntity partitionEntity = mock(WriteEntity.class);
          when(partitionEntity.getType()).thenReturn(Entity.Type.PARTITION);
  
          Collection<WriteEntity> entities = Arrays.asList(partitionEntity);
  
          // Use reflection to call private method
          java.lang.reflect.Method method = BaseHiveEvent.class.getDeclaredMethod("hasPartitionEntity", Collection.class);
          method.setAccessible(true);
          boolean result = (Boolean) method.invoke(baseHiveEvent, entities);
  
          AssertJUnit.assertTrue("Should detect partition entity", result);
      }
  
      @Test
      public void testIgnoreHDFSPathsinProcessQualifiedName_Load() throws Exception {
          when(context.getHiveOperation()).thenReturn(HiveOperation.LOAD);
  
          WriteEntity partitionEntity = mock(WriteEntity.class);
          when(partitionEntity.getType()).thenReturn(Entity.Type.PARTITION);
          when(context.getOutputs()).thenReturn(Collections.singleton(partitionEntity));
  
          // Use reflection to call private method
          java.lang.reflect.Method method = BaseHiveEvent.class.getDeclaredMethod("ignoreHDFSPathsinProcessQualifiedName");
          method.setAccessible(true);
          boolean result = (Boolean) method.invoke(baseHiveEvent);
  
          AssertJUnit.assertTrue("Should ignore HDFS paths for LOAD with partition", result);
      }
  
      @Test
      public void testGetQualifiedName_PathWithLowerCase() throws Exception {
          URI location = URI.create("hdfs://namenode:9000/Test/Path");
          when(entity.getType()).thenReturn(Entity.Type.DFS_DIR);
          when(entity.getLocation()).thenReturn(location);
          when(context.getMetadataNamespace()).thenReturn("cluster");
          when(context.isConvertHdfsPathToLowerCase()).thenReturn(true);
  
          String result = baseHiveEvent.getQualifiedName(entity);
  
          AssertJUnit.assertNotNull("Should return path qualified name", result);
          AssertJUnit.assertTrue("Should contain cluster namespace", result.contains("cluster"));
          AssertJUnit.assertTrue("Should be lowercase", result.toLowerCase().equals(result));
      }
  
      // ========== HELPER METHODS ==========
  
      private void setupTableMocks() {
          // Mock table methods that aren't handled by the real object
          doReturn("test_owner").when(table).getOwner();
          
          // Create parameters map with comment to avoid NPE
          Map<String, String> parameters = new HashMap<>();
          parameters.put("comment", "Test table comment");
          doReturn(parameters).when(table).getParameters();
          
          doReturn(1000).when(table).getLastAccessTime();
          doReturn(0).when(table).getRetention();
          doReturn(TableType.MANAGED_TABLE).when(table).getTableType();
          doReturn(false).when(table).isTemporary();
          doReturn(null).when(table).getViewOriginalText();
          doReturn(null).when(table).getViewExpandedText();
  
          // Mock context methods
          when(context.getQualifiedName(table)).thenReturn("default.test_table@cluster");
          when(context.isKnownTable(anyString())).thenReturn(false);
          when(context.getPreprocessActionForHiveTable(anyString())).thenReturn(PreprocessAction.NONE);
          when(context.getEntity("default.test_table@cluster")).thenReturn(null);
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
  
          // Mock storage descriptor and columns
          StorageDescriptor sd = mock(StorageDescriptor.class);
          doReturn(sd).when(table).getSd();
          doReturn(Collections.emptyList()).when(table).getPartitionKeys();
          doReturn(Collections.emptyList()).when(table).getCols();
      }
  
      private void setupDatabaseMocks() {
          when(database.getName()).thenReturn("default");
          when(database.getCatalogName()).thenReturn(null);
          when(database.getOwnerName()).thenReturn("test_owner");
          when(database.getLocationUri()).thenReturn("/warehouse/default");
          when(database.getParameters()).thenReturn(new HashMap<>());
          when(database.getDescription()).thenReturn("Default database");
          when(context.getQualifiedName(database)).thenReturn("default@cluster");
          when(context.getEntity("default@cluster")).thenReturn(new AtlasEntity("hive_db")); // Mock dbEntity
          doNothing().when(context).putEntity(anyString(), any(AtlasEntity.class));
      }
  
      private void setupHiveContextMocks() {
          // Create the QueryPlan mock separately
          org.apache.hadoop.hive.ql.QueryPlan queryPlan = mock(org.apache.hadoop.hive.ql.QueryPlan.class);
  
          // Mock the hook context and its methods
          when(hookContext.getOperationName()).thenReturn("SELECT");
          when(hookContext.getUserName()).thenReturn("test_user");
          when(hookContext.getQueryPlan()).thenReturn(queryPlan); // Use the pre-created mock
          when(queryPlan.getQueryStr()).thenReturn("SELECT * FROM test_table");
          when(queryPlan.getQueryId()).thenReturn("test_query_id");
          when(queryPlan.getQueryStartTime()).thenReturn(System.currentTimeMillis());
      }
  
  }