"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dbt_1 = require("./dbt");
const field_1 = require("./field");
const timeFrames_1 = require("./timeFrames");
describe('convertToAiHints', () => {
    it('normalizes strings and arrays of strings', () => {
        expect((0, dbt_1.convertToAiHints)('Use the canonical value.')).toEqual([
            'Use the canonical value.',
        ]);
        expect((0, dbt_1.convertToAiHints)(['First hint.', 'Second hint.'])).toEqual([
            'First hint.',
            'Second hint.',
        ]);
    });
    it('drops non-string values without discarding valid array entries', () => {
        expect((0, dbt_1.convertToAiHints)([
            'Valid hint.',
            { Formula: 'clicks + keys' },
            null,
            42,
            false,
        ])).toEqual(['Valid hint.']);
        expect((0, dbt_1.convertToAiHints)({ Formula: 'clicks + keys' })).toBeUndefined();
        expect((0, dbt_1.convertToAiHints)(42)).toBeUndefined();
        expect((0, dbt_1.convertToAiHints)(null)).toBeUndefined();
    });
    it('returns undefined when no usable hints remain', () => {
        expect((0, dbt_1.convertToAiHints)('')).toBeUndefined();
        expect((0, dbt_1.convertToAiHints)([])).toBeUndefined();
        expect((0, dbt_1.convertToAiHints)([{ Formula: 'clicks + keys' }])).toBeUndefined();
    });
    it('flattens only validated hints', () => {
        expect((0, dbt_1.flattenAiHints)(['First hint.', { invalid: true }, 'Second hint.'])).toBe('First hint. Second hint.');
    });
});
describe('getEffectiveFieldAiHints', () => {
    it('combines field and group hints without changing their source metadata', () => {
        const field = {
            aiHint: ['Use as the canonical value.', 'Shared guidance.'],
            groups: ['settlements', 'missing'],
        };
        const table = {
            groupDetails: {
                settlements: {
                    label: 'Settlements',
                    aiHint: [
                        'Shared guidance.',
                        'Use for settlement questions.',
                    ],
                },
            },
        };
        expect((0, dbt_1.getEffectiveFieldAiHints)(field, table)).toEqual([
            'Use as the canonical value.',
            'Shared guidance.',
            'Use for settlement questions.',
        ]);
        expect(field.aiHint).toEqual([
            'Use as the canonical value.',
            'Shared guidance.',
        ]);
    });
    it('ignores malformed field and group hints', () => {
        const field = {
            aiHint: [
                'Valid field hint.',
                { Formula: 'clicks + keys' },
            ],
            groups: ['settlements', 'broken'],
        };
        const table = {
            groupDetails: {
                settlements: {
                    label: 'Settlements',
                    aiHint: [
                        { invalid: true },
                        'Valid group hint.',
                    ],
                },
                broken: {
                    label: 'Broken',
                    aiHint: { invalid: true },
                },
            },
        };
        expect((0, dbt_1.getEffectiveFieldAiHints)(field, table)).toEqual([
            'Valid field hint.',
            'Valid group hint.',
        ]);
    });
});
const makeManifest = (nodes) => ({
    metadata: {
        dbt_schema_version: 'https://schemas.getdbt.com/dbt/manifest/v12.json',
        generated_at: '2024-01-01T00:00:00Z',
        adapter_type: 'postgres',
    },
    nodes,
    sources: {},
    macros: {},
    docs: {},
    exposures: {},
    metrics: {},
});
const baseModel = {
    package_name: 'test',
    path: 'model.sql',
    original_file_path: 'models/model.sql',
    resource_type: 'model',
    alias: 'model',
    checksum: { name: 'sha256', checksum: 'abc' },
    tags: [],
    refs: [],
    sources: [],
    depends_on: { macros: [], nodes: [] },
    database: 'db',
    schema: 'public',
    fqn: ['test', 'model'],
    raw_code: 'SELECT 1',
    columns: {},
    meta: {},
    description: '',
    created_at: 0,
    language: 'sql',
    relation_name: '"db"."public"."model"',
};
describe('getModelsFromManifest', () => {
    it('should include models with config.materialized set to table', () => {
        const manifest = makeManifest({
            'model.test.table_model': {
                ...baseModel,
                unique_id: 'model.test.table_model',
                name: 'table_model',
                config: { materialized: 'table' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(1);
        expect(models[0].unique_id).toBe('model.test.table_model');
    });
    it('should include models with config.materialized set to view', () => {
        const manifest = makeManifest({
            'model.test.view_model': {
                ...baseModel,
                unique_id: 'model.test.view_model',
                name: 'view_model',
                config: { materialized: 'view' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(1);
        expect(models[0].unique_id).toBe('model.test.view_model');
    });
    it('should include models without config.materialized (default materialization)', () => {
        const manifest = makeManifest({
            'model.test.default_model': {
                ...baseModel,
                unique_id: 'model.test.default_model',
                name: 'default_model',
                config: {},
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(1);
        expect(models[0].unique_id).toBe('model.test.default_model');
    });
    it('should filter out ephemeral models', () => {
        const manifest = makeManifest({
            'model.test.table_model': {
                ...baseModel,
                unique_id: 'model.test.table_model',
                name: 'table_model',
                config: { materialized: 'table' },
            },
            'model.test.ephemeral_model': {
                ...baseModel,
                unique_id: 'model.test.ephemeral_model',
                name: 'ephemeral_model',
                config: { materialized: 'ephemeral' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(1);
        expect(models[0].unique_id).toBe('model.test.table_model');
    });
    it('should filter out non-model nodes', () => {
        const manifest = makeManifest({
            'model.test.real_model': {
                ...baseModel,
                unique_id: 'model.test.real_model',
                name: 'real_model',
                config: { materialized: 'table' },
            },
            'test.test.some_test': {
                ...baseModel,
                unique_id: 'test.test.some_test',
                name: 'some_test',
                resource_type: 'test',
                config: { materialized: 'table' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(1);
        expect(models[0].unique_id).toBe('model.test.real_model');
    });
    it('should include a mix of tables, views, and models without materialized while excluding ephemeral', () => {
        const manifest = makeManifest({
            'model.test.table_model': {
                ...baseModel,
                unique_id: 'model.test.table_model',
                name: 'table_model',
                config: { materialized: 'table' },
            },
            'model.test.view_model': {
                ...baseModel,
                unique_id: 'model.test.view_model',
                name: 'view_model',
                config: { materialized: 'view' },
            },
            'model.test.no_mat_model': {
                ...baseModel,
                unique_id: 'model.test.no_mat_model',
                name: 'no_mat_model',
                config: {},
            },
            'model.test.ephemeral_model': {
                ...baseModel,
                unique_id: 'model.test.ephemeral_model',
                name: 'ephemeral_model',
                config: { materialized: 'ephemeral' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        const ids = models.map((m) => m.unique_id).sort();
        expect(ids).toEqual([
            'model.test.no_mat_model',
            'model.test.table_model',
            'model.test.view_model',
        ]);
    });
    it('should include seed nodes alongside models', () => {
        const manifest = makeManifest({
            'model.test.my_model': {
                ...baseModel,
                unique_id: 'model.test.my_model',
                name: 'my_model',
                config: { materialized: 'table' },
            },
            'seed.test.raw_plan': {
                ...baseModel,
                unique_id: 'seed.test.raw_plan',
                name: 'raw_plan',
                resource_type: 'seed',
                config: { materialized: 'seed' },
            },
        });
        const models = (0, dbt_1.getModelsFromManifest)(manifest);
        expect(models).toHaveLength(2);
        const ids = models.map((m) => m.unique_id).sort();
        expect(ids).toEqual(['model.test.my_model', 'seed.test.raw_plan']);
    });
});
describe('convertColumnMetric — baseDimensionType plumbing', () => {
    const base = {
        modelName: 'orders',
        dimensionName: 'order_date',
        dimensionSql: '${TABLE}.order_date',
        tableLabel: 'Orders',
    };
    it('sets baseDimensionType/Interval for a MIN/MAX that aggregates its own DATE column', () => {
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            name: 'max_order_date',
            metric: { type: field_1.MetricType.MAX },
            dimensionType: field_1.DimensionType.DATE,
            dimensionTimeInterval: timeFrames_1.TimeFrames.DAY,
        });
        expect(metric.baseDimensionType).toEqual(field_1.DimensionType.DATE);
        expect(metric.baseDimensionTimeInterval).toEqual(timeFrames_1.TimeFrames.DAY);
    });
    it('sets it when an explicit sql equals the column it is declared under', () => {
        // A `sql` equal to the column it aggregates is still a calendar DATE base.
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            name: 'max_order_date_explicit',
            metric: { type: field_1.MetricType.MAX, sql: '${TABLE}.order_date' },
            dimensionType: field_1.DimensionType.DATE,
            dimensionTimeInterval: timeFrames_1.TimeFrames.DAY,
        });
        expect(metric.baseDimensionType).toEqual(field_1.DimensionType.DATE);
        expect(metric.baseDimensionTimeInterval).toEqual(timeFrames_1.TimeFrames.DAY);
    });
    it('does NOT set it when the metric overrides sql to aggregate a different column', () => {
        // Declared under order_date (DATE) but actually MAX(id), so the column
        // type must not be inferred as the base.
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            name: 'max_id',
            metric: { type: field_1.MetricType.MAX, sql: '${TABLE}.id' },
            dimensionType: field_1.DimensionType.DATE,
            dimensionTimeInterval: timeFrames_1.TimeFrames.DAY,
        });
        expect(metric.baseDimensionType).toBeUndefined();
        expect(metric.baseDimensionTimeInterval).toBeUndefined();
    });
    it('does NOT set it for a non MIN/MAX metric', () => {
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            name: 'count_order_date',
            metric: { type: field_1.MetricType.COUNT },
            dimensionType: field_1.DimensionType.DATE,
            dimensionTimeInterval: timeFrames_1.TimeFrames.DAY,
        });
        expect(metric.baseDimensionType).toBeUndefined();
    });
    it('does NOT set it for a non-temporal base (e.g. MAX over a number)', () => {
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            dimensionName: 'amount',
            dimensionSql: '${TABLE}.amount',
            name: 'max_amount',
            metric: { type: field_1.MetricType.MAX },
            dimensionType: field_1.DimensionType.NUMBER,
        });
        expect(metric.baseDimensionType).toBeUndefined();
    });
    it('records a TIMESTAMP base for a MIN/MAX over its own TIMESTAMP column', () => {
        const metric = (0, dbt_1.convertColumnMetric)({
            ...base,
            dimensionName: 'created_at',
            dimensionSql: '${TABLE}.created_at',
            name: 'min_created_at',
            metric: { type: field_1.MetricType.MIN },
            dimensionType: field_1.DimensionType.TIMESTAMP,
        });
        expect(metric.baseDimensionType).toEqual(field_1.DimensionType.TIMESTAMP);
        expect(metric.baseDimensionTimeInterval).toBeUndefined();
    });
});
describe('convertModelMetric — base type plumbing', () => {
    it('leaves the base type undefined for a model-level metric (translator never passes one)', () => {
        // Model-level metrics call convertModelMetric directly with no base
        // dimension argument, so they never carry a base type.
        const metric = (0, dbt_1.convertModelMetric)({
            modelName: 'orders',
            name: 'max_created_at',
            metric: { type: field_1.MetricType.MAX, sql: '${TABLE}.created_at' },
            tableLabel: 'Orders',
        });
        expect(metric.baseDimensionType).toBeUndefined();
        expect(metric.baseDimensionTimeInterval).toBeUndefined();
    });
});
describe('patchPathParts', () => {
    it('parses the dbt-core format with project prefix', () => {
        expect((0, dbt_1.patchPathParts)('dbt_artifacts://models/dim_current_models.yml')).toEqual({
            project: 'dbt_artifacts',
            path: 'models/dim_current_models.yml',
        });
    });
    it('parses dbt-core paths with nested directories', () => {
        expect((0, dbt_1.patchPathParts)('my_project://models/sources/exposures.yml')).toEqual({
            project: 'my_project',
            path: 'models/sources/exposures.yml',
        });
    });
    it('parses the dbt-fusion format (no project prefix)', () => {
        expect((0, dbt_1.patchPathParts)('models/dim_current_models.yml')).toEqual({
            project: null,
            path: 'models/dim_current_models.yml',
        });
    });
    it('normalizes Windows backslashes from dbt-fusion on Windows', () => {
        expect((0, dbt_1.patchPathParts)('models\\dim_current_models.yml')).toEqual({
            project: null,
            path: 'models/dim_current_models.yml',
        });
        expect((0, dbt_1.patchPathParts)('models\\sources\\exposures.yml')).toEqual({
            project: null,
            path: 'models/sources/exposures.yml',
        });
    });
    it('preserves additional :// occurrences after the project separator', () => {
        expect((0, dbt_1.patchPathParts)('my_project://models/weird://name.yml')).toEqual({
            project: 'my_project',
            path: 'models/weird://name.yml',
        });
    });
});
//# sourceMappingURL=dbt.test.js.map