import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { chunkLookerStagedDir } from '../../../../../src/context/ingest/adapters/looker/chunk.js'; import { writeLookerEvidenceDocuments } from '../../../../../src/context/ingest/adapters/looker/evidence-documents.js'; async function writeJson(stagedDir: string, relPath: string, value: unknown): Promise { const abs = join(stagedDir, relPath); await mkdir(join(abs, '..'), { recursive: true }); await writeFile(abs, `${JSON.stringify(value, null, 2)}\n`, 'utf-8'); } async function writeSmallFixture(stagedDir: string): Promise { await writeJson(stagedDir, 'sync-config.json', { lookerConnectionId: '11111111-1111-4111-8111-111111111111', fetchedAt: '2026-04-30T12:30:00.000Z', }); await writeJson(stagedDir, 'lookml_models.json', { models: [{ name: 'b2b', label: 'B2B', explores: [{ name: 'sales_pipeline', label: 'Sales Pipeline' }] }], }); await writeJson(stagedDir, 'explores/b2b/sales_pipeline.json', { modelName: 'b2b', exploreName: 'sales_pipeline', label: 'Sales Pipeline', description: null, fields: { dimensions: [{ name: 'opportunities.id' }], measures: [{ name: 'opportunities.arr' }] }, joins: [], }); await writeJson(stagedDir, 'dashboards/10.json', { lookerId: '10', title: 'Sales Pipeline', description: null, folderId: '7', ownerId: '3', updatedAt: '2026-04-30T12:00:00.000Z', tiles: [{ id: '100', title: 'ARR', lookId: null, query: { model: 'b2b', view: 'sales_pipeline' } }], }); await writeJson(stagedDir, 'looks/20.json', { lookerId: '20', title: 'Open Pipeline', description: null, folderId: '7', ownerId: '3', updatedAt: '2026-04-30T12:00:00.000Z', query: { model: 'b2b', view: 'sales_pipeline', fields: ['opportunities.arr'] }, }); await writeJson(stagedDir, 'folders/tree.json', { folders: [{ id: '7', name: 'Sandbox', parentId: null, path: ['Sandbox'] }], }); await writeJson(stagedDir, 'users/3.json', { id: '3', displayName: 'Ada Lovelace', email: null }); await writeJson(stagedDir, 'signals/dashboard_usage.json', [ { contentId: '10', queryCount30d: 50, uniqueUsers30d: 8 }, ]); await writeJson(stagedDir, 'signals/look_usage.json', [{ contentId: '20', queryCount30d: 20, uniqueUsers30d: 5 }]); await writeJson(stagedDir, 'signals/scheduled_plans.json', [ { contentId: '10', contentType: 'dashboard', isScheduled: true, scheduleCount: 1, recipientCount: 3 }, ]); await writeJson(stagedDir, 'signals/favorites.json', [ { contentId: '10', contentType: 'dashboard', favoriteCount: 4 }, ]); await writeLookerEvidenceDocuments(stagedDir); } describe('chunkLookerStagedDir', () => { let stagedDir: string; beforeEach(async () => { stagedDir = await mkdtemp(join(tmpdir(), 'looker-chunk-')); await writeSmallFixture(stagedDir); }); afterEach(async () => { await rm(stagedDir, { recursive: true, force: true }); }); it('emits one WU per explore, dashboard, and Look with readable dependencies', async () => { const result = await chunkLookerStagedDir(stagedDir); expect(result.reconcileNotes).toEqual([ expect.stringContaining('emit_artifact_resolution with actionType="subsumed"'), ]); expect(result.workUnits.map((wu) => wu.unitKey).sort()).toEqual([ 'looker-dashboard-10', 'looker-explore-b2b-sales_pipeline', 'looker-look-20', ]); const dashboard = result.workUnits.find((wu) => wu.unitKey === 'looker-dashboard-10'); expect(dashboard?.rawFiles).toEqual([ 'dashboards/10.json', 'evidence/dashboards/10/metadata.json', 'evidence/dashboards/10/page.md', ]); expect(dashboard?.notes).toContain('context_candidate_write'); expect(dashboard?.notes).not.toContain('wiki_write'); expect(dashboard?.dependencyPaths.sort()).toEqual([ 'explores/b2b/sales_pipeline.json', 'folders/tree.json', 'signals/dashboard_usage.json', 'signals/favorites.json', 'signals/scheduled_plans.json', 'users/3.json', ]); const explore = result.workUnits.find((wu) => wu.unitKey === 'looker-explore-b2b-sales_pipeline'); expect(explore?.rawFiles).toEqual([ 'explores/b2b/sales_pipeline.json', 'evidence/explores/b2b/sales_pipeline/metadata.json', 'evidence/explores/b2b/sales_pipeline/page.md', ]); expect(explore?.dependencyPaths).toEqual(['lookml_models.json']); }); it('keeps downstream dashboard and Look WUs when an explore dependency changes', async () => { const result = await chunkLookerStagedDir(stagedDir, { added: [], modified: ['explores/b2b/sales_pipeline.json'], deleted: [], unchanged: [ 'dashboards/10.json', 'looks/20.json', 'lookml_models.json', 'folders/tree.json', 'users/3.json', 'signals/dashboard_usage.json', 'signals/look_usage.json', 'signals/scheduled_plans.json', 'signals/favorites.json', ], }); expect(result.workUnits.map((wu) => wu.unitKey).sort()).toEqual([ 'looker-dashboard-10', 'looker-explore-b2b-sales_pipeline', 'looker-look-20', ]); expect(result.workUnits.find((wu) => wu.unitKey === 'looker-dashboard-10')?.rawFiles).toEqual([ 'dashboards/10.json', 'evidence/dashboards/10/metadata.json', 'evidence/dashboards/10/page.md', ]); }); it('returns an EvictionUnit for deleted runtime entity raw paths', async () => { const result = await chunkLookerStagedDir(stagedDir, { added: [], modified: [], deleted: ['looks/20.json'], unchanged: ['dashboards/10.json', 'explores/b2b/sales_pipeline.json'], }); expect(result.eviction).toEqual({ deletedRawPaths: ['looks/20.json'] }); }); });