import { KetchupSynced } from '.' interface TTTState { board: ('X' | 'O' | undefined)[][] } const tttInitial = { board: [ [undefined, undefined, undefined], [undefined, undefined, undefined], [undefined, undefined, undefined], ], } interface TTTAction { row: number col: number mark: 'X' | 'O' } function tttReducer(state: TTTState, { row, col, mark }: TTTAction): TTTState { if (state.board[row][col] !== undefined) { return state } return { board: state.board.map((rArr, rIdx) => rArr.map((cell, cIdx) => rIdx === row && cIdx === col ? mark : cell) ), } } test('Basic linear', () => { const s = new KetchupSynced(tttInitial, 1000, tttReducer) expect(s.tryProcess({ action: { row: 0, col: 0, mark: 'X' }, timestamp: 1010, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 1, col: 2, mark: 'O' }, timestamp: 1020, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 2, col: 1, mark: 'X' }, timestamp: 1030, clientId: 0, })).toBe(true) expect(s.projState).toEqual({ board: [ ['X', undefined, undefined], [undefined, undefined, 'O'], [undefined, 'X', undefined], ], }) }) test('Out-of-order', () => { const s = new KetchupSynced(tttInitial, 1000, tttReducer) expect(s.tryProcess({ action: { row: 0, col: 0, mark: 'X' }, timestamp: 1010, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 1, col: 2, mark: 'X' }, timestamp: 1040, clientId: 0, })).toBe(true) expect(s.projState).toEqual({ board: [ ['X', undefined, undefined], [undefined, undefined, 'X'], [undefined, undefined, undefined], ], }) expect(s.tryProcess({ action: { row: 1, col: 2, mark: 'O' }, timestamp: 1030, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 2, col: 1, mark: 'X' }, timestamp: 1020, clientId: 0, })).toBe(true) expect(s.projState).toEqual({ board: [ ['X', undefined, undefined], [undefined, undefined, 'O'], [undefined, 'X', undefined], ], }) }) test('Out-of-order with confirms', () => { const s = new KetchupSynced(tttInitial, 1000, tttReducer) expect(s.tryProcess({ action: { row: 0, col: 0, mark: 'X' }, timestamp: 1010, clientId: 0, })).toBe(true) expect(s.confirmBefore(1010)).toBe(0) expect(s.confState).toEqual(tttInitial) expect(s.projs).toEqual([{ action: { row: 0, col: 0, mark: 'X' }, timestamp: 1010, clientId: 0, state: { board: [ ['X', undefined, undefined], [undefined, undefined, undefined], [undefined, undefined, undefined], ], }, }]) expect(s.confirmBefore(1011)).toBe(1) expect(s.confState).toEqual({ board: [ ['X', undefined, undefined], [undefined, undefined, undefined], [undefined, undefined, undefined], ], }) expect(s.projs).toEqual([]) expect(s.tryProcess({ action: { row: 0, col: 1, mark: 'X' }, timestamp: 1040, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 0, col: 2, mark: 'O' }, timestamp: 1030, clientId: 0, })).toBe(true) expect(s.tryProcess({ action: { row: 1, col: 0, mark: 'X' }, timestamp: 1020, clientId: 0, })).toBe(true) expect(s.confirmBefore(1021)).toBe(1) expect(s.confState).toEqual({ board: [ ['X', undefined, undefined], ['X', undefined, undefined], [undefined, undefined, undefined], ], }) expect(s.projs).toEqual([{ action: { row: 0, col: 2, mark: 'O' }, timestamp: 1030, clientId: 0, state: { board: [ ['X', undefined, 'O'], ['X', undefined, undefined], [undefined, undefined, undefined], ], }, }, { action: { row: 0, col: 1, mark: 'X' }, timestamp: 1040, clientId: 0, state: { board: [ ['X', 'X', 'O'], ['X', undefined, undefined], [undefined, undefined, undefined], ], }, }]) expect(s.projState).toEqual({ board: [ ['X', 'X', 'O'], ['X', undefined, undefined], [undefined, undefined, undefined], ], }) })