aopSlice.test.ts5.0 KBView on GitHub
/**
 * Tests for AopSlice — setAops (full replace, not merge), getAopById, getAops, isAopsLoaded
 */

import { act } from '@testing-library/react';
import { useCedarStore } from '@/modules/cedar-os/src/store/CedarStore';

// Minimal Aop shape compatible with the store type
const makeAop = (overrides: Partial<{ id: string; name: string }> = {}) =>
  ({
    id: 'aop-1',
    name: 'Test AOP',
    ...overrides,
  }) as Parameters<ReturnType<typeof useCedarStore.getState>['setAops']>[0][number];

const resetAops = () =>
  useCedarStore.setState((s) => ({
    ...s,
    aopsById: {},
    isAopsLoaded: false,
  }));

beforeEach(resetAops);
afterEach(resetAops);

// ---------------------------------------------------------------------------
// setAops
// ---------------------------------------------------------------------------

describe('setAops', () => {
  it('sets isAopsLoaded to true after first call', () => {
    act(() => useCedarStore.getState().setAops([]));
    expect(useCedarStore.getState().isAopsLoaded).toBe(true);
  });

  it('stores AOPs keyed by id', () => {
    const aop = makeAop({ id: 'aop-1' });
    act(() => useCedarStore.getState().setAops([aop]));
    expect(useCedarStore.getState().aopsById['aop-1']).toEqual(aop);
  });

  it('stores multiple AOPs', () => {
    const aops = [makeAop({ id: 'aop-1' }), makeAop({ id: 'aop-2', name: 'Second' })];
    act(() => useCedarStore.getState().setAops(aops));
    expect(Object.keys(useCedarStore.getState().aopsById)).toHaveLength(2);
  });

  it('completely replaces existing cache (does NOT merge)', () => {
    act(() => useCedarStore.getState().setAops([makeAop({ id: 'aop-old' })]));
    act(() => useCedarStore.getState().setAops([makeAop({ id: 'aop-new' })]));

    const state = useCedarStore.getState();
    expect(state.aopsById['aop-old']).toBeUndefined();
    expect(state.aopsById['aop-new']).toBeDefined();
  });

  it('clears all AOPs when called with empty array', () => {
    act(() => useCedarStore.getState().setAops([makeAop()]));
    act(() => useCedarStore.getState().setAops([]));
    expect(useCedarStore.getState().aopsById).toEqual({});
    expect(useCedarStore.getState().isAopsLoaded).toBe(true);
  });

  it('last entry wins when duplicate IDs provided', () => {
    const first = makeAop({ id: 'aop-1', name: 'First' });
    const second = makeAop({ id: 'aop-1', name: 'Second' });
    act(() => useCedarStore.getState().setAops([first, second]));
    expect(useCedarStore.getState().aopsById['aop-1'].name).toBe('Second');
  });
});

// ---------------------------------------------------------------------------
// getAopById
// ---------------------------------------------------------------------------

describe('getAopById', () => {
  it('returns the AOP with the given id', () => {
    const aop = makeAop({ id: 'aop-42' });
    act(() => useCedarStore.getState().setAops([aop]));
    expect(useCedarStore.getState().getAopById('aop-42')).toEqual(aop);
  });

  it('returns undefined for an unknown id', () => {
    act(() => useCedarStore.getState().setAops([makeAop({ id: 'aop-1' })]));
    expect(useCedarStore.getState().getAopById('does-not-exist')).toBeUndefined();
  });

  it('returns undefined when the cache is empty', () => {
    expect(useCedarStore.getState().getAopById('any-id')).toBeUndefined();
  });
});

// ---------------------------------------------------------------------------
// getAops
// ---------------------------------------------------------------------------

describe('getAops', () => {
  it('returns an empty array when no AOPs are loaded', () => {
    expect(useCedarStore.getState().getAops()).toEqual([]);
  });

  it('returns all stored AOPs as an array', () => {
    const aops = [makeAop({ id: 'a' }), makeAop({ id: 'b' }), makeAop({ id: 'c' })];
    act(() => useCedarStore.getState().setAops(aops));
    const result = useCedarStore.getState().getAops();
    expect(result).toHaveLength(3);
    expect(result.map((a) => a.id).sort()).toEqual(['a', 'b', 'c']);
  });

  it('reflects the latest setAops call (full replace)', () => {
    act(() => useCedarStore.getState().setAops([makeAop({ id: 'old' })]));
    act(() => useCedarStore.getState().setAops([makeAop({ id: 'new' })]));
    const result = useCedarStore.getState().getAops();
    expect(result).toHaveLength(1);
    expect(result[0].id).toBe('new');
  });
});

// ---------------------------------------------------------------------------
// isAopsLoaded flag
// ---------------------------------------------------------------------------

describe('isAopsLoaded', () => {
  it('starts as false', () => {
    expect(useCedarStore.getState().isAopsLoaded).toBe(false);
  });

  it('becomes true after setAops is called', () => {
    act(() => useCedarStore.getState().setAops([]));
    expect(useCedarStore.getState().isAopsLoaded).toBe(true);
  });

  it('stays true on subsequent setAops calls', () => {
    act(() => useCedarStore.getState().setAops([]));
    act(() => useCedarStore.getState().setAops([makeAop()]));
    expect(useCedarStore.getState().isAopsLoaded).toBe(true);
  });
});