"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dbt_1 = require("../types/dbt");
const field_1 = require("../types/field");
const timeFrames_1 = require("../types/timeFrames");
const timeFrames_2 = require("./timeFrames");
describe('TimeFrames', () => {
    describe('getSqlForTruncatedDate', () => {
        test('should get sql where stat of the week is Wednesday', async () => {
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.POSTGRES, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual("(DATE_TRUNC('WEEK', (${TABLE}.created - interval '2 days')) + interval '2 days')");
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.REDSHIFT, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual("(DATE_TRUNC('WEEK', (${TABLE}.created - interval '2 days')) + interval '2 days')");
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.DUCKDB, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual("(DATE_TRUNC('WEEK', (${TABLE}.created - interval '2 days')) + interval '2 days')");
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.BIGQUERY, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual('TIMESTAMP_TRUNC(${TABLE}.created, WEEK(WEDNESDAY))');
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.DATABRICKS, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual("DATEADD(DAY, 2, DATE_TRUNC('WEEK', DATEADD(DAY, -2, ${TABLE}.created)))");
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.SNOWFLAKE, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual("DATE_TRUNC('WEEK', ${TABLE}.created)");
            // ClickHouse: toStartOfWeek(date, 1) uses mode 1 (Monday base), matching Postgres DATE_TRUNC('week')
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.CLICKHOUSE, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.WEDNESDAY)).toEqual('addDays(toStartOfWeek(addDays(${TABLE}.created, -2), 1), 2)');
        });
        test('should get sql where start of the week is Monday for ClickHouse', () => {
            // Monday (startOfWeek=0): must pass mode 1 to toStartOfWeek() so it returns Monday not Sunday
            expect(timeFrames_2.timeFrameConfigs[timeFrames_1.TimeFrames.WEEK].getSql(dbt_1.SupportedDbtAdapter.CLICKHOUSE, timeFrames_1.TimeFrames.WEEK, '${TABLE}.created', field_1.DimensionType.TIMESTAMP, timeFrames_2.WeekDay.MONDAY)).toEqual('addDays(toStartOfWeek(addDays(${TABLE}.created, -0), 1), 0)');
        });
    });
    describe('getSqlForDatePart - DAY_OF_WEEK_INDEX with startOfWeek', () => {
        const col = '${TABLE}.created';
        const tf = timeFrames_1.TimeFrames.DAY_OF_WEEK_INDEX;
        const dt = field_1.DimensionType.DATE;
        test('should adjust day of week index for Monday start', () => {
            // BigQuery: native DAYOFWEEK 1=Sun..7=Sat, offset for Monday = 2
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.BIGQUERY, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(EXTRACT(DAYOFWEEK FROM ${col}) - 2 + 7, 7) + 1`);
            // PostgreSQL: native DOW 0=Sun..6=Sat, offset for Monday = 1
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.POSTGRES, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(CAST(DATE_PART('DOW', ${col}) AS INT) - 1 + 7, 7) + 1`);
            // Redshift uses same config as PostgreSQL
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.REDSHIFT, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(CAST(DATE_PART('DOW', ${col}) AS INT) - 1 + 7, 7) + 1`);
            // Databricks: native DOW 0=Sun..6=Sat, offset for Monday = 1
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.DATABRICKS, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(CAST(DATE_PART('DOW', ${col}) AS INT) - 1 + 7, 7) + 1`);
            // Trino: native DOW (ISO) 1=Mon..7=Sun, offset for Monday = 1
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.TRINO, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(EXTRACT(DOW FROM ${col}) - 1 + 7, 7) + 1`);
            // Athena uses Trino config
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.ATHENA, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`MOD(EXTRACT(DOW FROM ${col}) - 1 + 7, 7) + 1`);
            // ClickHouse: native toDayOfWeek (ISO) 1=Mon..7=Sun, offset for Monday = 1
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.CLICKHOUSE, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`modulo(toDayOfWeek(${col}) - 1 + 7, 7) + 1`);
        });
        test('should adjust day of week index for Sunday start', () => {
            // BigQuery: offset for Sunday = (6+1)%7+1 = 0+1 = 1
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.BIGQUERY, tf, col, dt, timeFrames_2.WeekDay.SUNDAY)).toEqual(`MOD(EXTRACT(DAYOFWEEK FROM ${col}) - 1 + 7, 7) + 1`);
            // PostgreSQL: offset for Sunday = (6+1)%7 = 0
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.POSTGRES, tf, col, dt, timeFrames_2.WeekDay.SUNDAY)).toEqual(`MOD(CAST(DATE_PART('DOW', ${col}) AS INT) - 0 + 7, 7) + 1`);
            // Trino: offset for Sunday = 6+1 = 7
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.TRINO, tf, col, dt, timeFrames_2.WeekDay.SUNDAY)).toEqual(`MOD(EXTRACT(DOW FROM ${col}) - 7 + 7, 7) + 1`);
            // ClickHouse: offset for Sunday = 6+1 = 7
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.CLICKHOUSE, tf, col, dt, timeFrames_2.WeekDay.SUNDAY)).toEqual(`modulo(toDayOfWeek(${col}) - 7 + 7, 7) + 1`);
        });
        test('should not adjust day of week index when startOfWeek is not set', () => {
            // Without startOfWeek, native behavior is preserved
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.BIGQUERY, tf, col, dt)).toEqual(`EXTRACT(DAYOFWEEK FROM ${col})`);
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.POSTGRES, tf, col, dt)).toEqual(`DATE_PART('DOW', ${col})`);
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.DUCKDB, tf, col, dt)).toEqual(`DATE_PART('DOW', ${col})`);
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.TRINO, tf, col, dt)).toEqual(`EXTRACT(DOW FROM ${col})`);
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.CLICKHOUSE, tf, col, dt)).toEqual(`toDayOfWeek(${col})`);
            // Snowflake: always uses session variable, never adjusts
            expect(timeFrames_2.timeFrameConfigs[tf].getSql(dbt_1.SupportedDbtAdapter.SNOWFLAKE, tf, col, dt, timeFrames_2.WeekDay.MONDAY)).toEqual(`DATE_PART('DOW', ${col})`);
        });
    });
    describe('isSubDayGranularity', () => {
        test('SUB_DAY_GRANULARITIES matches timeFrameConfigs that produce TIMESTAMP', () => {
            // Map from DateGranularity to TimeFrames for the sub-day ones
            const subDayTimeFrames = {
                [timeFrames_1.DateGranularity.SECOND]: timeFrames_1.TimeFrames.SECOND,
                [timeFrames_1.DateGranularity.MINUTE]: timeFrames_1.TimeFrames.MINUTE,
                [timeFrames_1.DateGranularity.HOUR]: timeFrames_1.TimeFrames.HOUR,
            };
            for (const g of timeFrames_2.SUB_DAY_GRANULARITIES) {
                const tf = subDayTimeFrames[g];
                const dimType = timeFrames_2.timeFrameConfigs[tf].getDimensionType(field_1.DimensionType.DATE);
                expect(dimType).toBe(field_1.DimensionType.TIMESTAMP);
            }
        });
        test('standard granularities not in SUB_DAY produce DATE', () => {
            const dayAndAbove = [
                {
                    granularity: timeFrames_1.DateGranularity.DAY,
                    timeFrame: timeFrames_1.TimeFrames.DAY,
                },
                {
                    granularity: timeFrames_1.DateGranularity.WEEK,
                    timeFrame: timeFrames_1.TimeFrames.WEEK,
                },
                {
                    granularity: timeFrames_1.DateGranularity.MONTH,
                    timeFrame: timeFrames_1.TimeFrames.MONTH,
                },
                {
                    granularity: timeFrames_1.DateGranularity.QUARTER,
                    timeFrame: timeFrames_1.TimeFrames.QUARTER,
                },
                {
                    granularity: timeFrames_1.DateGranularity.YEAR,
                    timeFrame: timeFrames_1.TimeFrames.YEAR,
                },
            ];
            for (const { granularity, timeFrame } of dayAndAbove) {
                expect((0, timeFrames_2.isSubDayGranularity)(granularity)).toBe(false);
                const dimType = timeFrames_2.timeFrameConfigs[timeFrame].getDimensionType(field_1.DimensionType.DATE);
                expect(dimType).toBe(field_1.DimensionType.DATE);
            }
        });
        test('identifies sub-day granularities correctly', () => {
            expect((0, timeFrames_2.isSubDayGranularity)(timeFrames_1.DateGranularity.SECOND)).toBe(true);
            expect((0, timeFrames_2.isSubDayGranularity)(timeFrames_1.DateGranularity.MINUTE)).toBe(true);
            expect((0, timeFrames_2.isSubDayGranularity)(timeFrames_1.DateGranularity.HOUR)).toBe(true);
            expect((0, timeFrames_2.isSubDayGranularity)(timeFrames_1.DateGranularity.DAY)).toBe(false);
            expect((0, timeFrames_2.isSubDayGranularity)(timeFrames_1.DateGranularity.YEAR)).toBe(false);
        });
    });
    describe('getDateDimension', () => {
        test('should parse dates', async () => {
            // Invalid date granularities
            expect((0, timeFrames_2.getDateDimension)('dimension')).toEqual({});
            expect((0, timeFrames_2.getDateDimension)('dimension_date')).toEqual({});
            expect((0, timeFrames_2.getDateDimension)('dimension_raw')).toEqual({});
            // Valid date granularities
            expect((0, timeFrames_2.getDateDimension)('dimension_second')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.SECOND,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_minute')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.MINUTE,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_hour')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.HOUR,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_day')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.DAY,
            });
            expect((0, timeFrames_2.getDateDimension)('table_my_dimension_day')).toEqual({
                baseDimensionId: `table_my_dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.DAY,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_week')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.WEEK,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_month')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.MONTH,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_quarter')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.QUARTER,
            });
            expect((0, timeFrames_2.getDateDimension)('dimension_year')).toEqual({
                baseDimensionId: `dimension`,
                newTimeFrame: timeFrames_1.TimeFrames.YEAR,
            });
        });
    });
    describe('dateTruncTimezoneConversions', () => {
        // Trino's client serializes `timestamp with time zone` as
        // "YYYY-MM-DD HH:mm:ss.SSS <zone>", which dayjs/moment can't parse.
        // `toUTC` must cast back to a naive `timestamp` so downstream parsing
        // works.
        test('Trino toUTC returns a naive timestamp at the correct UTC instant', () => {
            const { toUTC } = timeFrames_2.dateTruncTimezoneConversions[dbt_1.SupportedDbtAdapter.TRINO];
            expect(toUTC('truncated', 'America/New_York')).toEqual(`CAST(with_timezone(truncated, 'America/New_York') AT TIME ZONE 'UTC' AS timestamp)`);
        });
        test('Trino toProjectTz returns a naive timestamp in the project zone', () => {
            const { toProjectTz } = timeFrames_2.dateTruncTimezoneConversions[dbt_1.SupportedDbtAdapter.TRINO];
            expect(toProjectTz('column', 'America/New_York')).toEqual(`CAST(column AT TIME ZONE 'America/New_York' AS timestamp)`);
        });
        test('Athena mirrors Trino', () => {
            const trino = timeFrames_2.dateTruncTimezoneConversions[dbt_1.SupportedDbtAdapter.TRINO];
            const athena = timeFrames_2.dateTruncTimezoneConversions[dbt_1.SupportedDbtAdapter.ATHENA];
            expect(athena.toUTC('x', 'UTC')).toEqual(trino.toUTC('x', 'UTC'));
            expect(athena.toProjectTz('x', 'UTC')).toEqual(trino.toProjectTz('x', 'UTC'));
        });
        test('ClickHouse toUTC lifts Date-returning truncs into DateTime before relabeling', () => {
            const { toUTC } = timeFrames_2.dateTruncTimezoneConversions[dbt_1.SupportedDbtAdapter.CLICKHOUSE];
            expect(toUTC('truncated', 'Asia/Tokyo')).toEqual(`toTimeZone(toDateTime(truncated, 'Asia/Tokyo'), 'UTC')`);
        });
    });
    describe('getSqlForTruncatedDate type guard for DATE', () => {
        const tz = 'America/New_York';
        const col = 'my_col';
        test.each([
            [dbt_1.SupportedDbtAdapter.POSTGRES, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.REDSHIFT, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.DUCKDB, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.SNOWFLAKE, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.DATABRICKS, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.TRINO, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.ATHENA, `DATE_TRUNC('DAY', ${col})`],
            [dbt_1.SupportedDbtAdapter.CLICKHOUSE, `toStartOfDay(${col})`],
        ])('%s: DATE dimension with timezone skips tz round-trip', (adapter, expected) => {
            expect((0, timeFrames_2.getSqlForTruncatedDate)(adapter, timeFrames_1.TimeFrames.DAY, col, field_1.DimensionType.DATE, null, tz)).toEqual(expected);
        });
        test('TIMESTAMP dimension with timezone still gets tz round-trip (Postgres)', () => {
            expect((0, timeFrames_2.getSqlForTruncatedDate)(dbt_1.SupportedDbtAdapter.POSTGRES, timeFrames_1.TimeFrames.DAY, col, field_1.DimensionType.TIMESTAMP, null, tz)).toEqual(`(DATE_TRUNC('DAY', (${col})::timestamptz AT TIME ZONE '${tz}')) AT TIME ZONE '${tz}'`);
        });
        test('TIMESTAMP dimension with timezone still gets tz round-trip (Snowflake)', () => {
            expect((0, timeFrames_2.getSqlForTruncatedDate)(dbt_1.SupportedDbtAdapter.SNOWFLAKE, timeFrames_1.TimeFrames.DAY, col, field_1.DimensionType.TIMESTAMP, null, tz)).toEqual(`CONVERT_TIMEZONE('${tz}', 'UTC', DATE_TRUNC('DAY', CONVERT_TIMEZONE('UTC', '${tz}', ${col})))`);
        });
    });
});
//# sourceMappingURL=timeFrames.test.js.map