newsie
Version:
An NNTP Client Library targeting NodeJS. It supports the authentication, TLS encryption, base NNTP commands, and more.
1,631 lines (1,413 loc) • 58.1 kB
text/typescript
import { Command } from '../src'
import { parse } from '../src/parse'
jest.disableAutomock()
describe('5 Session Administration Commands', () => {
describe('5.1 Initial connection', () => {
const command = Command.GREETING
test('200 service available, posting allowed', () => {
const reply = '200 NNTP Service Ready, posting permitted\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 200,
comment: 'NNTP Service Ready, posting permitted',
description: 'Service available, posting allowed'
})
})
test('201 service available, posting prohibited', () => {
const reply = '201 service available, posting prohibited\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 201,
comment: 'service available, posting prohibited',
description: 'Service available, posting prohibited'
})
})
test('400 service temporarily unavailable', () => {
const reply = '400 service temporarily unavailable\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 400,
comment: 'service temporarily unavailable',
description: 'Service temporarily unavailable'
})
})
test('502 service permanently unavailable', () => {
const reply = '502 service permanently unavailable\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 502,
comment: 'service permanently unavailable',
description: 'Service permanently unavailable'
})
})
})
describe('5.2. CAPABILITIES', () => {
const command = Command.CAPABILITIES
test('Example of a minimal response (a read-only server)', () => {
const reply = '101 Capability list:\r\nVERSION 2\r\nREADER\r\nLIST ACTIVE NEWSGROUPS\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: 'Capability list:',
description: 'Capability list follows (multi-line)',
capabilities: {
VERSION: ['2'],
READER: [],
LIST: ['ACTIVE', 'NEWSGROUPS']
}
})
})
test('Example of a response from a server that has a range of facilities and that also describes itself:', () => {
const reply =
'101 Capability list:\r\n' +
'VERSION 2\r\nREADER\r\nIHAVE\r\nPOST\r\nNEWNEWS\r\n' +
'LIST ACTIVE NEWSGROUPS ACTIVE.TIMES OVERVIEW.FMT\r\n' +
'IMPLEMENTATION INN 4.2 2004-12-25\r\nOVER MSGID\r\n' +
'STREAMING\r\nXSECRET\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: 'Capability list:',
description: 'Capability list follows (multi-line)',
capabilities: {
VERSION: ['2'],
READER: [],
IHAVE: [],
POST: [],
NEWNEWS: [],
LIST: ['ACTIVE', 'NEWSGROUPS', 'ACTIVE.TIMES', 'OVERVIEW.FMT'],
IMPLEMENTATION: ['INN', '4.2', '2004-12-25'],
OVER: ['MSGID'],
STREAMING: [],
XSECRET: []
}
})
})
test('Example of a server that supports more than one version of NNTP:', () => {
const reply =
'101 Capability list:\r\nVERSION 2 3\r\nREADER\r\nLIST ACTIVE NEWSGROUPS\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: 'Capability list:',
description: 'Capability list follows (multi-line)',
capabilities: {
VERSION: ['2', '3'],
READER: [],
LIST: ['ACTIVE', 'NEWSGROUPS']
}
})
})
test('tokens MUST be separated by one or more space or TAB characters', () => {
const reply = '101\r\nVERSION\t\t\t\t1234\r\nHELLO WORLD\r\nBYE \t BYE\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: '',
description: 'Capability list follows (multi-line)',
capabilities: {
VERSION: ['1234'],
HELLO: ['WORLD'],
BYE: ['BYE']
}
})
})
test('tokens are case insensitive', () => {
const reply = '101\r\nveRsIoN 4\r\nlist active NEWSGroups\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: '',
description: 'Capability list follows (multi-line)',
capabilities: {
VERSION: ['4'],
LIST: ['ACTIVE', 'NEWSGROUPS']
}
})
})
test('empty capabilities list', () => {
const reply = '101\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: '',
description: 'Capability list follows (multi-line)',
capabilities: {}
})
})
test('capability begins with dot', () => {
const reply = '101\r\n..BEGINS WITH DOT\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 101,
comment: '',
description: 'Capability list follows (multi-line)',
capabilities: {
'.BEGINS': ['WITH', 'DOT']
}
})
})
})
describe('5.3. MODE READER', () => {
const command = Command.MODE_READER
test('200 Example of use of the MODE READER command on a server that provides reading facilities:', () => {
const reply = '200 Reader mode, posting permitted\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 200,
comment: 'Reader mode, posting permitted',
description: 'Posting allowed'
})
})
test('201 Example of use of the MODE READER command where the client is not permitted to post:', () => {
const reply = '201 NNTP Service Ready, posting prohibited\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 201,
comment: 'NNTP Service Ready, posting prohibited',
description: 'Posting prohibited'
})
})
test(
'502 Example of use of the MODE READER command on a transit-only ' +
'server (which therefore does not providing reading facilities):',
() => {
const reply = '502 Transit service only\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 502,
comment: 'Transit service only',
description: 'Reading service permanently unavailable'
})
}
)
})
describe('5.4. QUIT', () => {
const command = Command.QUIT
test('205 connection closing', () => {
const reply = '205 connection closing\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 205,
comment: 'connection closing',
description: 'Connection closing'
})
})
})
})
describe('6.1. Group and Article Selection', () => {
describe('6.1.1. GROUP', () => {
const command = Command.GROUP
test('211 Example for a group known to the server:', () => {
const reply = '211 1234 3000234 3002322 misc.test\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 211,
comment: '',
description: 'Group successfully selected',
group: {
name: 'misc.test',
number: 1234,
low: 3000234,
high: 3002322
}
})
})
test('411 Example for a group unknown to the server:', () => {
const reply = '411 example.is.sob.bradner.or.barber is unknown\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 411,
comment: 'example.is.sob.bradner.or.barber is unknown',
description: 'No such newsgroup'
})
})
test('211 Example of an empty group using the preferred response:', () => {
const reply = '211 0 4000 3999 example.currently.empty.newsgroup\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 211,
comment: '',
description: 'Group successfully selected',
group: {
name: 'example.currently.empty.newsgroup',
number: 0,
low: 4000,
high: 3999
}
})
})
test('211 Example of an empty group using an alternative response:', () => {
const reply = '211 0 0 0 example.currently.empty.newsgroup\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 211,
comment: '',
description: 'Group successfully selected',
group: {
name: 'example.currently.empty.newsgroup',
number: 0,
low: 0,
high: 0
}
})
})
test('211 Example of an empty group using a different alternative response:', () => {
const reply = '211 0 4000 4321 example.currently.empty.newsgroup\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 211,
comment: '',
description: 'Group successfully selected',
group: {
name: 'example.currently.empty.newsgroup',
number: 0,
low: 4000,
high: 4321
}
})
})
})
describe('6.1.2. LISTGROUP', () => {
const command = Command.LISTGROUP
test('211 Example of LISTGROUP being used to select a group:', () => {
const reply =
'211 2000 3000234 3002322 misc.test list follows\r\n' +
'3000234\r\n3000237\r\n3000238\r\n3000239\r\n3002322\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: expect.any(Number),
code: 211,
comment: 'list follows',
description: 'Article numbers follow (multi-line)',
group: {
number: 2000,
low: 3000234,
high: 3002322,
name: 'misc.test',
articleNumbers: [3000234, 3000237, 3000238, 3000239, 3002322]
}
})
})
test('211 Example of LISTGROUP on an empty group:', () => {
const reply = '211 0 0 0 example.empty.newsgroup list follows\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: expect.any(Number),
code: 211,
comment: 'list follows',
description: 'Article numbers follow (multi-line)',
group: {
number: 0,
low: 0,
high: 0,
name: 'example.empty.newsgroup',
articleNumbers: []
}
})
})
test('211 Example of LISTGROUP with a range:', () => {
const reply =
'211 2000 3000234 3002322 misc.test list follows\r\n' + '3000238\r\n3000239\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: expect.any(Number),
code: 211,
comment: 'list follows',
description: 'Article numbers follow (multi-line)',
group: {
number: 2000,
low: 3000234,
high: 3002322,
name: 'misc.test',
articleNumbers: [3000238, 3000239]
}
})
})
test('411 no such newsgroup', () => {
const reply = '411 no such newsgroup\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: expect.any(Number),
code: 411,
comment: 'no such newsgroup',
description: 'No such newsgroup'
})
})
test('412 no newsgroup selected', () => {
const reply = '412 no newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: expect.any(Number),
code: 412,
comment: 'no newsgroup selected',
description: 'No newsgroup selected'
})
})
})
describe('6.1.3. LAST', () => {
const command = Command.LAST
test('223 - Example of a successful article retrieval using LAST:', () => {
const reply = '223 3000234 <45223423@example.com> retrieved\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 223,
comment: 'retrieved',
description: 'Article found',
article: {
articleNumber: 3000234,
messageId: '<45223423@example.com>'
}
})
})
test(
'412 - Example of an attempt to retrieve an article without having ' +
'selected a group (via the GROUP command) first',
() => {
const reply = '412 no newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'no newsgroup selected',
description: 'No newsgroup selected'
})
}
)
test(
'420 - Example of an attempt to retrieve an article using the LAST ' +
'command when the currently selected newsgroup is empty',
() => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
}
)
test('422 - No previous article to retrieve', () => {
const reply = '422 No previous article to retrieve\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 422,
comment: 'No previous article to retrieve',
description: 'No previous article in this group'
})
})
})
describe('6.1.4. NEXT', () => {
const command = Command.NEXT
test('223 - Example of a successful article retrieval using NEXT:', () => {
const reply = '223 3000237 <668929@example.org> retrieved\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 223,
comment: 'retrieved',
description: 'Article found',
article: {
articleNumber: 3000237,
messageId: '<668929@example.org>'
}
})
})
test(
'412 - Example of an attempt to retrieve an article without having ' +
'selected a group (via the GROUP command) first:',
() => {
const reply = '412 no newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'no newsgroup selected',
description: 'No newsgroup selected'
})
}
)
test(
'420 - Example of an attempt to retrieve an article using the NEXT ' +
'command when the currently selected newsgroup is empty:',
() => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
}
)
test(
'421- Example of an attempt to retrieve an article using the NEXT ' +
'command when the current article number is that of the last article in the group:',
() => {
const reply = '421 No next article to retrieve\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 421,
comment: 'No next article to retrieve',
description: 'No next article in this group'
})
}
)
})
describe('6.2.1. ARTICLE', () => {
const command = Command.ARTICLE
test(
'220 - Example of a successful retrieval of an article (explicitly ' +
'not using an article number):',
() => {
const reply =
'220 3000234 <45223423@example.com>\r\n' +
'Path: pathost!demo!whitehouse!not-for-mail\r\n' +
'From: "Demo User" <nobody@example.net>\r\n' +
'Newsgroups: misc.test\r\n' +
'Subject: I am just a test article\r\n' +
'Date: 6 Oct 1998 04:38:40 -0500\r\n' +
'Organization: An Example Net, Uncertain, Texas\r\n' +
'Message-ID: <45223423@example.com>\r\n' +
'\r\n' +
'This is just a test article.\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 220,
comment: '',
description: 'Article follows (multi-line)',
article: {
articleNumber: 3000234,
messageId: '<45223423@example.com>',
headers: {
PATH: 'pathost!demo!whitehouse!not-for-mail',
FROM: '"Demo User" <nobody@example.net>',
NEWSGROUPS: 'misc.test',
SUBJECT: 'I am just a test article',
DATE: '6 Oct 1998 04:38:40 -0500',
ORGANIZATION: 'An Example Net, Uncertain, Texas',
'MESSAGE-ID': '<45223423@example.com>'
},
body: ['This is just a test article.']
}
})
}
)
test('430 - Example of an unsuccessful retrieval of an article by message-id:', () => {
const reply = '430 No Such Article Found\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 430,
comment: 'No Such Article Found',
description: 'No article with that message-id'
})
})
test(
'412 - Example of an unsuccessful retrieval of an article by number ' +
'because no newsgroup was selected first:',
() => {
const reply = '412 No newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'No newsgroup selected',
description: 'No newsgroup selected'
})
}
)
test('423 - Example of an unsuccessful retrieval of an article by number:', () => {
const reply = '423 No article with that number\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 423,
comment: 'No article with that number',
description: 'No article with that number'
})
})
test(
'420 - Example of an attempt to retrieve an article when the ' +
'currently selected newsgroup is empty:',
() => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
}
)
})
describe('6.2.2. HEAD', () => {
const command = Command.HEAD
test(
'221 - Example of a successful retrieval of the headers of an ' +
'article (explicitly not using an article number):',
() => {
const reply =
'221 3000234 <45223423@example.com>\r\n' +
'Path: pathost!demo!whitehouse!not-for-mail\r\n' +
'From: "Demo User" <nobody@example.net>\r\n' +
'Newsgroups: misc.test\r\n' +
'Subject: I am just a test article\r\n' +
'Date: 6 Oct 1998 04:38:40 -0500\r\n' +
'Organization: An Example Net, Uncertain, Texas\r\n' +
'Message-ID: <45223423@example.com>\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 221,
comment: '',
description: 'Headers follow (multi-line)',
article: {
articleNumber: 3000234,
messageId: '<45223423@example.com>',
headers: {
PATH: 'pathost!demo!whitehouse!not-for-mail',
FROM: '"Demo User" <nobody@example.net>',
NEWSGROUPS: 'misc.test',
SUBJECT: 'I am just a test article',
DATE: '6 Oct 1998 04:38:40 -0500',
ORGANIZATION: 'An Example Net, Uncertain, Texas',
'MESSAGE-ID': '<45223423@example.com>'
}
}
})
}
)
test(
'430 - Example of an unsuccessful retrieval of the headers of an ' + 'article by message-id:',
() => {
const reply = '430 No Such Article Found\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 430,
comment: 'No Such Article Found',
description: 'No article with that message-id'
})
}
)
test(
'412 - Example of an unsuccessful retrieval of the headers of an ' +
'article by number because no newsgroup was selected first:',
() => {
const reply = '412 No newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'No newsgroup selected',
description: 'No newsgroup selected'
})
}
)
test(
'423 - Example of an unsuccessful retrieval of the headers of an ' + 'article by number:',
() => {
const reply = '423 No article with that number\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 423,
comment: 'No article with that number',
description: 'No article with that number'
})
}
)
test(
'420 - Example of an attempt to retrieve the headers of an article ' +
'when the currently selected newsgroup is empty:',
() => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
}
)
})
describe('6.2.3. BODY', () => {
const command = Command.BODY
test(
'222 - Example of a successful retrieval of the body of an article ' +
'(explicitly not using an article number):',
() => {
const reply = '222 3000234 <45223423@example.com>\r\nThis is just a test article.\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 222,
comment: '',
description: 'Body follows (multi-line)',
article: {
articleNumber: 3000234,
messageId: '<45223423@example.com>',
body: ['This is just a test article.']
}
})
}
)
test('430 - Example of an unsuccessful retrieval of the body of an article by message-id:', () => {
const reply = '430 No Such Article Found\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 430,
comment: 'No Such Article Found',
description: 'No article with that message-id'
})
})
test(
'412 - Example of an unsuccessful retrieval of the body of an article ' +
'by number because no newsgroup was selected first:',
() => {
const reply = '412 No newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'No newsgroup selected',
description: 'No newsgroup selected'
})
}
)
test('423 - Example of an unsuccessful retrieval of the body of an article by number:', () => {
const reply = '423 No article with that number\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 423,
comment: 'No article with that number',
description: 'No article with that number'
})
})
test(
'420 - Example of an attempt to retrieve the body of an article when the ' +
'currently selected newsgroup is empty:',
() => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
}
)
})
describe('6.2.4. STAT', () => {
const command = Command.STAT
test('223 - Example of STAT on an existing article (explicitly not using an article number):', () => {
const reply = '223 3000234 <45223423@example.com>\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 223,
comment: '',
description: 'Article exists',
article: {
articleNumber: 3000234,
messageId: '<45223423@example.com>'
}
})
})
test('430 - Example of STAT on an article not on the server by message-id:', () => {
const reply = '430 No Such Article Found\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 430,
comment: 'No Such Article Found',
description: 'No article with that message-id'
})
})
test('412 - Example of STAT on an article by number when no newsgroup was selected first:', () => {
const reply = '412 No newsgroup selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 412,
comment: 'No newsgroup selected',
description: 'No newsgroup selected'
})
})
test('423 - Example of STAT on an article not in the server by number:', () => {
const reply = '423 No article with that number\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 423,
comment: 'No article with that number',
description: 'No article with that number'
})
})
test('420 - Example of STAT on an article when the currently selected newsgroup is empty:', () => {
const reply = '420 No current article selected\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 420,
comment: 'No current article selected',
description: 'Current article number is invalid'
})
})
})
describe('7.1. DATE', () => {
const command = Command.DATE
test('111 - Examples', () => {
const reply = '111 19990623135624\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 111,
comment: '',
description: 'Server date and time',
isoDateTime: '1999-06-23T13:56:24.000Z'
})
})
})
describe('7.2. HELP', () => {
const command = Command.HELP
test('100 - Examples', () => {
const reply =
'100 Help text follows\r\n' +
'This is some help text. There is no specific\r\n' +
'formatting requirement for this test, though\r\n' +
'it is customary for it to list the valid commands\r\n' +
'and give a brief definition of what they do.\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 100,
comment: 'Help text follows',
description: 'Help text follows (multi-line)',
text: [
'This is some help text. There is no specific',
'formatting requirement for this test, though',
'it is customary for it to list the valid commands',
'and give a brief definition of what they do.'
]
})
})
})
describe('7.3. NEWGROUPS', () => {
const command = Command.NEWGROUPS
test('231 - Example where there are new groups:', () => {
const reply =
'231 list of new newsgroups follows\r\n' +
'alt.rfc-writers.recovery 4 1 y\r\n' +
'tx.natives.recovery 89 56 y\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 231,
comment: 'list of new newsgroups follows',
description: 'List of new newsgroups follows (multi-line)',
newsgroups: [
{
name: 'alt.rfc-writers.recovery',
high: 4,
low: 1,
status: 'y'
},
{
name: 'tx.natives.recovery',
high: 89,
low: 56,
status: 'y'
}
]
})
})
test('231 - Example where there are no new groups:', () => {
const reply = '231 list of new newsgroups follows\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 231,
comment: 'list of new newsgroups follows',
description: 'List of new newsgroups follows (multi-line)',
newsgroups: []
})
})
})
describe('7.4. NEWNEWS', () => {
const command = Command.NEWNEWS
test('230 - Example where there are new articles:', () => {
const reply =
'230 list of new articles by message-id follows\r\n' +
'<i.am.a.new.article@example.com>\r\n' +
'<i.am.another.new.article@example.com>\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 230,
comment: 'list of new articles by message-id follows',
description: 'List of new articles follows (multi-line)',
messageIds: ['<i.am.a.new.article@example.com>', '<i.am.another.new.article@example.com>']
})
})
test('230 - Example where there are no new articles:', () => {
const reply = '230 list of new articles by message-id follows\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 230,
comment: 'list of new articles by message-id follows',
description: 'List of new articles follows (multi-line)',
messageIds: []
})
})
})
describe('7.6.1. LIST', () => {
test('215 - Example of LIST with the ACTIVE keyword:', () => {
const command = Command.LIST_ACTIVE
const reply =
'215 list of newsgroups follows\r\n' +
'misc.test 3002322 3000234 y\r\n' +
'comp.risks 442001 441099 m\r\n' +
'alt.rfc-writers.recovery 4 1 y\r\n' +
'tx.natives.recovery 89 56 y\r\n' +
'tx.natives.recovery.d 11 9 n\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'list of newsgroups follows',
description: 'Information follows (multi-line)',
newsgroups: [
{ name: 'misc.test', high: 3002322, low: 3000234, status: 'y' },
{ name: 'comp.risks', high: 442001, low: 441099, status: 'm' },
{ name: 'alt.rfc-writers.recovery', high: 4, low: 1, status: 'y' },
{ name: 'tx.natives.recovery', high: 89, low: 56, status: 'y' },
{ name: 'tx.natives.recovery.d', high: 11, low: 9, status: 'n' }
]
})
})
test('215 - Example of LIST with no keyword:', () => {
const command = Command.LIST
const reply =
'215 list of newsgroups follows\r\n' +
'misc.test 3002322 3000234 y\r\n' +
'comp.risks 442001 441099 m\r\n' +
'alt.rfc-writers.recovery 4 1 y\r\n' +
'tx.natives.recovery 89 56 y\r\n' +
'tx.natives.recovery.d 11 9 n\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'list of newsgroups follows',
description: 'Information follows (multi-line)',
newsgroups: [
{ name: 'misc.test', high: 3002322, low: 3000234, status: 'y' },
{ name: 'comp.risks', high: 442001, low: 441099, status: 'm' },
{ name: 'alt.rfc-writers.recovery', high: 4, low: 1, status: 'y' },
{ name: 'tx.natives.recovery', high: 89, low: 56, status: 'y' },
{ name: 'tx.natives.recovery.d', high: 11, low: 9, status: 'n' }
]
})
})
test('215 - Example of LIST on a newsgroup-based keyword without wildmat:', () => {
const command = Command.LIST_ACTIVE_TIMES
const reply =
'215 information follows\r\n' +
'misc.test 930445408 <creatme@isc.org>\r\n' +
'alt.rfc-writers.recovery 930562309 <m@example.com>\r\n' +
'tx.natives.recovery 930678923 <sob@academ.com>\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
newsgroups: [
{
name: 'misc.test',
created: '1999-06-27T01:03:28.000Z',
creator: '<creatme@isc.org>'
},
{
name: 'alt.rfc-writers.recovery',
created: '1999-06-28T09:31:49.000Z',
creator: '<m@example.com>'
},
{
name: 'tx.natives.recovery',
created: '1999-06-29T17:55:23.000Z',
creator: '<sob@academ.com>'
}
]
})
})
test('215 - Example of LIST on a newsgroup-based keyword with wildmat:', () => {
const command = Command.LIST_ACTIVE_TIMES
const reply =
'215 information follows\r\n' +
'tx.natives.recovery 930678923 <sob@academ.com>\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
newsgroups: [
{
name: 'tx.natives.recovery',
created: '1999-06-29T17:55:23.000Z',
creator: '<sob@academ.com>'
}
]
})
})
test(
'503 - Example of LIST returning an error where the keyword is ' +
'recognized but the software does not maintain this information:',
() => {
const command = 'LIST XTRA.DATA'
const reply = '503 Data item not stored\r\n'
// @ts-expect-error
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 503,
comment: 'Data item not stored',
description: 'feature not supported'
})
}
)
test('501 - Example of LIST where the keyword is not recognised:', () => {
const command = Command.LIST_DISTRIB_PATS
const reply = '501 Syntax Error\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 501,
comment: 'Syntax Error',
description: 'syntax error in command'
})
})
})
describe('7.6.3. LIST ACTIVE', () => {
const command = Command.LIST_ACTIVE
test('215 - For example:', () => {
const reply =
'215 list of newsgroups follows\r\n' +
'misc.test 3002322 3000234 y\r\n' +
'comp.risks 442001 441099 m\r\n' +
'alt.rfc-writers.recovery 4 1 y\r\n' +
'tx.natives.recovery 89 56 y\r\n' +
'tx.natives.recovery.d 11 9 n\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'list of newsgroups follows',
description: 'Information follows (multi-line)',
newsgroups: [
{ name: 'misc.test', high: 3002322, low: 3000234, status: 'y' },
{ name: 'comp.risks', high: 442001, low: 441099, status: 'm' },
{ name: 'alt.rfc-writers.recovery', high: 4, low: 1, status: 'y' },
{ name: 'tx.natives.recovery', high: 89, low: 56, status: 'y' },
{ name: 'tx.natives.recovery.d', high: 11, low: 9, status: 'n' }
]
})
})
test('215 - or, on an implementation that includes leading zeroes:', () => {
const reply =
'215 list of newsgroups follows\r\n' +
'misc.test 0003002322 0003000234 y\r\n' +
'comp.risks 0000442001 0000441099 m\r\n' +
'alt.rfc-writers.recovery 0000000004 0000000001 y\r\n' +
'tx.natives.recovery 0000000089 0000000056 y\r\n' +
'tx.natives.recovery.d 0000000011 0000000009 n\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'list of newsgroups follows',
description: 'Information follows (multi-line)',
newsgroups: [
{ name: 'misc.test', high: 3002322, low: 3000234, status: 'y' },
{ name: 'comp.risks', high: 442001, low: 441099, status: 'm' },
{ name: 'alt.rfc-writers.recovery', high: 4, low: 1, status: 'y' },
{ name: 'tx.natives.recovery', high: 89, low: 56, status: 'y' },
{ name: 'tx.natives.recovery.d', high: 11, low: 9, status: 'n' }
]
})
})
test(
'215 - The information is newsgroup based, and a wildmat MAY be ' +
'specified, in which case the response is limited to only the groups ' +
'(if any) whose names match the wildmat. For example:',
() => {
const reply =
'215 list of newsgroups follows\r\n' +
'alt.rfc-writers.recovery 4 1 y\r\n' +
'tx.natives.recovery 89 56 y\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'list of newsgroups follows',
description: 'Information follows (multi-line)',
newsgroups: [
{ name: 'alt.rfc-writers.recovery', high: 4, low: 1, status: 'y' },
{ name: 'tx.natives.recovery', high: 89, low: 56, status: 'y' }
]
})
}
)
})
describe('7.6.4. LIST ACTIVE.TIMES', () => {
const command = Command.LIST_ACTIVE_TIMES
test('215 - For example:', () => {
const reply =
'215 information follows\r\n' +
'misc.test 930445408 <creatme@isc.org>\r\n' +
'alt.rfc-writers.recovery 930562309 <m@example.com>\r\n' +
'tx.natives.recovery 930678923 <sob@academ.com>\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
newsgroups: [
{
name: 'misc.test',
created: '1999-06-27T01:03:28.000Z',
creator: '<creatme@isc.org>'
},
{
name: 'alt.rfc-writers.recovery',
created: '1999-06-28T09:31:49.000Z',
creator: '<m@example.com>'
},
{
name: 'tx.natives.recovery',
created: '1999-06-29T17:55:23.000Z',
creator: '<sob@academ.com>'
}
]
})
})
})
describe('7.6.5. LIST DISTRIB.PATS', () => {
const command = Command.LIST_DISTRIB_PATS
test('215 - For example:', () => {
const reply =
'215 information follows\r\n' +
'10:local.*:local\r\n' +
'5:*:world\r\n' +
'20:local.here.*:thissite\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
distributionPatterns: [
{
weight: 10,
wildmat: 'local.*',
distributionHeader: 'local'
},
{
weight: 5,
wildmat: '*',
distributionHeader: 'world'
},
{
weight: 20,
wildmat: 'local.here.*',
distributionHeader: 'thissite'
}
]
})
})
})
describe('7.6.6. LIST NEWSGROUPS', () => {
const command = Command.LIST_NEWSGROUPS
test('215 - For example:', () => {
const reply =
'215 information follows\r\n' +
'misc.test General Usenet testing\r\n' +
'alt.rfc-writers.recovery RFC Writers Recovery\r\n' +
'tx.natives.recovery Texas Natives Recovery\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
newsgroups: [
{
name: 'misc.test',
description: 'General Usenet testing'
},
{
name: 'alt.rfc-writers.recovery',
description: 'RFC Writers Recovery'
},
{
name: 'tx.natives.recovery',
description: 'Texas Natives Recovery'
}
]
})
})
test(
'215 - two fields separated from each other by one or more space or ' + 'TAB characters',
() => {
const reply =
'215 information follows\r\n' +
'misc.test \t General Usenet testing\r\n' +
'alt.rfc-writers.recovery \t \t \tRFC Writers Recovery\r\n' +
'tx.natives.recovery\tTexas Natives Recovery\r\n' +
'.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 215,
comment: 'information follows',
description: 'Information follows (multi-line)',
newsgroups: [
{
name: 'misc.test',
description: 'General Usenet testing'
},
{
name: 'alt.rfc-writers.recovery',
description: 'RFC Writers Recovery'
},
{
name: 'tx.natives.recovery',
description: 'Texas Natives Recovery'
}
]
})
}
)
test.skip('215 - clients MUST be prepared to receive such descriptions.', () => {
expect(false).toBe(true)
})
})
describe('8.3. OVER', () => {
const command = Command.OVER
test.skip('There MAY be more than one header line with the same name.', () => {
// TODO: handle multiple subject headers
})
test(
'224 - Example of a successful retrieval of overview information for ' +
'an article (explicitly not using an article number):',
() => {
const reply =
'224 Overview information follows\r\n' +
'3000234\tI am just a test article\t"Demo User"' +
'<nobody@example.com>\t6 Oct 1998 04:38:40 -0500\t' +
'<45223423@example.com>\t<45454@example.net>\t1234\t' +
'17\tXref: news.example.com misc.test:3000363\r\n.\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 224,
comment: 'Overview information follows',
description: 'Overview information follows (multi-line)',
articles: [
{
articleNumber: 3000234,
headers: {
SUBJECT: 'I am just a test article',
FROM: '"Demo User"<nobody@example.com>',
DATE: '6 Oct 1998 04:38:40 -0500',
'MESSAGE-ID': '<45223423@example.com>',
REFERENCES: '<45454@example.net>',
XREF: 'news.example.com misc.test:3000363'
},
metadata: {
':bytes': 1234,
':lines': 17
}
}
]
})
}
)
test(
'224 - Example of a successful retrieval of overview information for ' +
'an article by message-id:',
() => {
const reply =
'224 Overview information follows\r\n' +
'0|I am just a test article|"Demo User"' +
'<nobody@example.com>|6 Oct 1998 04:38:40 -0500|' +
'<45223423@example.com>|<45454@example.net>|1234|' +
'17|Xref: news.example.com misc.test:3000363\r\n.\r\n'
const response = parse(command, reply.replace(/\|/g, '\t'))
expect(response).toEqual({
_i: reply.length,
code: 224,
comment: 'Overview information follows',
description: 'Overview information follows (multi-line)',
articles: [
{
articleNumber: 0,
headers: {
SUBJECT: 'I am just a test article',
FROM: '"Demo User"<nobody@example.com>',
DATE: '6 Oct 1998 04:38:40 -0500',
'MESSAGE-ID': '<45223423@example.com>',
REFERENCES: '<45454@example.net>',
XREF: 'news.example.com misc.test:3000363'
},
metadata: {
':bytes': 1234,
':lines': 17
}
}
]
})
}
)
test(
'503 - Example of the same commands on a system that does not ' +
'implement retrieval by message-id:',
() => {
const reply = '503 Overview by message-id unsupported\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 503,
comment: 'Overview by message-id unsupported',
description: 'feature not supported'
})
}
)
test(
'224 - Example of a successful retrieval of overview information ' +
'for a range of articles:',
() => {
const reply =
'224 Overview information follows\r\n' +
'3000234|I am just a test article|"Demo User"<nobody@example.com>|6 Oct 1998 04:38:40 -0500|<45223423@example.com>|<45454@example.net>|1234|17|Xref: news.example.com misc.test:3000363\r\n' +
'3000235|Another test article|nobody@nowhere.to(Demo User)|6 Oct 1998 04:38:45 -0500|<45223425@to.to>||4818|37||Distribution: fi\r\n' +
'3000238|Re: I am just a test article|somebody@elsewhere.to|7 Oct 1998 11:38:40 +1200|<kfwer3v@elsewhere.to>|<45223423@to.to>|9234|51\r\n' +
'.\r\n'
const response = parse(command, reply.replace(/\|/g, '\t'))
expect(response).toEqual({
_i: reply.length,
code: 224,
comment: 'Overview information follows',
description: 'Overview information follows (multi-line)',
articles: [
{
articleNumber: 3000234,
headers: {
SUBJECT: 'I am just a test article',
FROM: '"Demo User"<nobody@example.com>',
DATE: '6 Oct 1998 04:38:40 -0500',
'MESSAGE-ID': '<45223423@example.com>',
REFERENCES: '<45454@example.net>',
XREF: 'news.example.com misc.test:3000363'
},
metadata: {
':bytes': 1234,
':lines': 17
}
},
{
articleNumber: 3000235,
headers: {
SUBJECT: 'Another test article',
FROM: 'nobody@nowhere.to(Demo User)',
DATE: '6 Oct 1998 04:38:45 -0500',
'MESSAGE-ID': '<45223425@to.to>',
REFERENCES: '',
DISTRIBUTION: 'fi'
},
metadata: {
':bytes': 4818,
':lines': 37
}
},
{
articleNumber: 3000238,
headers: {
SUBJECT: 'Re: I am just a test article',
FROM: 'somebody@elsewhere.to',
DATE: '7 Oct 1998 11:38:40 +1200',
'MESSAGE-ID': '<kfwer3v@elsewhere.to>',
REFERENCES: '<45223423@to.to>'
},
metadata: {
':bytes': 9234,
':lines': 51
}
}
]
})
}
)
test('430 - not found by message-id', () => {
const reply = '430 not found by message-id\r\n'
const response = parse(command, reply)
expect(response).toEqual({
_i: reply.length,
code: 430,
comment: 'not found by message-id',
description: 'No article with that message-id'
})
})
test(
'412 - Example of an unsuccessful retrieval of overview information ' +
'by number because no newsgroup was selected first',
() => {
const reply = '412 No newsgroup selected\r\n'
const response = parse(command, reply)