/**
 * @module tests/unit/aggregation/materialize-failure-loud
 * @description Aggregation (Pattern C): a failed materialization must not be
 * swallowed. `scheduleMaterialize`'s debounced `materializeOne(key).catch(() => {})`
 * silently discarded the error, leaving the materialized Measurement entity stale
 * with no signal. It now emits a loud warning (the value is still rebuildable via
 * backfill-on-query, so it stays non-fatal — but visible). Load-bearing assertion:
 * prodLog.warn fires instead of an empty catch.
 */
import { describe, it, expect, vi, afterEach } from 'vitest'
import { AggregateMaterializer } from '../../../src/aggregation/materializer.js'
import { prodLog } from '../../../src/utils/logger.js'
import { NounType } from '../../../src/types/graphTypes.js'

describe('Aggregation — swallowed materialize failure is surfaced', () => {
  afterEach(() => {
    vi.restoreAllMocks()
    vi.useRealTimers()
  })

  it('logs loudly when materializeOne rejects', async () => {
    vi.useFakeTimers()
    const warn = vi.spyOn(prodLog, 'warn').mockImplementation(() => {})

    // Brain access whose add()/update() always reject → doMaterialize throws.
    const brain = {
      add: vi.fn().mockRejectedValue(new Error('storage down')),
      update: vi.fn().mockRejectedValue(new Error('storage down'))
    }
    const m = new AggregateMaterializer(brain as any, 10)
    const def: any = {
      name: 'sales',
      source: { type: NounType.Document },
      groupBy: ['region'],
      metrics: { total: { op: 'count' } },
      materialize: { debounceMs: 10 }
    }
    m.scheduleMaterialize(
      'sales',
      def,
      { region: 'us' },
      { groupKey: { region: 'us' }, metrics: { total: { count: 1 } } } as any
    )

    await vi.advanceTimersByTimeAsync(20) // fire the debounce timer
    await Promise.resolve()

    expect(warn).toHaveBeenCalled()
    expect(String(warn.mock.calls[0][0])).toMatch(/Failed to materialize aggregate group/)
  })
})
