Commit 8abae596 authored by Conrad Meyer's avatar Conrad Meyer Committed by Russ Cox

nntp: new package, NNTP client

R=rsc, rsc1
CC=golang-dev
https://golang.org/cl/808041
parent 8b20200f
......@@ -94,6 +94,7 @@ DIRS=\
math\
mime\
net\
nntp\
once\
os\
os/signal\
......
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../Make.$(GOARCH)
TARG=nntp
GOFILES=\
nntp.go
include ../../Make.pkg
This diff is collapsed.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nntp
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
func TestSanityChecks(t *testing.T) {
if _, err := Dial("", ""); err == nil {
t.Fatal("Dial should require at least a destination address.")
}
}
type faker struct {
io.Writer
}
func (f faker) Close() os.Error {
return nil
}
func TestBasic(t *testing.T) {
basicServer = strings.Join(strings.Split(basicServer, "\n", 0), "\r\n")
basicClient = strings.Join(strings.Split(basicClient, "\n", 0), "\r\n")
var cmdbuf bytes.Buffer
var fake faker
fake.Writer = &cmdbuf
conn := &Conn{conn: fake, r: bufio.NewReader(strings.NewReader(basicServer))}
// Test some global commands that don't take arguments
if _, err := conn.Capabilities(); err != nil {
t.Fatal("should be able to request CAPABILITIES after connecting: " + err.String())
}
_, err := conn.Date()
if err != nil {
t.Fatal("should be able to send DATE: " + err.String())
}
/*
Test broken until time.Parse adds this format.
cdate := time.UTC()
if sdate.Year != cdate.Year || sdate.Month != cdate.Month || sdate.Day != cdate.Day {
t.Fatal("DATE seems off, probably erroneous: " + sdate.String())
}
*/
// Test LIST (implicit ACTIVE)
if _, err = conn.List(); err != nil {
t.Fatal("LIST should work: " + err.String())
}
tt := new(time.Time)
tt.Year = 2010
tt.Month = 3
tt.Day = 1
const grp = "gmane.comp.lang.go.general"
_, l, h, err := conn.Group(grp)
if err != nil {
t.Fatal("Group shouldn't error: " + err.String())
}
// test STAT, NEXT, and LAST
if _, _, err = conn.Stat(""); err != nil {
t.Fatal("should be able to STAT after selecting a group: " + err.String())
}
if _, _, err = conn.Next(); err != nil {
t.Fatal("should be able to NEXT after selecting a group: " + err.String())
}
if _, _, err = conn.Last(); err != nil {
t.Fatal("should be able to LAST after a NEXT selecting a group: " + err.String())
}
// Can we grab articles?
a, err := conn.Article(fmt.Sprintf("%d", l))
if err != nil {
t.Fatal("should be able to fetch the low article: " + err.String())
}
body, err := ioutil.ReadAll(a.Body)
if err != nil {
t.Fatal("error reading reader: " + err.String())
}
// Test that the article body doesn't get mangled.
expectedbody := `Blah, blah.
.A single leading .
Fin.
`
if !bytes.Equal([]byte(expectedbody), body) {
t.Fatalf("article body read incorrectly; got:\n%s\nExpected:\n%s", body, expectedbody)
}
// Test articleReader
expectedart := `Message-Id: <b@c.d>
Body.
`
a, err = conn.Article(fmt.Sprintf("%d", l+1))
if err != nil {
t.Fatal("shouldn't error reading article low+1: " + err.String())
}
var abuf bytes.Buffer
_, err = a.WriteTo(&abuf)
if err != nil {
t.Fatal("shouldn't error writing out article: " + err.String())
}
actualart := abuf.String()
if actualart != expectedart {
t.Fatalf("articleReader broke; got:\n%s\nExpected\n%s", actualart, expectedart)
}
// Just headers?
if _, err = conn.Head(fmt.Sprintf("%d", h)); err != nil {
t.Fatal("should be able to fetch the high article: " + err.String())
}
// Without an id?
if _, err = conn.Head(""); err != nil {
t.Fatal("should be able to fetch the selected article without specifying an id: " + err.String())
}
// How about bad articles? Do they error?
if _, err = conn.Head(fmt.Sprintf("%d", l-1)); err == nil {
t.Fatal("shouldn't be able to fetch articles lower than low")
}
if _, err = conn.Head(fmt.Sprintf("%d", h+1)); err == nil {
t.Fatal("shouldn't be able to fetch articles higher than high")
}
// Just the body?
r, err := conn.Body(fmt.Sprintf("%d", l))
if err != nil {
t.Fatal("should be able to fetch the low article body\n" + err.String())
}
if _, err = ioutil.ReadAll(r); err != nil {
t.Fatal("error reading reader: " + err.String())
}
if _, err = conn.NewNews(grp, tt); err != nil {
t.Fatal("newnews should work: " + err.String())
}
// NewGroups
if _, err = conn.NewGroups(tt); err != nil {
t.Fatal("newgroups shouldn't error " + err.String())
}
if err = conn.Quit(); err != nil {
t.Fatal("Quit shouldn't error: " + err.String())
}
actualcmds := cmdbuf.String()
if basicClient != actualcmds {
t.Fatalf("Got:\n%s\nExpected\n%s", actualcmds, basicClient)
}
}
var basicServer = `101 Capability list:
VERSION 2
.
111 20100329034158
215 Blah blah
foo 7 3 y
bar 000008 02 m
.
211 100 1 100 gmane.comp.lang.go.general
223 1 <a@b.c> status
223 2 <b@c.d> Article retrieved
223 1 <a@b.c> Article retrieved
220 1 <a@b.c> article
Path: fake!not-for-mail
From: Someone
Newsgroups: gmane.comp.lang.go.general
Subject: [go-nuts] What about base members?
Message-ID: <a@b.c>
Blah, blah.
..A single leading .
Fin.
.
220 2 <b@c.d> article
Message-ID: <b@c.d>
Body.
.
221 100 <c@d.e> head
Path: fake!not-for-mail
Message-ID: <c@d.e>
.
221 100 <c@d.e> head
Path: fake!not-for-mail
Message-ID: <c@d.e>
.
423 Bad article number
423 Bad article number
222 1 <a@b.c> body
Blah, blah.
..A single leading .
Fin.
.
230 list of new articles by message-id follows
<d@e.c>
.
231 New newsgroups follow
.
205 Bye!
`
var basicClient = `CAPABILITIES
DATE
LIST
GROUP gmane.comp.lang.go.general
STAT
NEXT
LAST
ARTICLE 1
ARTICLE 2
HEAD 100
HEAD
HEAD 0
HEAD 101
BODY 1
NEWNEWS gmane.comp.lang.go.general 20100301 000000 GMT
NEWGROUPS 20100301 000000 GMT
QUIT
`
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment