UNPKG

newsie

Version:

An NNTP Client Library targeting NodeJS. It supports the authentication, TLS encryption, base NNTP commands, and more.

516 lines (440 loc) 15.1 kB
import { Article, ArticleResponse, GroupResponse, GroupsResponse, NewnewsResponse, NntpErrorResponse, NntpResponse, PostResponse } from '../src' import { c, client, integrationSetup, s } from './IntegrationCommon' integrationSetup() // Note: RFC977 is deprecated in favor of RFC3977, test cases note // where the two RFCs conflict. describe(`4. Sample Conversations These are samples of the conversations that might be expected with the news server in hypothetical sessions. The notation C: indicates commands sent to the news server from the client program; S: indicate responses received from the server by the client.`, () => { test('4.1. Example 1 - relative access with NEXT', async () => { // s('(listens at TCP port 119)') // c('(requests connection on TCP port 119)') // s('200 wombatvax news server ready - posting ok') // (client asks for a current newsgroup list) c('LIST') s('215 list of newsgroups follows') s('net.wombats 00543 00501 y') s('net.unix-wizards 10125 10011 y') // (more information here) s('net.idiots 00100 00001 n') s('.') let response = await client.list() expect(response).toEqual({ code: 215, comment: 'list of newsgroups follows', description: 'Information follows (multi-line)', newsgroups: [ { name: 'net.wombats', high: 543, low: 501, status: 'y' }, { name: 'net.unix-wizards', high: 10125, low: 10011, status: 'y' }, { name: 'net.idiots', high: 100, low: 1, status: 'n' } ] } as GroupsResponse) // (client selects a newsgroup) c('GROUP net.unix-wizards') s('211 104 10011 10125 net.unix-wizards group selected') // (there are 104 articles on file, from 10011 to 10125) response = await client.group('net.unix-wizards') expect(response).toEqual({ code: 211, comment: 'group selected', description: 'Group successfully selected', group: { number: 104, low: 10011, high: 10125, name: 'net.unix-wizards' } } as GroupResponse) // (client selects an article to read) c('STAT 10110') s('223 10110 <23445@sdcsvax.ARPA> article retrieved - statistics') // only (article 10110 selected, its message-id is <23445@sdcsvax.ARPA>) response = await client.stat(10110) expect(response).toEqual({ code: 223, comment: 'article retrieved - statistics', description: 'Article exists', article: { articleNumber: 10110, messageId: '<23445@sdcsvax.ARPA>' } } as ArticleResponse) // (client examines the header) c('HEAD') s('221 10110 <23445@sdcsvax.ARPA> article retrieved - head') // follows (text of the header appears here) s('.') response = await client.head() expect(response).toEqual({ code: 221, comment: 'article retrieved - head', description: 'Headers follow (multi-line)', article: { articleNumber: 10110, messageId: '<23445@sdcsvax.ARPA>', headers: {} } } as ArticleResponse) // (client wants to see the text body of the article) c('BODY') s('222 10110 <23445@sdcsvax.ARPA> article retrieved - body') // follows (body text here) s('.') response = await client.body() expect(response).toEqual({ code: 222, comment: 'article retrieved - body', description: 'Body follows (multi-line)', article: { articleNumber: 10110, messageId: '<23445@sdcsvax.ARPA>', body: [] } } as ArticleResponse) // (client selects next article in group) c('NEXT') s('223 10113 <21495@nudebch.uucp> article retrieved - statistics') // only (article 10113 was next in group) response = await client.next() expect(response).toEqual({ code: 223, comment: 'article retrieved - statistics', description: 'Article found', article: { articleNumber: 10113, messageId: '<21495@nudebch.uucp>' } } as ArticleResponse) // (client finishes session) c('QUIT') s('205 goodbye.') response = await client.quit() expect(response).toEqual({ code: 205, comment: 'NNTP Service exits normally', description: 'Connection closing' } as NntpResponse) }) test('4.2. Example 2 - absolute article access with ARTICLE', async () => { // s('(listens at TCP port 119)') // c('(requests connection on TCP port 119)') // s('201 UCB-VAX netnews server ready -- no posting allowed') c('GROUP msgs') s('211 103 402 504 msgs Your new group is msgs') // (there are 103 articles, from 402 to 504) let response = await client.group('msgs') expect(response).toEqual({ code: 211, comment: 'Your new group is msgs', description: 'Group successfully selected', group: { number: 103, low: 402, high: 504, name: 'msgs' } } as GroupResponse) c('ARTICLE 401') s('423 No such article in this newsgroup') let caught = false try { await client.article(401) } catch (response) { expect(response).toEqual({ code: 423, comment: 'No such article in this newsgroup', description: 'No article with that number' } as NntpErrorResponse) caught = true } expect(caught).toBe(true) c('ARTICLE 402') s('220 402 <4105@ucbvax.ARPA> Article retrieved, text follows') // article header and body follow s('') s('.') response = await client.article(402) expect(response).toEqual({ code: 220, comment: 'Article retrieved, text follows', description: 'Article follows (multi-line)', article: { articleNumber: 402, messageId: '<4105@ucbvax.ARPA>', headers: {}, body: [] } } as ArticleResponse) c('HEAD 403') s('221 403 <3108@mcvax.UUCP> Article retrieved, header follows') // article header follows s('.') response = await client.head(403) expect(response).toEqual({ code: 221, comment: 'Article retrieved, header follows', description: 'Headers follow (multi-line)', article: { articleNumber: 403, messageId: '<3108@mcvax.UUCP>', headers: {} } } as ArticleResponse) c('QUIT') s('205 UCB-VAX news server closing connection. Goodbye.') response = await client.quit() expect(response).toEqual({ code: 205, comment: 'NNTP Service exits normally', description: 'Connection closing' } as NntpErrorResponse) }) test('4.3. Example 3 - NEWGROUPS command', async () => { // s('(listens at TCP port 119)') // c('(requests connection on TCP port 119)') // s('200 Imaginary Institute News Server ready (posting ok)') // (client asks for new newsgroups since April 3, 1985) c('NEWGROUPS 850403 020000') s('231 New newsgroups since 03/04/85 02:00:00 follow') s('net.music.gdead') s('net.games.sources') s('.') // Note: RFC977 NEWGROUPS command conflicts with RFC3977 NEWGROUPS command: // In RFC977, the command returns 1 groups per line // In RFC3977 the command returns 4 fields per line // let response = await client.newgroups('1985-04-03T02:00:00Z') // let response = await client.command('NEWGROUPS', '850403', '020000') // expect(response).toEqual({ // code: 231, // comment: 'New newsgroups since 03/04/85 02:00:00 follow', // description: 'List of new newsgroups follows (multi-line)', // newsgroups: [ // { // name: 'net.music.gdead', // }, // { // name: 'net.games.sources', // } // ] // }) c('GROUP net.music.gdead') s('211 0 1 1 net.music.gdead Newsgroup selected') // (there are no articles in that newsgroup, and // the first and last article numbers should be ignored) let response = await client.group('net.music.gdead') expect(response).toEqual({ code: 211, comment: 'Newsgroup selected', description: 'Group successfully selected', group: { number: 0, low: 1, high: 1, name: 'net.music.gdead' } } as GroupResponse) c('QUIT') s('205 Imaginary Institute news server ceasing service. Bye!') response = await client.quit() expect(response).toEqual({ code: 205, comment: 'NNTP Service exits normally', description: 'Connection closing' } as NntpResponse) }) test('4.4. Example 4 - posting a news article', async () => { // s('(listens at TCP port 119)') // c('(requests connection on TCP port 119)') // s('200 BANZAIVAX news server ready, posting allowed.') c('POST') s('340 Continue posting; Period on a line by itself to end') let response = await client.post() expect(response).toEqual({ code: 340, comment: 'Continue posting; Period on a line by itself to end', description: 'Send article to be posted', send: expect.any(Function) } as PostResponse) // Note: RFC977's above comment invalidated by RFC3977's ABNF which shows // an empty article still has a CR-LF for headers // (transmits news article in RFC850 format) c('', '.') s('240 Article posted successfully.') response = await response.send({ headers: {}, body: [] }) expect(response).toEqual({ code: 240, comment: 'Article posted successfully.', description: 'Article received OK' } as NntpResponse) c('QUIT') s('205 BANZAIVAX closing connection. Goodbye.') response = await client.quit() expect(response).toEqual({ code: 205, comment: 'NNTP Service exits normally', description: 'Connection closing' } as NntpResponse) }) test('4.5. Example 5 - interruption due to operator request', async () => { s('(listens at TCP port 119)') c('(requests connection on TCP port 119)') s('201 genericvax news server ready, no posting allowed.') // (assume normal conversation for some time, and // that a newsgroup has been selected) c('NEXT') s('223 1013 <5734@mcvax.UUCP> Article retrieved; text separate.') const response = await client.next() expect(response).toEqual({ code: 223, comment: 'Article retrieved; text separate.', description: 'Article found', article: { articleNumber: 1013, messageId: '<5734@mcvax.UUCP>' } } as ArticleResponse) c('HEAD') c('221 1013 <5734@mcvax.UUCP> Article retrieved; head follows.') // s(`(sends head of article, but halfway through is // interrupted by an operator request. The following // then occurs, without client intervention.)`) // s('(ends current line with a CR-LF pair)') s('.') s('400 Connection closed by operator. Goodbye.') // s('(closes connection)') // Note: in RFC3977 the client does not take random error codes from server, // but expects the 400 as a response to the next command }) test(`4.6. Example 6 - Using the news server to distribute news between systems.`, async () => { // s('(listens at TCP port 119)') // c('(requests connection on TCP port 119)') // s('201 Foobar NNTP server ready (no posting)') // (client asks for new newsgroups since 2 am, May 15, 1985) c('NEWGROUPS 850515 020000') s('235 New newsgroups since 850515 follow') s('net.fluff') s('net.lint') s('.') // let response = await client.newgroups('1985-05-15T02:00:00Z') // let response = await client.command('NEWGROUPS', '850515', '020000') // NOTE: RFC977 and RFC3977 have different responses to NEWGROUPS command // (client asks for new news articles since 2 am, May 15, 1985) c('NEWNEWS * 850515 020000') s('230 New news since 850515 020000 follows') s('<1772@foo.UUCP>') s('<87623@baz.UUCP>') s('<17872@GOLD.CSNET>') s('.') // let response = await client.newnews('*', '1985-05-15T02:00:00') let response = await client.command('NEWNEWS', '*', '850515', '020000') expect(response).toEqual({ code: 230, comment: 'New news since 850515 020000 follows', description: 'List of new articles follows (multi-line)', messageIds: ['<1772@foo.UUCP>', '<87623@baz.UUCP>', '<17872@GOLD.CSNET>'] } as NewnewsResponse) // (client asks for article <1772@foo.UUCP>) c('ARTICLE <1772@foo.UUCP>') s('220 <1772@foo.UUCP> All of article follows') // s('(sends entire message)') s('') s('.') // response = await client.article('<1772@foo.UUCP>') // expect(response).toEqual({ // code: 220, // comment: '', // description: 'Article follows (multi-line)', // article: { // articleNumber: /* ??? */0, // messageId: '<1772@foo.UUCP>', // headers: {}, // body: [] // } // }) // Note: RFC977 is missing articleNumber from RFC3977 in response // (client asks for article <87623@baz.UUCP> c('ARTICLE <87623@baz.UUCP>') s('220 <87623@baz.UUCP> All of article follows') s('(sends entire message)') s('.') // Note: RFCs conflict, see above // (client asks for article <17872@GOLD.CSNET> c('ARTICLE <17872@GOLD.CSNET>') s('220 <17872@GOLD.CSNET> All of article follows') s('(sends entire message)') s('.') // Note: RFCs conflict, see above // (client offers an article it has received recently) c('IHAVE <4105@ucbvax.ARPA>') s('435 Already seen that one, where you been?') let caught = false try { await client.ihave('<4105@ucbvax.ARPA>') } catch (response) { caught = true expect(response).toEqual({ code: 435, comment: 'Already seen that one, where you been?', description: 'Article not wanted' } as NntpErrorResponse) } expect(caught).toBe(true) // (client offers another article) c('IHAVE <4106@ucbvax.ARPA>') s('335 News to me! <CRLF.CRLF> to end.') response = await client.ihave('<4106@ucbvax.ARPA>') expect(response).toEqual({ code: 335, comment: 'News to me! <CRLF.CRLF> to end.', description: 'Send article to be transferred', send: expect.any(Function) } as PostResponse) // c('(sends article)') c('.') // Note: In RFC3977, a single dot is a syntax error // s('235 Article transferred successfully. Thanks.') // // (or) // // s('436 Transfer failed.') // (client is all through with the session) c('QUIT') s('205 Foobar NNTP server bids you farewell.') response = await client.quit() expect(response).toEqual({ code: 205, comment: 'NNTP Service exits normally', description: 'Connection closing' } as NntpResponse) }) })