UNPKG

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
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)