1717
1818package org .apache .hertzbeat .warehouse .store ;
1919
20+ import com .zaxxer .hikari .HikariDataSource ;
21+ import org .apache .arrow .vector .types .pojo .ArrowType ;
22+ import org .apache .arrow .vector .types .pojo .Field ;
23+ import org .apache .arrow .vector .types .pojo .FieldType ;
24+ import org .apache .hertzbeat .common .constants .CommonConstants ;
25+ import org .apache .hertzbeat .common .constants .MetricDataConstants ;
26+ import org .apache .hertzbeat .common .entity .arrow .ArrowCell ;
27+ import org .apache .hertzbeat .common .entity .arrow .RowWrapper ;
28+ import org .apache .hertzbeat .common .entity .message .CollectRep ;
2029import org .apache .hertzbeat .warehouse .store .history .tsdb .tdengine .TdEngineDataStorage ;
30+ import org .apache .hertzbeat .warehouse .store .history .tsdb .tdengine .TdEngineProperties ;
2131import org .junit .jupiter .api .BeforeEach ;
2232import org .junit .jupiter .api .Test ;
33+ import org .junit .jupiter .api .extension .ExtendWith ;
34+ import org .mockito .ArgumentCaptor ;
35+ import org .mockito .Mock ;
36+ import org .mockito .Mockito ;
37+ import org .mockito .junit .jupiter .MockitoExtension ;
38+ import org .mockito .junit .jupiter .MockitoSettings ;
39+ import org .mockito .quality .Strictness ;
40+
41+ import java .sql .Connection ;
42+ import java .sql .Statement ;
43+ import java .util .List ;
44+
45+ import static org .junit .jupiter .api .Assertions .assertNotNull ;
46+ import static org .junit .jupiter .api .Assertions .assertTrue ;
47+ import static org .mockito .Mockito .atLeastOnce ;
48+ import static org .mockito .Mockito .verify ;
49+ import static org .mockito .Mockito .when ;
2350
2451/**
2552 * Test case for {@link TdEngineDataStorage}
2653 */
54+ @ ExtendWith (MockitoExtension .class )
55+ @ MockitoSettings (strictness = Strictness .LENIENT )
2756class TdEngineDataStorageTest {
2857
58+ @ Mock
59+ private TdEngineProperties tdEngineProperties ;
60+
61+ @ Mock
62+ private HikariDataSource mockHikariDataSource ;
63+
64+ @ Mock
65+ private Connection mockConnection ;
66+
67+ @ Mock
68+ private Statement mockStatement ;
69+
70+ private TdEngineDataStorage tdEngineDataStorage ;
71+
2972 @ BeforeEach
30- void setUp () {
73+ void setUp () throws Exception {
74+ when (tdEngineProperties .enabled ()).thenReturn (true );
75+ when (tdEngineProperties .url ()).thenReturn ("jdbc:TAOS-RS://localhost:6041/demo" );
76+ when (tdEngineProperties .username ()).thenReturn ("root" );
77+ when (tdEngineProperties .password ()).thenReturn ("root" );
78+ when (tdEngineProperties .tableStrColumnDefineMaxLength ()).thenReturn (200 );
79+ when (mockHikariDataSource .getConnection ()).thenReturn (mockConnection );
80+ when (mockConnection .createStatement ()).thenReturn (mockStatement );
3181 }
3282
3383 @ Test
3484 void isServerAvailable () {
3585 }
3686
3787 @ Test
38- void saveData () {
88+ void testSaveData () throws Exception {
89+ tdEngineDataStorage = new TdEngineDataStorage (tdEngineProperties );
90+ setPrivateField (tdEngineDataStorage , "hikariDataSource" , mockHikariDataSource );
91+ setParentPrivateField (tdEngineDataStorage , "serverAvailable" , true );
92+
93+ CollectRep .MetricsData metricsData = generateMockedMetricsData ();
94+ tdEngineDataStorage .saveData (metricsData );
95+
96+ ArgumentCaptor <String > sqlCaptor = ArgumentCaptor .forClass (String .class );
97+ verify (mockStatement , atLeastOnce ()).execute (sqlCaptor .capture ());
98+
99+ String executedSql = sqlCaptor .getValue ();
100+
101+ // Verify SQL structure
102+ assertNotNull (executedSql );
103+ assertTrue (executedSql .startsWith ("INSERT INTO" ), "SQL should start with INSERT INTO" );
104+ assertTrue (executedSql .contains ("USING" ), "SQL should contain USING clause" );
105+ assertTrue (executedSql .contains ("TAGS" ), "SQL should contain TAGS clause" );
106+ assertTrue (executedSql .contains ("VALUES" ), "SQL should contain VALUES clause" );
107+
108+ // Verify table name format: app_metrics_instance_v2
109+ assertTrue (executedSql .contains ("app_cpu_test-%server-01_v2" ), "Should contain correct table name" );
110+ // Verify super table name format: app_metrics_super_v2
111+ assertTrue (executedSql .contains ("app_cpu_super_v2" ), "Should contain correct super table name" );
112+ // Verify tags format: test-%server-01
113+ assertTrue (executedSql .contains ("TAGS ('test-%server-01')" ), "Should contain correct super table name" );
114+ // Verify VALUES clause structure (timestamp + data values)
115+ assertTrue (executedSql .matches (".*VALUES\\ s+\\ (\\ d+.*68\\ .7\\ )" ), "Should contain timestamp and value 68.7" );
39116 }
40117
41118 @ Test
@@ -49,4 +126,81 @@ void getHistoryMetricData() {
49126 @ Test
50127 void getHistoryIntervalMetricData () {
51128 }
129+
130+ /**
131+ * Helper method to set private field using reflection
132+ */
133+ private void setPrivateField (Object target , String fieldName , Object value ) throws Exception {
134+ java .lang .reflect .Field field = target .getClass ().getDeclaredField (fieldName );
135+ field .setAccessible (true );
136+ field .set (target , value );
137+ }
138+
139+ /**
140+ * Helper method to set private field from parent class using reflection
141+ */
142+ private void setParentPrivateField (Object target , String fieldName , Object value ) throws Exception {
143+ java .lang .reflect .Field field = target .getClass ().getSuperclass ().getDeclaredField (fieldName );
144+ field .setAccessible (true );
145+ field .set (target , value );
146+ }
147+
148+ public static CollectRep .MetricsData generateMockedMetricsData () {
149+ CollectRep .MetricsData mockMetricsData = Mockito .mock (CollectRep .MetricsData .class );
150+
151+ when (mockMetricsData .getId ()).thenReturn (0L );
152+ when (mockMetricsData .getMetrics ()).thenReturn ("cpu" );
153+ when (mockMetricsData .getTime ()).thenReturn (System .currentTimeMillis ());
154+ when (mockMetricsData .getCode ()).thenReturn (CollectRep .Code .SUCCESS );
155+ when (mockMetricsData .getApp ()).thenReturn ("app" );
156+ when (mockMetricsData .getInstance ()).thenReturn ("test-%server-01" );
157+
158+ CollectRep .ValueRow mockValueRow = Mockito .mock (CollectRep .ValueRow .class );
159+ List <String > columnValues = List .of ("test-%server-01" , "68.7" );
160+ when (mockValueRow .getColumnsList ()).thenReturn (columnValues );
161+ when (mockValueRow .getColumns (0 )).thenReturn ("test-%server-01" );
162+ when (mockValueRow .getColumns (1 )).thenReturn ("68.7" );
163+ List <CollectRep .ValueRow > mockValueRowsList = List .of (mockValueRow );
164+ when (mockMetricsData .getValues ()).thenReturn (mockValueRowsList );
165+
166+ CollectRep .Field instanceField = Mockito .mock (CollectRep .Field .class );
167+ when (instanceField .getName ()).thenReturn ("instance" );
168+ CollectRep .Field usageField = Mockito .mock (CollectRep .Field .class );
169+ when (usageField .getName ()).thenReturn ("usage" );
170+ CollectRep .Field systemField = Mockito .mock (CollectRep .Field .class );
171+ when (systemField .getName ()).thenReturn ("system" );
172+ List <CollectRep .Field > mockFields = List .of (instanceField , usageField , systemField );
173+ when (mockMetricsData .getFields ()).thenReturn (mockFields );
174+
175+ ArrowType instanceArrowType = new ArrowType .Utf8 ();
176+ FieldType instanceFieldType = new FieldType (true , instanceArrowType , null , null );
177+ Field instanceArrowField = new Field ("instance" , instanceFieldType , null );
178+ ArrowCell instanceCell = Mockito .mock (ArrowCell .class );
179+ when (instanceCell .getField ()).thenReturn (instanceArrowField );
180+ when (instanceCell .getValue ()).thenReturn ("test-%server-01" );
181+ when (instanceCell .getMetadataAsBoolean (MetricDataConstants .LABEL )).thenReturn (true );
182+ when (instanceCell .getMetadataAsByte (MetricDataConstants .TYPE )).thenReturn (CommonConstants .TYPE_STRING );
183+
184+ ArrowType usageArrowType = new ArrowType .Utf8 ();
185+ FieldType usageFieldType = new FieldType (true , usageArrowType , null , null );
186+ Field usageArrowField = new Field ("usage" , usageFieldType , null );
187+ ArrowCell usageCell = Mockito .mock (ArrowCell .class );
188+ when (usageCell .getField ()).thenReturn (usageArrowField );
189+ when (usageCell .getValue ()).thenReturn ("68.7" );
190+ when (usageCell .getMetadataAsBoolean (MetricDataConstants .LABEL )).thenReturn (false );
191+ when (usageCell .getMetadataAsByte (MetricDataConstants .TYPE )).thenReturn (CommonConstants .TYPE_NUMBER );
192+ List <ArrowCell > mockCells = List .of (instanceCell , usageCell );
193+
194+ // Create Arrow Field list for RowWrapper
195+ List <org .apache .arrow .vector .types .pojo .Field > arrowFields = List .of (instanceArrowField , usageArrowField );
196+
197+ RowWrapper mockRowWrapper = Mockito .mock (RowWrapper .class );
198+ when (mockRowWrapper .hasNextRow ()).thenReturn (true ).thenReturn (false );
199+ when (mockRowWrapper .nextRow ()).thenReturn (mockRowWrapper );
200+ when (mockRowWrapper .cellStream ()).thenAnswer (invocation -> mockCells .stream ());
201+ when (mockRowWrapper .getFieldList ()).thenReturn (arrowFields );
202+ when (mockMetricsData .readRow ()).thenReturn (mockRowWrapper );
203+ return mockMetricsData ;
204+ }
205+
52206}
0 commit comments