@@ -10,7 +10,7 @@ import com.digitalasset.canton.resource.DbStorage
1010import com .digitalasset .canton .topology .PartyId
1111import com .digitalasset .daml .lf .data .Time .Timestamp as LfTimestamp
1212import com .google .cloud .bigquery as bq
13- import bq .{Field , JobInfo , Schema , TableId }
13+ import bq .{Field , FieldValueList , JobInfo , Schema , TableId , TableResult }
1414import bq .storage .v1 .{JsonStreamWriter , TableSchema }
1515import slick .jdbc .canton .ActionBasedSQLInterpolation .Implicits .*
1616import slick .jdbc .GetResult
@@ -83,6 +83,7 @@ class ScanTotalSupplyBigQueryIntegrationTest
8383 // The peak is 17 transactions in a (simulated) minute, or 0.28333 tps over a minute,
8484 // so we assert 15-20 transactions, or 0.25-0.34 tps
8585 private val peakTps = (0.25 , 0.34 )
86+ private val totalRounds = 4
8687
8788 override def beforeAll () = {
8889 super .beforeAll()
@@ -152,12 +153,14 @@ class ScanTotalSupplyBigQueryIntegrationTest
152153 createBigQueryFunctions()
153154 }
154155
155- val results = withClue(" running total supply queries in BigQuery" ) {
156- runTotalSupplyQueries()
156+ withClue(" testing total supply queries in BigQuery" ) {
157+ val results = runDashboardQueries()
158+ verifyDashboardResults(results)
157159 }
158160
159- withClue(s " verify total supply results " ) {
160- verifyResults(results)
161+ withClue(" testing finance queries" ) {
162+ val results = runFinanceQueries()
163+ verifyFinanceResults(results)
161164 }
162165 }
163166
@@ -523,16 +526,31 @@ class ScanTotalSupplyBigQueryIntegrationTest
523526 job.waitFor()
524527 }
525528
526- /** Runs the total supply queries from the SQL file
529+ private def runFinanceQueries ()(implicit env : FixtureParam ): FinanceMetrics = {
530+ val project = bigquery.getOptions.getProjectId
531+ // The TPS query assumes staleness of up to 4 hours, so we query for stats 5 hours after the current ledger time.
532+ val timestamp = getLedgerTime.toInstant.plus(5 , ChronoUnit .HOURS ).toString
533+ logger.info(s " Querying all dashboard stats as of $timestamp" )
534+ val sql =
535+ s " SELECT * FROM ` $project. $functionsDatasetName.all_finance_stats`(' $timestamp', 0); "
536+
537+ parseFinanceResults(runTableSqlQuery(sql))
538+ }
539+
540+ /** Runs the dashboard queries from the SQL file
527541 */
528- private def runTotalSupplyQueries ()(implicit env : FixtureParam ): ExpectedMetrics = {
542+ private def runDashboardQueries ()(implicit env : FixtureParam ): DashboardMetrics = {
529543 val project = bigquery.getOptions.getProjectId
530544 // The TPS query assumes staleness of up to 4 hours, so we query for stats 5 hours after the current ledger time.
531545 val timestamp = getLedgerTime.toInstant.plus(5 , ChronoUnit .HOURS ).toString
546+ logger.info(s " Querying all dashboard stats as of $timestamp" )
532547 val sql =
533548 s " SELECT * FROM ` $project. $functionsDatasetName.all_dashboard_stats`(' $timestamp', 0); "
534549
535- logger.info(s " Querying all stats as of $timestamp" )
550+ parseDashboardResults(runTableSqlQuery(sql))
551+ }
552+
553+ private def runTableSqlQuery (sql : String ): TableResult = {
536554
537555 // Execute the query
538556 val queryConfig = bq.QueryJobConfiguration
@@ -549,11 +567,15 @@ class ScanTotalSupplyBigQueryIntegrationTest
549567 job.waitFor()
550568
551569 // results should be available now
552- val result = job.getQueryResults()
553- parseQueryResults(result)
570+ job.getQueryResults()
554571 }
555572
556- private case class ExpectedMetrics (
573+ private case class FinanceMetrics (
574+ // Most metrics reuse the same code as the dasboard computation, so we don't bother validating them again
575+ latestRound : Long
576+ )
577+
578+ private case class DashboardMetrics (
557579 locked : BigDecimal ,
558580 unlocked : BigDecimal ,
559581 currentSupplyTotal : BigDecimal ,
@@ -572,51 +594,64 @@ class ScanTotalSupplyBigQueryIntegrationTest
572594 avgCoinPrice : BigDecimal ,
573595 )
574596
575- private def parseQueryResults (result : bq.TableResult ) = {
576- // We expect the final query to return a single row with all metrics
577- val row = result.iterateAll().iterator().next()
578- logger.debug(s " Query row: $row; schema ${result.getSchema}" )
597+ private def required (row : FieldValueList , column : String ) = {
598+ val field = row get column
599+ if (field.isNull)
600+ fail(s " Column ' $column' in all-stats results is null " )
601+ field
602+ }
579603
580- def required (column : String ) = {
581- val field = row get column
582- if (field.isNull)
583- fail(s " Column ' $column' in all-stats results is null " )
584- field
585- }
604+ def bd (row : FieldValueList , column : String ) = {
605+ BigDecimal (required(row, column).getStringValue)
606+ }
586607
587- def bd ( column : String ) = {
588- BigDecimal ( required(column).getStringValue)
589- }
608+ def int ( row : FieldValueList , column : String ) = {
609+ required(row, column).getLongValue
610+ }
590611
591- def int ( column : String ) = {
592- required(column).getLongValue
593- }
612+ def float ( row : FieldValueList , column : String ) = {
613+ required(row, column).getDoubleValue
614+ }
594615
595- def float (column : String ) = {
596- required(column).getDoubleValue
597- }
616+ private def parseFinanceResults (result : bq.TableResult ) = {
617+ val row = result.iterateAll().iterator().next()
618+ logger.debug(s " Query row: $row; schema ${result.getSchema}" )
619+
620+ FinanceMetrics (
621+ latestRound = int(row, " latest_round" )
622+ )
623+ }
624+
625+ private def parseDashboardResults (result : bq.TableResult ) = {
626+ // We expect the final query to return a single row with all metrics
627+ val row = result.iterateAll().iterator().next()
628+ logger.debug(s " Query row: $row; schema ${result.getSchema}" )
598629
599- ExpectedMetrics (
600- locked = bd(" locked" ),
601- unlocked = bd(" unlocked" ),
602- currentSupplyTotal = bd(" current_supply_total" ),
603- unminted = bd(" unminted" ),
604- mintedAppRewards = bd(" daily_mint_app_rewards" ),
605- mintedValidatorRewards = bd(" daily_mint_validator_rewards" ),
606- mintedSvRewards = bd(" daily_mint_sv_rewards" ),
607- mintedUnclaimed = bd(" daily_mint_unclaimed_activity_records" ),
608- burned = bd(" daily_burn" ),
609- numAmuletHolders = int(" num_amulet_holders" ),
610- numActiveValidators = int(" num_active_validators" ),
611- avgTps = float(" average_tps" ),
612- peakTps = float(" peak_tps" ),
613- minCoinPrice = bd(" daily_min_coin_price" ),
614- maxCoinPrice = bd(" daily_max_coin_price" ),
615- avgCoinPrice = bd(" daily_avg_coin_price" ),
630+ DashboardMetrics (
631+ locked = bd(row, " locked" ),
632+ unlocked = bd(row, " unlocked" ),
633+ currentSupplyTotal = bd(row, " current_supply_total" ),
634+ unminted = bd(row, " unminted" ),
635+ mintedAppRewards = bd(row, " daily_mint_app_rewards" ),
636+ mintedValidatorRewards = bd(row, " daily_mint_validator_rewards" ),
637+ mintedSvRewards = bd(row, " daily_mint_sv_rewards" ),
638+ mintedUnclaimed = bd(row, " daily_mint_unclaimed_activity_records" ),
639+ burned = bd(row, " daily_burn" ),
640+ numAmuletHolders = int(row, " num_amulet_holders" ),
641+ numActiveValidators = int(row, " num_active_validators" ),
642+ avgTps = float(row, " average_tps" ),
643+ peakTps = float(row, " peak_tps" ),
644+ minCoinPrice = bd(row, " daily_min_coin_price" ),
645+ maxCoinPrice = bd(row, " daily_max_coin_price" ),
646+ avgCoinPrice = bd(row, " daily_avg_coin_price" ),
616647 )
617648 }
618649
619- private def verifyResults (results : ExpectedMetrics ): Unit = {
650+ private def verifyFinanceResults (results : FinanceMetrics ): Unit = {
651+ results.latestRound shouldBe totalRounds withClue " total_rounds"
652+ }
653+
654+ private def verifyDashboardResults (results : DashboardMetrics ): Unit = {
620655 // Verify individual metrics
621656 forEvery(
622657 Seq (
0 commit comments