import { BN } from '@statechannels/wallet-core' ; import { DBAdmin } from '../../db-admin/db-admin' ; import { testKnex as knex } from '../../../jest/knex-setup-teardown' ; import { defaultTestWalletConfig } from '../../config' ; import { Store } from '../../engine/store' ; import { EngineResponse } from '../../engine/engine-response' ; import { TestChannel } from '../../engine/__test__/fixtures/test-channel' ; import { TestLedgerChannel } from '../../engine/__test__/fixtures/test-ledger-channel' ; import { SingleAppUpdater } from '../single-app-updater' ; const FINAL = 8 ; const testChan = TestChannel . create ( { finalFrom : FINAL } ) ; const testChan2 = TestChannel . create ( { } ) ; let store ; let singleAppUpdater ; let response ; beforeEach ( async ( ) => { store = new Store ( knex , defaultTestWalletConfig ( ) . metricsConfiguration . timingMetrics , defaultTestWalletConfig ( ) . skipEvmValidation , '0' ) ; await DBAdmin . truncateDataBaseFromKnex ( knex ) ; await DBAdmin . migrateDatabaseFromKnex ( knex ) ; singleAppUpdater = SingleAppUpdater . create ( store ) ; response = EngineResponse . initialize ( ) ; } ) ; afterEach ( async ( ) => await DBAdmin . truncateDataBaseFromKnex ( knex ) ) ; describe ( 'when given an update for a single, running app channel' , ( ) => { it ( `updates the channel` , async ( ) => { await setup ( { states : [ testChan . wirePayload ( 5 ) , testChan . wirePayload ( 6 ) ] } ) ; await singleAppUpdater . update ( testChan . wirePayload ( 7 ) , response ) ; expect ( response . singleChannelOutput ( ) . channelResult . turnNum ) . toEqual ( 7 ) ; } ) ; } ) ; describe ( 'bad payload' , ( ) => { const state0WithObj = testChan . openChannelPayload ; const getRequest = TestChannel . mergePayloads ( testChan . wirePayload ( 7 ) , testChan . getChannelRequest ) ; const twoStates = TestChannel . mergePayloads ( testChan . wirePayload ( 7 ) , testChan2 . wirePayload ( 7 ) ) ; const emptyPayload = TestChannel . emptyPayload ; it . each `
    case                 | payload          | error
    ${ 'multiple states' } | ${ twoStates }     | ${ `The payload sent to pushUpdate must contain exactly 1 signedState.` }
    ${ 'no states' }       | ${ emptyPayload }  | ${ `The payload sent to pushUpdate must contain exactly 1 signedState.` }
    ${ 'an objective' }    | ${ state0WithObj } | ${ `The payload sent to pushUpdate must not contain objectives.` }
    ${ 'a request' }       | ${ getRequest }    | ${ `The payload sent to pushUpdate must not contain requests.` }
  ` ( 'errors when given $case' , async ( { payload , error } ) => { await expect ( singleAppUpdater . update ( payload , response ) ) . rejects . toThrow ( error ) ; } ) ; } ) ; describe ( 'app channel not running' , ( ) => { it ( `errors if the channel is still opening` , async ( ) => { await setup ( { states : [ testChan . wirePayload ( 0 ) , testChan . wirePayload ( 1 ) ] } ) ; await expect ( singleAppUpdater . update ( testChan . wirePayload ( 2 ) , response ) ) . rejects . toThrow ( 'The update sent to pushUpdate must be for a running channel' ) ; } ) ; it ( `errors if the channel has a final state` , async ( ) => { await setup ( { states : [ testChan . wirePayload ( FINAL - 1 ) , testChan . wirePayload ( FINAL , [ 5 , 5 ] ) ] , } ) ; await expect ( singleAppUpdater . update ( testChan . wirePayload ( FINAL + 1 , [ 5 , 5 ] ) , response ) ) . rejects . toThrow ( 'The update sent to pushUpdate must be for a running channel' ) ; } ) ; it ( `errors if given a first final state` , async ( ) => { await setup ( { states : [ testChan . wirePayload ( FINAL - 2 ) , testChan . wirePayload ( FINAL - 1 , [ 5 , 5 ] ) ] , } ) ; await expect ( singleAppUpdater . update ( testChan . wirePayload ( FINAL , [ 5 , 5 ] ) , response ) ) . rejects . toThrow ( 'The update sent to pushUpdate must be for a running channel' ) ; } ) ; } ) ; describe ( 'not an app channel' , ( ) => { it ( `errors if the channel is a ledger channel` , async ( ) => { const testLedger = TestLedgerChannel . create ( { aBal : 5 , bBal : 5 } ) ; await testLedger . insertInto ( store ) ; await store . pushMessage ( testLedger . wirePayload ( 5 ) ) ; await store . pushMessage ( testLedger . wirePayload ( 6 ) ) ; await store . updateFunding ( testLedger . channelId , BN . from ( 10 ) , testLedger . asset ) ; await expect ( singleAppUpdater . update ( testLedger . wirePayload ( 7 ) , response ) ) . rejects . toThrow ( 'The update sent to pushUpdate must be for an application channel' ) ; } ) ; } ) ; interface SetupParams { states : any [ ] ; } const setup = async ( { states } ) : Promise < void > => { await store . addSigningKey ( testChan . signingKeyA ) ; for ( const state of states ) { await store . pushMessage ( state ) ; } await store . updateFunding ( testChan . channelId , BN . from ( 10 ) , testChan . asset ) ; } ;