import { c, client, integrationSetup, s, server } from './IntegrationCommon'

integrationSetup()

describe('2.2.  Advertising the STREAMING Extension', () => {
  test(
    'Example of a client using CAPABILITIES and MODE STREAM on a mode-\n' + '  switching server:',
    async () => {
      c('CAPABILITIES')
      s('101 Capability list:')
      s('VERSION 2')
      s('MODE-READER')
      s('IHAVE')
      s('LIST ACTIVE')
      s('STREAMING')
      s('.')

      let response = await client.capabilities()
      expect(response).toEqual({
        code: 101,
        comment: 'Capability list:',
        description: 'Capability list follows (multi-line)',
        capabilities: {
          VERSION: ['2'],
          'MODE-READER': [],
          IHAVE: [],
          LIST: ['ACTIVE'],
          STREAMING: []
        }
      })

      c('MODE STREAM')
      s('203 Streaming permitted')

      response = await client.modeStream()
      expect(response).toEqual({
        code: 203,
        comment: 'Streaming permitted',
        description: 'Streaming permitted'
      })

      c('CAPABILITIES')
      s('101 Capability list:')
      s('VERSION 2')
      s('MODE-READER')
      s('IHAVE')
      s('LIST ACTIVE')
      s('STREAMING')
      s('.')

      response = await client.capabilities()
      expect(response).toEqual({
        code: 101,
        comment: 'Capability list:',
        description: 'Capability list follows (multi-line)',
        capabilities: {
          VERSION: ['2'],
          'MODE-READER': [],
          IHAVE: [],
          LIST: ['ACTIVE'],
          STREAMING: []
        }
      })

      c('MODE READER')
      s('200 Posting allowed')

      response = await client.modeReader()
      expect(response).toEqual({
        code: 200,
        comment: 'Posting allowed',
        description: 'Posting allowed'
      })

      c('CAPABILITIES')
      s('101 Capability list:')
      s('VERSION 2')
      s('READER')
      s('POST')
      s('LIST ACTIVE NEWSGROUPS HEADERS')
      s('HDR')
      s('.')

      response = await client.capabilities()
      expect(response).toEqual({
        code: 101,
        comment: 'Capability list:',
        description: 'Capability list follows (multi-line)',
        capabilities: {
          VERSION: ['2'],
          READER: [],
          POST: [],
          LIST: ['ACTIVE', 'NEWSGROUPS', 'HEADERS'],
          HDR: []
        }
      })
    }
  )
})

describe('2.3.  MODE STREAM Command', () => {
  test(
    'Example of a client checking the ability to stream articles on a\n' +
      '  server which does not support this extension:',
    async () => {
      c('MODE STREAM')
      s('501 Unknown MODE variant')

      let caught = false
      return client
        .modeStream()
        .catch(response => {
          caught = true
          expect(response).toEqual({
            code: 501,
            comment: 'Unknown MODE variant',
            description: 'syntax error in command'
          })
        })
        .then(() => expect(caught).toBe(true))
    }
  )
  test(
    'Example of a client checking the ability to stream articles on a' +
      ' server which supports this extension:',
    async () => {
      c('MODE STREAM')
      s('203 Streaming permitted')

      const response = await client.modeStream()
      expect(response).toEqual({
        code: 203,
        comment: 'Streaming permitted',
        description: 'Streaming permitted'
      })
    }
  )
})

describe('2.4.  CHECK Command', () => {
  test(
    'Example of a client checking whether the server would like a set of' +
      'articles and getting a mixture of responses:',
    async () => {
      c('CHECK <i.am.an.article.you.will.want@example.com>')
      s('238 <i.am.an.article.you.will.want@example.com>')

      const response = await client.check('<i.am.an.article.you.will.want@example.com>')
      expect(response).toEqual({
        code: 238,
        comment: '',
        description: 'Send article to be transferred',
        article: {
          messageId: '<i.am.an.article.you.will.want@example.com>'
        }
      })

      c('CHECK <i.am.an.article.you.have@example.com>')
      s('438 <i.am.an.article.you.have@example.com>')

      let caught = false
      try {
        await client.check('<i.am.an.article.you.have@example.com>')
      } catch (response) {
        caught = true
        expect(response).toEqual({
          code: 438,
          comment: '',
          description: 'Article not wanted',
          article: {
            messageId: '<i.am.an.article.you.have@example.com>'
          }
        })
      }
      expect(caught).toBe(true)

      c('CHECK <i.am.an.article.you.defer@example.com>')
      s('431 <i.am.an.article.you.defer@example.com>')

      caught = false
      try {
        await client.check('<i.am.an.article.you.defer@example.com>')
      } catch (response) {
        caught = true
        expect(response).toEqual({
          code: 431,
          comment: '',
          description: 'Transfer not possible; try again later',
          article: {
            messageId: '<i.am.an.article.you.defer@example.com>'
          }
        })
      }
      expect(caught).toBe(true)
    }
  )

  test('Example of pipelining the CHECK commands in the previous example:', async () => {
    c('CHECK <i.am.an.article.you.will.want@example.com>')
    c('CHECK <i.am.an.article.you.have@example.com>')
    c('CHECK <i.am.an.article.you.defer@example.com>')
    s('238 <i.am.an.article.you.will.want@example.com>')
    s('438 <i.am.an.article.you.have@example.com>')
    s('431 <i.am.an.article.you.defer@example.com>')

    // Setup pipelined response (awkward with MockServer)
    server.resetHandlers()
    server.addSimpleHandler((command, args) => {
      expect(command).toEqual('CHECK')
      const line = args.join(' ')
      expect(line).toBe(
        '<i.am.an.article.you.will.want@example.com>\r\nCHECK <i.am.an.article.you.have@example.com>\r\nCHECK <i.am.an.article.you.defer@example.com>'
      )
      return (
        '238 <i.am.an.article.you.will.want@example.com>\r\n' +
        '438 <i.am.an.article.you.have@example.com>\r\n' +
        '431 <i.am.an.article.you.defer@example.com>\r\n'
      )
    })

    const responses = await Promise.all([
      client.check('<i.am.an.article.you.will.want@example.com>').catch(r => r),
      client.check('<i.am.an.article.you.have@example.com>').catch(r => r),
      client.check('<i.am.an.article.you.defer@example.com>').catch(r => r)
    ])
    expect(responses).toEqual([
      {
        code: 238,
        comment: '',
        description: 'Send article to be transferred',
        article: {
          messageId: '<i.am.an.article.you.will.want@example.com>'
        }
      },
      {
        code: 438,
        comment: '',
        description: 'Article not wanted',
        article: {
          messageId: '<i.am.an.article.you.have@example.com>'
        }
      },
      {
        code: 431,
        comment: '',
        description: 'Transfer not possible; try again later',
        article: {
          messageId: '<i.am.an.article.you.defer@example.com>'
        }
      }
    ])
  })
})

describe('2.5.  TAKETHIS Command', () => {
  test(
    'Example of streaming two articles to another site (the first article' +
      'is accepted and the second is rejected):',
    async () => {
      c(
        'TAKETHIS <i.am.an.article.you.will.want@example.com>',
        'Path: pathost!demo!somewhere!not-for-mail',
        'From: "Demo User" <nobody@example.com>',
        'Newsgroups: misc.test',
        'Subject: I am just a test article',
        'Date: 6 Oct 1998 04:38:40 -0500',
        'Organization: An Example Com, San Jose, CA',
        'Message-ID: <i.am.an.article.you.will.want@example.com>',
        '',
        'This is just a test article.',
        '.'
      )
      c(
        'TAKETHIS <i.am.an.article.you.have@example.com>',
        'Path: pathost!demo!somewhere!not-for-mail',
        'From: "Demo User" <nobody@example.com>',
        'Newsgroups: misc.test',
        'Subject: I am just a test article',
        'Date: 6 Oct 1998 04:38:40 -0500',
        'Organization: An Example Com, San Jose, CA',
        'Message-ID: <i.am.an.article.you.have@example.com>',
        '',
        'This is just a test article.',
        '.'
      )
      s('239 <i.am.an.article.you.will.want@example.com>')
      s('439 <i.am.an.article.you.have@example.com>')

      // Setup pipelined response (awkward with MockServer)
      server.resetHandlers()
      server.addSimpleHandler((command, args) => {
        expect(command).toEqual('TAKETHIS')
        expect(args).toEqual(expect.any(Array))
        const data = args.join(' ').split('\r\n')
        data[0] = command + ' ' + data[0]
        expect(data).toEqual([
          'TAKETHIS <i.am.an.article.you.will.want@example.com>',
          'PATH: pathost!demo!somewhere!not-for-mail',
          'FROM: "Demo User" <nobody@example.com>',
          'NEWSGROUPS: misc.test',
          'SUBJECT: I am just a test article',
          'DATE: 6 Oct 1998 04:38:40 -0500',
          'ORGANIZATION: An Example Com, San Jose, CA',
          'MESSAGE-ID: <i.am.an.article.you.will.want@example.com>',
          '',
          'This is just a test article.',
          '.',
          'TAKETHIS <i.am.an.article.you.have@example.com>',
          'PATH: pathost!demo!somewhere!not-for-mail',
          'FROM: "Demo User" <nobody@example.com>',
          'NEWSGROUPS: misc.test',
          'SUBJECT: I am just a test article',
          'DATE: 6 Oct 1998 04:38:40 -0500',
          'ORGANIZATION: An Example Com, San Jose, CA',
          'MESSAGE-ID: <i.am.an.article.you.have@example.com>',
          '',
          'This is just a test article.',
          '.'
        ])
        return (
          '239 <i.am.an.article.you.will.want@example.com>\r\n' +
          '439 <i.am.an.article.you.have@example.com>\r\n'
        )
      })

      const responses = await Promise.all([
        client
          .takeThis({
            messageId: '<i.am.an.article.you.will.want@example.com>',
            headers: {
              PATH: 'pathost!demo!somewhere!not-for-mail',
              FROM: '"Demo User" <nobody@example.com>',
              NEWSGROUPS: 'misc.test',
              SUBJECT: 'I am just a test article',
              DATE: '6 Oct 1998 04:38:40 -0500',
              ORGANIZATION: 'An Example Com, San Jose, CA',
              'MESSAGE-ID': '<i.am.an.article.you.will.want@example.com>'
            },
            body: ['This is just a test article.']
          })
          .catch(r => r),
        client
          .takeThis({
            messageId: '<i.am.an.article.you.have@example.com>',
            headers: {
              PATH: 'pathost!demo!somewhere!not-for-mail',
              FROM: '"Demo User" <nobody@example.com>',
              NEWSGROUPS: 'misc.test',
              SUBJECT: 'I am just a test article',
              DATE: '6 Oct 1998 04:38:40 -0500',
              ORGANIZATION: 'An Example Com, San Jose, CA',
              'MESSAGE-ID': '<i.am.an.article.you.have@example.com>'
            },
            body: ['This is just a test article.']
          })
          .catch(r => r)
      ])
      expect(responses).toEqual([
        {
          code: 239,
          comment: '',
          description: 'Article transferred OK',
          article: {
            messageId: '<i.am.an.article.you.will.want@example.com>'
          }
        },
        {
          code: 439,
          comment: '',
          description: 'Transfer rejected; do not retry',
          article: {
            messageId: '<i.am.an.article.you.have@example.com>'
          }
        }
      ])
    }
  )

  test('Example of sending an article to a site where the transfer fails:', async () => {
    c(
      'TAKETHIS <i.am.an.article.you.will.want@example.com>',
      'PATH: pathost!demo!somewhere!not-for-mail',
      'FROM: "Demo User" <nobody@example.com>',
      'NEWSGROUPS: misc.test',
      'SUBJECT: I am just a test article',
      'DATE: 6 Oct 1998 04:38:40 -0500',
      'ORGANIZATION: An Example Com, San Jose, CA',
      'MESSAGE-ID: <i.am.an.article.you.will.want@example.com>',
      '',
      'This is just a test article.',
      '.'
    )
    s('400 Service temporarily unavailable')

    let caught = false
    try {
      await client.takeThis({
        messageId: '<i.am.an.article.you.will.want@example.com>',
        headers: {
          PATH: 'pathost!demo!somewhere!not-for-mail',
          FROM: '"Demo User" <nobody@example.com>',
          NEWSGROUPS: 'misc.test',
          SUBJECT: 'I am just a test article',
          DATE: '6 Oct 1998 04:38:40 -0500',
          ORGANIZATION: 'An Example Com, San Jose, CA',
          'MESSAGE-ID': '<i.am.an.article.you.will.want@example.com>'
        },
        body: ['This is just a test article.']
      })
    } catch (response) {
      caught = true
      expect(response).toEqual({
        code: 400,
        comment: 'Service temporarily unavailable',
        description: 'service not available or no longer available'
      })
    }
    expect(caught).toBe(true)
  })
})
