|
1 | 1 | /* eslint-disable global-require */ |
2 | 2 | /* globals describe, jest, beforeEach, test, expect */ |
3 | 3 | import R from 'ramda'; |
| 4 | +import { |
| 5 | + BUILD_RANGE_END_LOCAL, |
| 6 | + BUILD_RANGE_START_LOCAL, |
| 7 | + FROM_PARTITION_RANGE, |
| 8 | + TO_PARTITION_RANGE |
| 9 | +} from '@cubejs-backend/shared'; |
4 | 10 | import { PreAggregationPartitionRangeLoader, PreAggregations } from '../../src'; |
5 | 11 |
|
6 | 12 | class MockDriver { |
@@ -67,6 +73,43 @@ class MockDriver { |
67 | 73 | } |
68 | 74 | } |
69 | 75 |
|
| 76 | +const mockPreAggregation = (overrides = {}) => ({ |
| 77 | + tableName: 'test_table', |
| 78 | + partitionGranularity: 'day', |
| 79 | + timezone: 'UTC', |
| 80 | + timestampFormat: 'YYYY-MM-DDTHH:mm:ss.SSS', |
| 81 | + timestampPrecision: 3, |
| 82 | + dataSource: 'default', |
| 83 | + preAggregationStartEndQueries: [ |
| 84 | + ['SELECT MIN(ts)', [], {}], |
| 85 | + ['SELECT MAX(ts)', [], {}] |
| 86 | + ], |
| 87 | + loadSql: ['SELECT * FROM test_table WHERE ts >= ? and ts < ?', [FROM_PARTITION_RANGE, TO_PARTITION_RANGE]], |
| 88 | + ...overrides, |
| 89 | +}); |
| 90 | + |
| 91 | +const createLoader = (overrides = {}, options = {}) => { |
| 92 | + const loader = new PreAggregationPartitionRangeLoader( |
| 93 | + {}, // driverFactory |
| 94 | + {}, // logger |
| 95 | + { options: {} }, // queryCache |
| 96 | + {}, // preAggregations |
| 97 | + mockPreAggregation(overrides), |
| 98 | + [], // preAggregationsTablesToTempTables |
| 99 | + {}, // loadCache |
| 100 | + options, |
| 101 | + ); |
| 102 | + |
| 103 | + jest.spyOn(loader, 'loadRangeQuery').mockImplementation(async (query, _partitionRange) => { |
| 104 | + if (query[0].includes('MIN')) { |
| 105 | + return [{ value: '2024-01-01T00:00:00.000' }]; |
| 106 | + } |
| 107 | + return [{ value: '2024-01-03T23:59:59.999' }]; |
| 108 | + }); |
| 109 | + |
| 110 | + return loader; |
| 111 | +}; |
| 112 | + |
70 | 113 | describe('PreAggregations', () => { |
71 | 114 | let mockDriver = null; |
72 | 115 | let mockExternalDriver = null; |
@@ -434,4 +477,218 @@ describe('PreAggregations', () => { |
434 | 477 | )).toThrow('Date range expected to be in YYYY-MM-DDTHH:mm:ss.SSS format'); |
435 | 478 | }); |
436 | 479 | }); |
| 480 | + |
| 481 | + describe('partitionTableName', () => { |
| 482 | + test('should generate correct table names for different granularities', () => { |
| 483 | + const testDateRange = ['2024-01-05T12:34:56.789', '2024-01-05T23:59:59.999']; |
| 484 | + |
| 485 | + // Daily granularity |
| 486 | + expect(PreAggregationPartitionRangeLoader.partitionTableName( |
| 487 | + 'test_table', |
| 488 | + 'day', |
| 489 | + testDateRange |
| 490 | + )).toBe('test_table20240105'); |
| 491 | + |
| 492 | + // Hourly granularity |
| 493 | + expect(PreAggregationPartitionRangeLoader.partitionTableName( |
| 494 | + 'test_table', |
| 495 | + 'hour', |
| 496 | + testDateRange |
| 497 | + )).toBe('test_table2024010512'); |
| 498 | + |
| 499 | + // Minute granularity |
| 500 | + expect(PreAggregationPartitionRangeLoader.partitionTableName( |
| 501 | + 'test_table', |
| 502 | + 'minute', |
| 503 | + testDateRange |
| 504 | + )).toBe('test_table202401051234'); |
| 505 | + }); |
| 506 | + }); |
| 507 | + |
| 508 | + describe('replaceQueryBuildRangeParams', () => { |
| 509 | + test('should replace BUILD_RANGE params with actual dates', async () => { |
| 510 | + const loader = createLoader(); |
| 511 | + jest.spyOn(loader, 'loadBuildRange').mockResolvedValue([ |
| 512 | + '2023-01-01T00:00:00.000', |
| 513 | + '2023-01-31T23:59:59.999', |
| 514 | + ]); |
| 515 | + |
| 516 | + const result = await loader.replaceQueryBuildRangeParams([ |
| 517 | + 'other_param_that_should_not_be_modified', |
| 518 | + BUILD_RANGE_START_LOCAL, |
| 519 | + BUILD_RANGE_END_LOCAL, |
| 520 | + ]); |
| 521 | + |
| 522 | + expect(result).toEqual([ |
| 523 | + 'other_param_that_should_not_be_modified', |
| 524 | + '2023-01-01T00:00:00.000', |
| 525 | + '2023-01-31T23:59:59.999', |
| 526 | + ]); |
| 527 | + }); |
| 528 | + |
| 529 | + test('should return null when no BUILD_RANGE params', async () => { |
| 530 | + const loader = createLoader(); |
| 531 | + const result = await loader.replaceQueryBuildRangeParams(['param1', 'param2']); |
| 532 | + expect(result).toBeNull(); |
| 533 | + }); |
| 534 | + }); |
| 535 | + |
| 536 | + describe('partitionPreAggregations', () => { |
| 537 | + test('should construct correct partitionPreAggregations for dateRange in UTC', async () => { |
| 538 | + const loader = createLoader({ |
| 539 | + timezone: 'UTC', |
| 540 | + }); |
| 541 | + |
| 542 | + const results = await loader.partitionPreAggregations(); |
| 543 | + expect(results.length).toEqual(3); |
| 544 | + |
| 545 | + let [preAggDesc] = results; |
| 546 | + expect(preAggDesc.tableName).toEqual('test_table20240101'); // Partition tables are the same for all time zones |
| 547 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-01T00:00:00.000'); // buildRange is the same for all time zones |
| 548 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-01T23:59:59.999'); |
| 549 | + expect(preAggDesc.loadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 550 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-01T00:00:00.000'); |
| 551 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-01T23:59:59.999'); |
| 552 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 553 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-01T00:00:00.000'); |
| 554 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-01T23:59:59.999'); |
| 555 | + |
| 556 | + [, preAggDesc] = results; |
| 557 | + expect(preAggDesc.tableName).toEqual('test_table20240102'); |
| 558 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-02T00:00:00.000'); |
| 559 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-02T23:59:59.999'); |
| 560 | + expect(preAggDesc.loadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 561 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-02T00:00:00.000'); |
| 562 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-02T23:59:59.999'); |
| 563 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 564 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-02T00:00:00.000'); |
| 565 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-02T23:59:59.999'); |
| 566 | + |
| 567 | + [,, preAggDesc] = results; |
| 568 | + expect(preAggDesc.tableName).toEqual('test_table20240103'); |
| 569 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-03T00:00:00.000'); |
| 570 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-03T23:59:59.999'); |
| 571 | + expect(preAggDesc.loadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 572 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-03T00:00:00.000'); |
| 573 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-03T23:59:59.999'); |
| 574 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 575 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-03T00:00:00.000'); |
| 576 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-03T23:59:59.999'); |
| 577 | + }); |
| 578 | + |
| 579 | + test('should construct correct partitionPreAggregations for dateRange in America/New_York', async () => { |
| 580 | + const loader = createLoader({ |
| 581 | + timezone: 'America/New_York', // UTC-5 |
| 582 | + }); |
| 583 | + |
| 584 | + const results = await loader.partitionPreAggregations(); |
| 585 | + expect(results.length).toEqual(3); |
| 586 | + |
| 587 | + let [preAggDesc] = results; |
| 588 | + expect(preAggDesc.tableName).toEqual('test_table20240101'); // Partition tables are the same for all time zones |
| 589 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-01T00:00:00.000'); // buildRange is the same for all time zones |
| 590 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-01T23:59:59.999'); |
| 591 | + expect(preAggDesc.loadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 592 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-01T05:00:00.000'); |
| 593 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-02T04:59:59.999'); |
| 594 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 595 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-01T05:00:00.000'); |
| 596 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-02T04:59:59.999'); |
| 597 | + |
| 598 | + [, preAggDesc] = results; |
| 599 | + expect(preAggDesc.tableName).toEqual('test_table20240102'); |
| 600 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-02T00:00:00.000'); |
| 601 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-02T23:59:59.999'); |
| 602 | + expect(preAggDesc.loadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 603 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-02T05:00:00.000'); |
| 604 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-03T04:59:59.999'); |
| 605 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 606 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-02T05:00:00.000'); |
| 607 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-03T04:59:59.999'); |
| 608 | + |
| 609 | + [,, preAggDesc] = results; |
| 610 | + expect(preAggDesc.tableName).toEqual('test_table20240103'); |
| 611 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-03T00:00:00.000'); |
| 612 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-03T23:59:59.999'); |
| 613 | + expect(preAggDesc.loadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 614 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-03T05:00:00.000'); |
| 615 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-04T04:59:59.999'); |
| 616 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 617 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-03T05:00:00.000'); |
| 618 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-04T04:59:59.999'); |
| 619 | + }); |
| 620 | + |
| 621 | + test('should construct correct partitionPreAggregations for dateRange in Asia/Tokyo', async () => { |
| 622 | + const loader = createLoader({ |
| 623 | + timezone: 'Asia/Tokyo', // UTC+9 |
| 624 | + }); |
| 625 | + |
| 626 | + const results = await loader.partitionPreAggregations(); |
| 627 | + expect(results.length).toEqual(3); |
| 628 | + |
| 629 | + let [preAggDesc] = results; |
| 630 | + expect(preAggDesc.tableName).toEqual('test_table20240101'); // Partition tables are the same for all time zones |
| 631 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-01T00:00:00.000'); // buildRange is the same for all time zones |
| 632 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-01T23:59:59.999'); |
| 633 | + expect(preAggDesc.loadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 634 | + expect(preAggDesc.loadSql[1][0]).toEqual('2023-12-31T15:00:00.000'); |
| 635 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-01T14:59:59.999'); |
| 636 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240101')).toBeTruthy(); |
| 637 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2023-12-31T15:00:00.000'); |
| 638 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-01T14:59:59.999'); |
| 639 | + |
| 640 | + [, preAggDesc] = results; |
| 641 | + expect(preAggDesc.tableName).toEqual('test_table20240102'); |
| 642 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-02T00:00:00.000'); |
| 643 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-02T23:59:59.999'); |
| 644 | + expect(preAggDesc.loadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 645 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-01T15:00:00.000'); |
| 646 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-02T14:59:59.999'); |
| 647 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240102')).toBeTruthy(); |
| 648 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-01T15:00:00.000'); |
| 649 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-02T14:59:59.999'); |
| 650 | + |
| 651 | + [,, preAggDesc] = results; |
| 652 | + expect(preAggDesc.tableName).toEqual('test_table20240103'); |
| 653 | + expect(preAggDesc.buildRangeStart).toEqual('2024-01-03T00:00:00.000'); |
| 654 | + expect(preAggDesc.buildRangeEnd).toEqual('2024-01-03T23:59:59.999'); |
| 655 | + expect(preAggDesc.loadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 656 | + expect(preAggDesc.loadSql[1][0]).toEqual('2024-01-02T15:00:00.000'); |
| 657 | + expect(preAggDesc.loadSql[1][1]).toEqual('2024-01-03T14:59:59.999'); |
| 658 | + expect(preAggDesc.structureVersionLoadSql[0].includes('test_table20240103')).toBeTruthy(); |
| 659 | + expect(preAggDesc.structureVersionLoadSql[1][0]).toEqual('2024-01-02T15:00:00.000'); |
| 660 | + expect(preAggDesc.structureVersionLoadSql[1][1]).toEqual('2024-01-03T14:59:59.999'); |
| 661 | + }); |
| 662 | + }); |
| 663 | + |
| 664 | + describe('partitionPreAggregations', () => { |
| 665 | + test('should generate partitioned pre-aggregations', async () => { |
| 666 | + const compilerCacheFn = jest.fn((subKey, fn) => fn()); |
| 667 | + const loader = createLoader( |
| 668 | + { |
| 669 | + partitionGranularity: 'day', |
| 670 | + matchedTimeDimensionDateRange: ['2023-01-01T00:00:00.000', '2023-01-02T23:59:59.999'], |
| 671 | + }, |
| 672 | + { compilerCacheFn } |
| 673 | + ); |
| 674 | + |
| 675 | + jest.spyOn(loader, 'partitionRanges').mockResolvedValue({ |
| 676 | + buildRange: ['2023-01-01T00:00:00.000', '2023-01-02T23:59:59.999'], |
| 677 | + partitionRanges: [ |
| 678 | + ['2023-01-01T00:00:00.000', '2023-01-01T23:59:59.999'], |
| 679 | + ['2023-01-02T00:00:00.000', '2023-01-02T23:59:59.999'], |
| 680 | + ], |
| 681 | + }); |
| 682 | + |
| 683 | + const result = await loader.partitionPreAggregations(); |
| 684 | + |
| 685 | + expect(result.length).toBe(2); |
| 686 | + expect(result[0].tableName).toMatch(/test_table20230101/); |
| 687 | + expect(result[1].tableName).toMatch(/test_table20230102/); |
| 688 | + expect(compilerCacheFn).toHaveBeenCalledWith( |
| 689 | + ['partitions', JSON.stringify(['2023-01-01T00:00:00.000', '2023-01-02T23:59:59.999'])], |
| 690 | + expect.any(Function) |
| 691 | + ); |
| 692 | + }); |
| 693 | + }); |
437 | 694 | }); |
0 commit comments